From cb2e91c7eba7c752db28972bfad853de4c202adc Mon Sep 17 00:00:00 2001 From: Nabarun Date: Mon, 25 Aug 2025 16:34:17 +0530 Subject: [PATCH 1/6] Add scripts for deploying to Laconic SP --- deploy/.gitignore | 3 + deploy/.registry.env.example | 10 +++ deploy/Dockerfile | 40 +++++++++++ deploy/README.md | 94 +++++++++++++++++++++++++ deploy/config.yml | 9 +++ deploy/deploy.sh | 132 +++++++++++++++++++++++++++++++++++ deploy/laconic-cli.sh | 50 +++++++++++++ deploy/records/.gitkeep | 0 deploy/remove-deployment.sh | 63 +++++++++++++++++ package-lock.json | 5 +- 10 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 deploy/.gitignore create mode 100644 deploy/.registry.env.example create mode 100644 deploy/Dockerfile create mode 100644 deploy/README.md create mode 100644 deploy/config.yml create mode 100755 deploy/deploy.sh create mode 100755 deploy/laconic-cli.sh create mode 100644 deploy/records/.gitkeep create mode 100755 deploy/remove-deployment.sh diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 0000000..97199cc --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1,3 @@ +.registry.env + +.app.env diff --git a/deploy/.registry.env.example b/deploy/.registry.env.example new file mode 100644 index 0000000..6ac4ee0 --- /dev/null +++ b/deploy/.registry.env.example @@ -0,0 +1,10 @@ +# ENV for registry operations + +# Bond to use +REGISTRY_BOND_ID=230cfedda15e78edc8986dfcb870e1b618f65c56e38d2735476d2a8cb3f25e38 + +# Target deployer LRN +DEPLOYER_LRN=lrn://vaasl-provider/deployers/webapp-deployer-api.apps.vaasl.io + +# Authority to deploy the app under +AUTHORITY=laconic diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..a120b22 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,40 @@ +ARG VARIANT=20-bullseye +FROM node:${VARIANT} + +ARG USERNAME=node +ARG NPM_GLOBAL=/usr/local/share/npm-global + +# Add NPM global to PATH. +ENV PATH=${NPM_GLOBAL}/bin:${PATH} + +RUN \ + # Configure global npm install location, use group to adapt to UID/GID changes + if ! cat /etc/group | grep -e "^npm:" > /dev/null 2>&1; then groupadd -r npm; fi \ + && usermod -a -G npm ${USERNAME} \ + && umask 0002 \ + && mkdir -p ${NPM_GLOBAL} \ + && touch /usr/local/etc/npmrc \ + && chown ${USERNAME}:npm ${NPM_GLOBAL} /usr/local/etc/npmrc \ + && chmod g+s ${NPM_GLOBAL} \ + && npm config -g set prefix ${NPM_GLOBAL} \ + && su ${USERNAME} -c "npm config -g set prefix ${NPM_GLOBAL}" \ + # Install eslint + && su ${USERNAME} -c "umask 0002 && npm install -g eslint" \ + && npm cache clean --force > /dev/null 2>&1 + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends jq bash + +# laconic-so +RUN curl -LO https://git.vdb.to/cerc-io/stack-orchestrator/releases/download/latest/laconic-so && \ +chmod +x ./laconic-so && \ +mv ./laconic-so /usr/bin/laconic-so + +# Configure the npm registry +RUN npm config set @cerc-io:registry https://git.vdb.to/api/packages/cerc-io/npm/ + +# DEBUG, remove +RUN yarn info @cerc-io/laconic-registry-cli + +# Globally install the cli package +RUN yarn global add @cerc-io/laconic-registry-cli diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..2194333 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,94 @@ +# Deploy + +## Setup + +- Clone the repo and run the next set of steps inside cloned repo directory + + +- Build registry CLI image: + + ```bash + docker build -t cerc/laconic-registry-cli . + + # Builds image cerc/laconic-registry-cli:latest + ``` + +- Configure `userKey` in the [registry CLI config](./config.yml): + + - User key should be of the account that owns the `laconic-deploy` authority (owner account address: `laconic1kwx2jm6vscz38qlyujvq6msujmk8l3zangqahs`) + + - Account should also own the bond `5d82586d156fb6671a9170d92f930a72a49a29afb45e30e16fff2100e30776e2` + + ```bash + nano config.yml + ``` + +- Add configuration for registry operations: + + ```bash + cp .registry.env.example .registry.env + + # Update values if required + nano .registry.env + ``` + +- Add configuration for the app: + + ```bash + # Create env for deployment from example env + cp ../env.example .app.env + + # Fill in the required values + nano .app.env + ``` + +## Run + +- Deploy `mtm-vpn-dashboard` App: + + ```bash + # In mtm-vpn-dashboard/deploy dir + docker run -it \ + -v ./:/app/deploy -w /app/deploy \ + -e DEPLOYMENT_DNS=mtm-vpn-dashboard \ + cerc/laconic-registry-cli:latest \ + ./deploy.sh + ``` + +- Check deployment logs on deployer UI: + +- Visit deployed app: + +### Remove deployment + +- Remove deployment: + + ```bash + # In gor-deploy/deploy dir + docker run -it \ + -v ./:/app/deploy -w /app/deploy \ + -e DEPLOYMENT_RECORD_ID= \ + cerc/laconic-registry-cli:latest \ + ./remove-deployment.sh + ``` + +## Troubleshoot + +- Check records in [registry console app](https://console.laconic.com/#/registry). + +- If deployment fails due to low bond balance + - Check balances + + ```bash + # Account balance + ./laconic-cli.sh account get + + # Bond balance + ./laconic-cli.sh bond get --id 5d82586d156fb6671a9170d92f930a72a49a29afb45e30e16fff2100e30776e2 + ``` + + - Command to refill bond + + ```bash + ./laconic-cli.sh bond refill --id 5d82586d156fb6671a9170d92f930a72a49a29afb45e30e16fff2100e30776e2 --type alnt --quantity 10000000 + ``` diff --git a/deploy/config.yml b/deploy/config.yml new file mode 100644 index 0000000..9866814 --- /dev/null +++ b/deploy/config.yml @@ -0,0 +1,9 @@ +# Registry CLI config +services: + registry: + rpcEndpoint: 'https://laconicd-mainnet-1.laconic.com' + gqlEndpoint: 'https://laconicd-mainnet-1.laconic.com/api' + userKey: + bondId: 230cfedda15e78edc8986dfcb870e1b618f65c56e38d2735476d2a8cb3f25e38 + chainId: laconic-mainnet + gasPrice: 0.001alnt diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100755 index 0000000..74dafa2 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +# Fail on error +set -e + +source .registry.env +echo "Using REGISTRY_BOND_ID: $REGISTRY_BOND_ID" +echo "Using DEPLOYER_LRN: $DEPLOYER_LRN" +echo "Using AUTHORITY: $AUTHORITY" + +# Repository URL +REPO_URL="https://github.com/deep-stack/mtm-vpn-dashboard" + +# Get the latest commit hash for a branch +BRANCH_NAME="ng-deploy-laconic" +LATEST_HASH=$(git ls-remote $REPO_URL refs/heads/$BRANCH_NAME | awk '{print $1}') + +# Gitea +# PACKAGE_VERSION=$(curl -s $REPO_URL/raw/branch/$BRANCH_NAME/package.json | jq -r .version) + +# GitHub +PACKAGE_VERSION=$(curl -s $REPO_URL/raw/refs/heads/$BRANCH_NAME/package.json | jq -r .version) + +APP_NAME=mtm-vpn-dashboard + +echo "Repo: ${REPO_URL}" +echo "Latest hash: ${LATEST_HASH}" +echo "App version: ${PACKAGE_VERSION}" +echo "Deployment DNS: ${DEPLOYMENT_DNS}" + +# Current date and time for note +CURRENT_DATE_TIME=$(date -u) + +CONFIG_FILE=config.yml + +# Reference: https://git.vdb.to/cerc-io/test-progressive-web-app/src/branch/main/scripts + +# Get latest version from registry and increment application-record version +NEW_APPLICATION_VERSION=$(laconic -c $CONFIG_FILE registry record list --type ApplicationRecord --all --name "$APP_NAME" 2>/dev/null | jq -r -s ".[] | sort_by(.createTime) | reverse | [ .[] | select(.bondId == \"$REGISTRY_BOND_ID\") ] | .[0].attributes.version" | awk -F. -v OFS=. '{$NF += 1 ; print}') + +if [ -z "$NEW_APPLICATION_VERSION" ] || [ "1" == "$NEW_APPLICATION_VERSION" ]; then + # Set application-record version if no previous records were found + NEW_APPLICATION_VERSION=0.0.1 +fi + +# Generate application-record.yml with incremented version +mkdir -p records +RECORD_FILE=./records/application-record.yml + +cat >$RECORD_FILE < /dev/null; then + echo "Error: Docker is not installed or not in PATH" + exit 1 +fi + +# Check if the cerc/laconic-registry-cli image exists +if ! docker image inspect cerc/laconic-registry-cli &> /dev/null; then + echo "Error: cerc/laconic-registry-cli Docker image not found" + echo "Please build the image first: docker build -t cerc/laconic-registry-cli ." + exit 1 +fi + +# Get current directory (should be deploy directory) +CURRENT_DIR="$(pwd)" +PROJECT_ROOT="$(dirname "$CURRENT_DIR")" + +# Verify we're in the deploy directory +if [ ! -f "config.yml" ] || [ ! -f "laconic-cli.sh" ]; then + echo "Error: This script must be run from the deploy directory" + echo "Current directory: $CURRENT_DIR" + echo "Please cd to the deploy directory and run: ./laconic-cli.sh" + exit 1 +fi + +# Set up volume mounts +DEPLOY_MOUNT="-v $CURRENT_DIR:/app/deploy" +OUT_MOUNT="" + +# Create out directory if it doesn't exist and always mount it +if [ ! -d "out" ]; then + mkdir -p "out" +fi +OUT_MOUNT="-v $CURRENT_DIR/out:/app/out" + +# Run the Docker command with processed arguments +docker run --rm \ + --add-host=host.docker.internal:host-gateway \ + $DEPLOY_MOUNT \ + $OUT_MOUNT \ + -w /app/deploy \ + cerc/laconic-registry-cli \ + laconic registry -c config.yml \ + "$@" diff --git a/deploy/records/.gitkeep b/deploy/records/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/deploy/remove-deployment.sh b/deploy/remove-deployment.sh new file mode 100755 index 0000000..5c9d000 --- /dev/null +++ b/deploy/remove-deployment.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -e + +if [[ -z $DEPLOYMENT_RECORD_ID ]]; then + echo "Error: please pass the deployment record ID" >&2 + exit 1 +fi + +source .registry.env +echo "Using DEPLOYER_LRN: $DEPLOYER_LRN" + +echo "Deployment record ID: $DEPLOYMENT_RECORD_ID" + +# Generate application-deployment-removal-request.yml +REMOVAL_REQUEST_RECORD_FILE=./records/application-deployment-removal-request.yml + +cat > $REMOVAL_REQUEST_RECORD_FILE < Date: Mon, 25 Aug 2025 18:42:17 +0530 Subject: [PATCH 2/6] Create UI mockup with claude --- CLAUDE.md | 162 ++++ components/Layout.tsx | 187 ++++ components/ProtectedRoute.tsx | 45 + data/mockData.ts | 179 ++++ deploy/README.md | 2 +- package-lock.json | 1493 ++++++++++++++++++++++++++++-- package.json | 12 +- pages/_app.tsx | 8 +- pages/dashboard/balances.tsx | 347 +++++++ pages/dashboard/downloads.tsx | 393 ++++++++ pages/dashboard/failed.tsx | 286 ++++++ pages/dashboard/index.tsx | 217 +++++ pages/dashboard/support.tsx | 368 ++++++++ pages/dashboard/transactions.tsx | 321 +++++++ pages/index.tsx | 57 +- pages/login.tsx | 181 ++++ postcss.config.js | 6 + public/manifest.json | 12 +- styles/globals.css | 19 +- tailwind.config.js | 11 + tsconfig.json | 4 +- types/index.ts | 69 ++ 22 files changed, 4233 insertions(+), 146 deletions(-) create mode 100644 CLAUDE.md create mode 100644 components/Layout.tsx create mode 100644 components/ProtectedRoute.tsx create mode 100644 data/mockData.ts create mode 100644 pages/dashboard/balances.tsx create mode 100644 pages/dashboard/downloads.tsx create mode 100644 pages/dashboard/failed.tsx create mode 100644 pages/dashboard/index.tsx create mode 100644 pages/dashboard/support.tsx create mode 100644 pages/dashboard/transactions.tsx create mode 100644 pages/login.tsx create mode 100644 postcss.config.js create mode 100644 tailwind.config.js create mode 100644 types/index.ts diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..1a4a41e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,162 @@ +# MTM VPN Dashboard - Codebase Reference + +## Project Overview +- **Name**: @cerc-io/test-progressive-web-app +- **Version**: 0.1.24 +- **Framework**: Next.js with React 18 +- **Language**: TypeScript +- **PWA**: Enabled with next-pwa plugin + +## Directory Structure +``` +/ +├── deploy/ # Deployment configurations and scripts +├── pages/ # Next.js pages (routing) +│ ├── _app.tsx # App wrapper component +│ ├── index.tsx # Home page +│ └── api/ # API routes +│ └── hello.ts # Sample API endpoint +├── public/ # Static assets +│ ├── icons/ # PWA icons (various sizes) +│ ├── manifest.json # PWA manifest +│ ├── sw.js # Service worker +│ └── workbox-*.js # Workbox files +├── styles/ # CSS files +│ ├── globals.css # Global styles +│ └── Home.module.css # Home page styles +└── scripts/ # Build/deployment scripts +``` + +## Dependencies +### Core Dependencies +- **next**: "latest" - Next.js framework +- **next-pwa**: "^5.6.0" - Progressive Web App support +- **react**: "^18.2.0" - React library +- **react-dom**: "^18.2.0" - React DOM + +### Dev Dependencies +- **@types/node**: "17.0.4" +- **@types/react**: "17.0.38" +- **typescript**: "4.5.4" + +## Configuration Files + +### next.config.js +- Uses next-pwa plugin for PWA functionality +- Environment variables exposed: + - `CERC_TEST_WEBAPP_CONFIG1` + - `CERC_TEST_WEBAPP_CONFIG2` + - `CERC_WEBAPP_DEBUG` + +### tsconfig.json +- Target: ES5 +- Strict mode enabled +- JSX preserve mode +- Incremental compilation enabled + +## Current Pages & Components + +### pages/_app.tsx +- Root application wrapper +- Contains PWA meta tags and configuration +- Sets up viewport, theme color, manifest, and icons +- Title: "Laconic Test PWA" + +### pages/index.tsx +- Home page component +- Displays environment variables in cards +- Uses CSS modules for styling +- Contains Laconic branding and logo + +### pages/api/hello.ts +- Sample API route +- Returns JSON: `{ name: 'John Doe' }` + +## Styling System +- **Global styles**: `/styles/globals.css` +- **Module styles**: CSS Modules pattern +- **Current theme**: + - Primary blue: `#0070f3` + - Border color: `#eaeaea` + - Theme color: `#317EFB` + +### Key CSS Classes (Home.module.css) +- `.container` - Main page container (flexbox, centered) +- `.main` - Content area +- `.footer` - Footer with logo +- `.title` - Large heading (4rem) +- `.grid` - Card container (flexbox grid) +- `.card` - Individual cards with hover effects + +## PWA Configuration +- Service worker enabled +- Multiple icon sizes provided (16x16 to 512x512) +- Manifest.json configured +- Workbox integration for caching + +## Environment Variables +The app currently reads and displays three environment variables: +1. `CERC_TEST_WEBAPP_CONFIG1` +2. `CERC_TEST_WEBAPP_CONFIG2` +3. `CERC_WEBAPP_DEBUG` + +## Scripts Available +- `npm run dev` - Development server +- `npm run build` - Production build +- `npm run start` - Production server + +## Deployment +- Contains Laconic-specific deployment scripts in `/deploy` +- Docker support with Dockerfile +- Shell scripts for deployment automation + +## Development Commands +- **Dev server**: `npm run dev` +- **Build**: `npm run build` +- **Lint**: Not configured +- **Typecheck**: Not configured + +## Dashboard Implementation Status +✅ **Completed Features:** +- Admin authentication UI (login/logout) +- Responsive dashboard layout with sidebar navigation +- Overview dashboard with metrics and charts +- Transaction monitoring (all types: MTM-to-NYM, Bridge, Swap) +- Failed transaction analysis and retry functionality +- Account balance monitoring with ETH refill interface +- App download analytics with charts and version tracking +- Customer support ticket management system +- PWA configuration updated for admin dashboard + +## Dashboard Pages +- `/login` - Admin authentication +- `/dashboard` - Overview with stats and charts +- `/dashboard/transactions` - All transaction monitoring +- `/dashboard/failed` - Failed transaction analysis +- `/dashboard/balances` - Account balance management +- `/dashboard/downloads` - App download analytics +- `/dashboard/support` - Customer support system + +## Technical Implementation +- **Framework**: Next.js with TypeScript +- **UI**: Tailwind CSS + Headless UI components +- **Charts**: Custom CSS-based charts (replaced Recharts for compatibility) +- **Authentication**: Local storage based (ready for backend integration) +- **Data**: Mock data based on mtm-to-nym-service entities +- **Responsive**: Mobile-first design with collapsible sidebar +- **Build Status**: ✅ Successfully builds and runs +- **Dev Server**: ✅ Runs at http://localhost:3000 + +## Getting Started +1. **Install dependencies**: `npm install` +2. **Run development server**: `npm run dev` +3. **Build for production**: `npm run build` +4. **Access dashboard**: Navigate to http://localhost:3000 +5. **Login**: Use any valid email + password (6+ characters) + +## Chart Implementation +- Replaced Recharts with custom CSS-based visualizations for better compatibility +- Bar charts using CSS flex and dynamic heights +- Line charts using SVG paths and CSS positioning +- Progress bars for platform distribution +- All charts are animated and responsive \ No newline at end of file diff --git a/components/Layout.tsx b/components/Layout.tsx new file mode 100644 index 0000000..0a29e60 --- /dev/null +++ b/components/Layout.tsx @@ -0,0 +1,187 @@ +import { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import Link from 'next/link'; +import { + HomeIcon, + ArrowsRightLeftIcon, + ExclamationTriangleIcon, + WalletIcon, + CloudArrowDownIcon, + ChatBubbleLeftRightIcon, + ArrowRightOnRectangleIcon, + Bars3Icon, + XMarkIcon, +} from '@heroicons/react/24/outline'; + +const navigation = [ + { name: 'Dashboard', href: '/dashboard', icon: HomeIcon }, + { name: 'Transactions', href: '/dashboard/transactions', icon: ArrowsRightLeftIcon }, + { name: 'Failed Transactions', href: '/dashboard/failed', icon: ExclamationTriangleIcon }, + { name: 'Account Balances', href: '/dashboard/balances', icon: WalletIcon }, + { name: 'App Downloads', href: '/dashboard/downloads', icon: CloudArrowDownIcon }, + { name: 'Support', href: '/dashboard/support', icon: ChatBubbleLeftRightIcon }, +]; + +interface LayoutProps { + children: React.ReactNode; +} + +export default function Layout({ children }: LayoutProps) { + const router = useRouter(); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [adminUser, setAdminUser] = useState<{ name: string; email: string } | null>(null); + + useEffect(() => { + if (typeof window !== 'undefined') { + const userData = localStorage.getItem('adminUser'); + if (userData) { + setAdminUser(JSON.parse(userData)); + } + } + }, []); + + const handleLogout = () => { + localStorage.removeItem('isAdminAuthenticated'); + localStorage.removeItem('adminUser'); + router.push('/login'); + }; + + return ( +
+ {/* Mobile sidebar */} +
+
setSidebarOpen(false)} /> + +
+
+ +
+ +
+
+

MTM VPN Admin

+
+ +
+
+
+ + {/* Desktop sidebar */} +
+
+
+
+

MTM VPN Admin

+
+ +
+
+
+ + {/* Main content */} +
+ {/* Header */} +
+ +
+ + {/* Header bar */} +
+
+
+
+

+ {navigation.find(item => item.href === router.pathname)?.name || 'Dashboard'} +

+
+ +
+ {adminUser && ( +
+
+

{adminUser.name}

+

{adminUser.email}

+
+ +
+ )} +
+
+
+
+ + {/* Page content */} +
+
+
+ {children} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/components/ProtectedRoute.tsx b/components/ProtectedRoute.tsx new file mode 100644 index 0000000..fb6a061 --- /dev/null +++ b/components/ProtectedRoute.tsx @@ -0,0 +1,45 @@ +import { useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +export default function ProtectedRoute({ children }: ProtectedRouteProps) { + const router = useRouter(); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const checkAuth = () => { + const authToken = localStorage.getItem('isAdminAuthenticated'); + + if (authToken === 'true') { + setIsAuthenticated(true); + } else { + router.push('/login'); + } + + setIsLoading(false); + }; + + // Only check auth on client side + if (typeof window !== 'undefined') { + checkAuth(); + } + }, [router]); + + if (isLoading) { + return ( +
+
+
+ ); + } + + if (!isAuthenticated) { + return null; + } + + return <>{children}; +} \ No newline at end of file diff --git a/data/mockData.ts b/data/mockData.ts new file mode 100644 index 0000000..fb7e872 --- /dev/null +++ b/data/mockData.ts @@ -0,0 +1,179 @@ +import { Transaction, BridgeTransaction, SwapTransaction, AccountBalance, AppDownload, SupportTicket, DashboardStats } from '../types'; + +export const mockTransactions: Transaction[] = [ + { + id: '1', + transactionHash: '2AUxZpuQqR7pYyuYcYqwbGHFgJfbvGRJfrGW1D4RSbVeHBprrCoVBb8YEb7uYAiGTL7tGLWYAbaJZjmxetDCpW9o', + fromAddress: 'HmWkGTaLQXzqDUVUThqSksp2gRYupRGKegJ4TZzBgEHi', + nymTransactionHash: '4E169C934D2782EC35DCC1BBB578FF543B081B06E76D8C030B75ACD3EDE590F8', + createdAt: new Date('2025-08-24T10:30:00Z') + }, + { + id: '2', + transactionHash: '3bVxNqrQsR8qZzrYdYrxcGHFgKgcwGTKgrHX2E5STdWfICqssEpVCc9ZFc8vZBjHTM8uHLXYBcbKam4yguEidl9P', + fromAddress: 'JkXlRgaQTbzfMcXvRuTyFqPmLdGhGfKjVnBxEzRgQaFi', + nymTransactionHash: '7F259D847F3892FD46EDD2CCC689GG654C192C17F87E9D141C86BDE4FEF701G9', + createdAt: new Date('2025-08-24T09:15:00Z') + }, + { + id: '3', + transactionHash: '4CWyOrsTt9rAarZeEasydIGGhKheaHTLhsIY3F6TUeXgJDrtfFqWDd0AhGd9wCkIUN9vIMYZCdcLbm5zhvFjelqQ', + fromAddress: 'MnYmShbRUczgOfYwSvUzGrRnHfHmGgLkWoCzF1ShRbGj', + error: 'Insufficient NYM balance in service wallet', + createdAt: new Date('2025-08-24T08:45:00Z') + } +]; + +export const mockBridgeTransactions: BridgeTransaction[] = [ + { + id: 1, + nymAmount: '125.5', + ethTransactionHash: '0x1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890ab', + createdAt: new Date('2025-08-24T11:00:00Z') + }, + { + id: 2, + nymAmount: '67.25', + ethTransactionHash: '0x2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890abcd', + createdAt: new Date('2025-08-24T10:20:00Z') + }, + { + id: 3, + nymAmount: '200.0', + error: 'Bridge transaction failed: Network congestion', + createdAt: new Date('2025-08-24T09:30:00Z') + } +]; + +export const mockSwapTransactions: SwapTransaction[] = [ + { + id: 1, + ethAmount: '0.05', + transactionHash: '0x3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef', + createdAt: new Date('2025-08-24T12:15:00Z') + }, + { + id: 2, + ethAmount: '0.025', + transactionHash: '0x4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12', + createdAt: new Date('2025-08-24T11:40:00Z') + }, + { + id: 3, + ethAmount: '0.1', + error: 'Swap failed: Slippage too high', + createdAt: new Date('2025-08-24T10:55:00Z') + } +]; + +export const mockAccountBalances: AccountBalance[] = [ + { + asset: 'ETH', + balance: '2.456789', + address: '0x742d35Cc643C0532E6A8b32F4F5bbFB6C6e13aE2' + }, + { + asset: 'NYM', + balance: '15234.789123', + address: 'n1sdnrq62m07gcwzpfmdgvqfpqqjvtnnllcypur8' + } +]; + +export const mockAppDownloads: AppDownload[] = [ + { + id: '1', + version: 'v1.8.0-mtm-0.1.2', + platform: 'Android', + downloads: 1247, + releaseDate: new Date('2025-08-19'), + fileSize: '111 MiB' + }, + { + id: '2', + version: 'v1.7.5-mtm-0.1.1', + platform: 'Android', + downloads: 892, + releaseDate: new Date('2025-08-10'), + fileSize: '108 MiB' + }, + { + id: '3', + version: 'v1.6.2-mtm-0.1.0', + platform: 'Android', + downloads: 634, + releaseDate: new Date('2025-07-28'), + fileSize: '105 MiB' + }, + { + id: '4', + version: 'v1.8.0-mtm-0.1.2', + platform: 'iOS', + downloads: 456, + releaseDate: new Date('2025-08-20'), + fileSize: '127 MiB' + } +]; + +export const mockSupportTickets: SupportTicket[] = [ + { + id: '1', + userId: 'user_123', + email: 'john.doe@example.com', + subject: 'Unable to connect to VPN servers', + message: 'I\'ve been trying to connect to VPN servers for the past 2 hours but keep getting connection timeout errors. Please help.', + status: 'open', + priority: 'high', + createdAt: new Date('2025-08-24T14:30:00Z'), + updatedAt: new Date('2025-08-24T14:30:00Z'), + responses: [] + }, + { + id: '2', + userId: 'user_456', + email: 'jane.smith@example.com', + subject: 'Transaction failed but MTM tokens were deducted', + message: 'I sent 50 MTM tokens but the transaction shows as failed, however the tokens are missing from my wallet.', + status: 'in-progress', + priority: 'critical', + createdAt: new Date('2025-08-24T13:15:00Z'), + updatedAt: new Date('2025-08-24T13:45:00Z'), + responses: [ + { + id: 'resp_1', + ticketId: '2', + message: 'We are investigating this issue. Can you please provide the transaction hash?', + isAdminResponse: true, + createdAt: new Date('2025-08-24T13:45:00Z') + } + ] + }, + { + id: '3', + userId: 'user_789', + email: 'mike.wilson@example.com', + subject: 'Feature request: Dark mode support', + message: 'Would love to see dark mode support in the VPN app. The current bright theme is hard on the eyes during night usage.', + status: 'resolved', + priority: 'low', + createdAt: new Date('2025-08-23T16:20:00Z'), + updatedAt: new Date('2025-08-24T10:00:00Z'), + responses: [ + { + id: 'resp_2', + ticketId: '3', + message: 'Thank you for the suggestion! Dark mode is planned for the next major release.', + isAdminResponse: true, + createdAt: new Date('2025-08-24T10:00:00Z') + } + ] + } +]; + +export const mockDashboardStats: DashboardStats = { + totalTransactions: 1247, + successfulTransactions: 1189, + failedTransactions: 58, + totalVolume: '2,456,789.12', + activeUsers: 3420, + totalDownloads: 3229 +}; \ No newline at end of file diff --git a/deploy/README.md b/deploy/README.md index 2194333..7a769b4 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -36,7 +36,7 @@ ```bash # Create env for deployment from example env - cp ../env.example .app.env + cp ../.env.example .app.env # Fill in the required values nano .app.env diff --git a/package-lock.json b/package-lock.json index 459f07d..f168d83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,15 +8,36 @@ "name": "@cerc-io/test-progressive-web-app", "version": "0.1.24", "dependencies": { + "@headlessui/react": "^2.2.7", + "@heroicons/react": "^2.2.0", + "autoprefixer": "^10.4.21", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", "next": "latest", "next-pwa": "^5.6.0", + "postcss": "^8.5.6", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@types/node": "17.0.4", "@types/react": "17.0.38", - "typescript": "4.5.4" + "tailwindcss": "^3.4.0", + "typescript": "^5.0.4" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@ampproject/remapping": { @@ -1563,17 +1584,114 @@ "node": ">=6.9.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@headlessui/react": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.7.tgz", + "integrity": "sha512-WKdTymY8Y49H8/gUc/lIyYK1M+/6dq0Iywh4zTZVAaiTDprRfioxSgD0wnXTQTBpjpGJuTL1NO/mqEvc//5SSg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@heroicons/react": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", + "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", + "license": "MIT", + "peerDependencies": { + "react": ">= 16 || ^19.0.0-rc" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1584,14 +1702,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", @@ -1602,14 +1712,16 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1787,6 +1899,114 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@react-aria/focus": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.0.tgz", + "integrity": "sha512-7NEGtTPsBy52EZ/ToVKCu0HSelE3kq9qeis+2eEq90XSuJOMaDHUQrA7RC2Y89tlEwQB31bud/kKRi9Qme1dkA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.25.4", + "@react-aria/utils": "^3.30.0", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.25.4", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.4.tgz", + "integrity": "sha512-HBQMxgUPHrW8V63u9uGgBymkMfj6vdWbB0GgUJY49K9mBKMsypcHeWkWM6+bF7kxRO728/IK8bWDV6whDbqjHg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.30.0", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.0.tgz", + "integrity": "sha512-ydA6y5G1+gbem3Va2nczj/0G0W7/jUVo/cbN10WA5IizzWIwMP5qhFr7macgbKfHMkZ+YZC3oXnt2NNre5odKw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.10.8", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz", + "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.31.0.tgz", + "integrity": "sha512-ua5U6V66gDcbLZe4P2QeyNgPp4YWD1ymGA6j3n+s8CGExtrCPe64v+g4mvpT8Bnb985R96e4zFT61+m0YCwqMg==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -1880,6 +2100,33 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@types/eslint": { "version": "8.44.6", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", @@ -2167,6 +2414,19 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -2178,6 +2438,34 @@ "node": ">=4" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -2239,6 +2527,43 @@ "node": ">= 4.0.0" } }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -2317,6 +2642,19 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2338,9 +2676,9 @@ } }, "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", "funding": [ { "type": "opencollective", @@ -2355,11 +2693,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -2408,10 +2747,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { - "version": "1.0.30001559", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001559.tgz", - "integrity": "sha512-cPiMKZgqgkg5LY3/ntGeLFUpi6tzddBNS58A4tnTgQw1zON7u2sZMU7SzOeVH4tj20++9ggL+V6FDOFMTaFFYA==", + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "funding": [ { "type": "opencollective", @@ -2425,7 +2774,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", @@ -2440,6 +2790,31 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -2468,6 +2843,15 @@ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2521,6 +2905,21 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -2529,12 +2928,35 @@ "node": ">=8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", "dev": true }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2639,6 +3061,13 @@ "node": ">=0.10.0" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2650,6 +3079,20 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.9", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", @@ -2665,9 +3108,17 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.572", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.572.tgz", - "integrity": "sha512-RlFobl4D3ieetbnR+2EpxdzFl9h0RAJkPK3pfiwMug2nhBin2ZCsGIAJWdpNniLz43sgXam/CgipOmvTA+rUiA==" + "version": "1.5.208", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz", + "integrity": "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -2678,9 +3129,10 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -2778,9 +3230,10 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2965,6 +3418,36 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs-extra": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", @@ -3302,6 +3785,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -3361,6 +3857,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -3551,6 +4057,29 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jake": { "version": "10.8.7", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", @@ -3667,6 +4196,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3737,6 +4276,23 @@ "node": ">=6" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -3883,21 +4439,44 @@ "node": "*" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3972,10 +4551,58 @@ "next": ">=9.0.0" } }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -3985,6 +4612,16 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -4067,6 +4704,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4088,11 +4732,45 @@ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4102,9 +4780,10 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4144,6 +4823,16 @@ "node": ">=0.10.0" } }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -4156,9 +4845,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -4173,15 +4862,100 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -4251,6 +5025,48 @@ "react": "^18.2.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cache/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -4583,6 +5399,29 @@ "node": ">= 0.4" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -4596,6 +5435,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4618,9 +5470,10 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -4648,6 +5501,70 @@ "node": ">=10.0.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -4722,6 +5639,46 @@ "node": ">=4" } }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -4752,6 +5709,86 @@ } } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -4774,6 +5811,112 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", + "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -4875,6 +6018,29 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4902,6 +6068,13 @@ "punycode": "^2.1.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -4980,16 +6153,17 @@ } }, "node_modules/typescript": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.4.tgz", - "integrity": "sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=12.20" } }, "node_modules/unbox-primitive": { @@ -5071,9 +6245,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -5088,9 +6262,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -5107,6 +6282,22 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -5208,6 +6399,22 @@ "webidl-conversions": "^4.0.2" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", @@ -5491,6 +6698,137 @@ "workbox-core": "6.6.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -5500,6 +6838,19 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } } } } diff --git a/package.json b/package.json index 4661af9..ec7c18a 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,22 @@ "start": "next start" }, "dependencies": { + "@headlessui/react": "^2.2.7", + "@heroicons/react": "^2.2.0", + "autoprefixer": "^10.4.21", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", "next": "latest", "next-pwa": "^5.6.0", + "postcss": "^8.5.6", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@types/node": "17.0.4", "@types/react": "17.0.38", - "typescript": "4.5.4" + "tailwindcss": "^3.4.0", + "typescript": "^5.0.4" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index a9ff536..8ac6f7f 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -12,9 +12,9 @@ export default function MyApp({ Component, pageProps }: AppProps) { name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /> - - - Laconic Test PWA + + + MTM VPN Admin Dashboard - + diff --git a/pages/dashboard/balances.tsx b/pages/dashboard/balances.tsx new file mode 100644 index 0000000..d32155d --- /dev/null +++ b/pages/dashboard/balances.tsx @@ -0,0 +1,347 @@ +import { useState } from 'react'; +import { + WalletIcon, + ArrowPathIcon, + PlusIcon, + ExclamationTriangleIcon, + CheckCircleIcon, +} from '@heroicons/react/24/outline'; +import Layout from '../../components/Layout'; +import ProtectedRoute from '../../components/ProtectedRoute'; +import { mockAccountBalances } from '../../data/mockData'; + +export default function AccountBalances() { + const [isRefilling, setIsRefilling] = useState(false); + const [refillAmount, setRefillAmount] = useState(''); + const [refillSuccess, setRefillSuccess] = useState(false); + + const handleRefill = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!refillAmount || parseFloat(refillAmount) <= 0) { + alert('Please enter a valid refill amount'); + return; + } + + setIsRefilling(true); + + // Simulate refill operation + setTimeout(() => { + setIsRefilling(false); + setRefillSuccess(true); + setRefillAmount(''); + + // Hide success message after 3 seconds + setTimeout(() => { + setRefillSuccess(false); + }, 3000); + }, 2000); + }; + + const getBalanceStatus = (asset: string, balance: string) => { + const balanceNum = parseFloat(balance); + + if (asset === 'ETH') { + if (balanceNum < 0.1) return { status: 'critical', message: 'Critical - Refill needed immediately' }; + if (balanceNum < 0.5) return { status: 'warning', message: 'Low - Consider refilling soon' }; + return { status: 'healthy', message: 'Healthy balance' }; + } + + if (asset === 'NYM') { + if (balanceNum < 1000) return { status: 'critical', message: 'Critical - Refill needed immediately' }; + if (balanceNum < 5000) return { status: 'warning', message: 'Low - Consider refilling soon' }; + return { status: 'healthy', message: 'Healthy balance' }; + } + + return { status: 'healthy', message: 'Healthy balance' }; + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'critical': + return 'text-red-600 bg-red-100'; + case 'warning': + return 'text-yellow-600 bg-yellow-100'; + case 'healthy': + return 'text-green-600 bg-green-100'; + default: + return 'text-gray-600 bg-gray-100'; + } + }; + + const getStatusIcon = (status: string) => { + switch (status) { + case 'critical': + return ExclamationTriangleIcon; + case 'warning': + return ExclamationTriangleIcon; + case 'healthy': + return CheckCircleIcon; + default: + return CheckCircleIcon; + } + }; + + return ( + + +
+
+
+

Account Balances

+

+ Monitor service wallet balances and refill accounts as needed. +

+
+
+ +
+
+ + {/* Success Alert */} + {refillSuccess && ( +
+
+
+ +
+
+

+ ETH refill transaction initiated successfully! +

+

+ The transaction has been submitted to the network and should be confirmed shortly. +

+
+
+
+ )} + + {/* Account Balance Cards */} +
+ {mockAccountBalances.map((account) => { + const { status, message } = getBalanceStatus(account.asset, account.balance); + const StatusIcon = getStatusIcon(status); + + return ( +
+
+
+
+ +
+
+
+
+ {account.asset} Balance +
+
+ {parseFloat(account.balance).toLocaleString(undefined, { + minimumFractionDigits: account.asset === 'ETH' ? 4 : 2, + maximumFractionDigits: account.asset === 'ETH' ? 6 : 2, + })} {account.asset} +
+
+ {account.address.slice(0, 12)}...{account.address.slice(-12)} +
+
+
+
+ +
+
+ +
+
+ {message} +
+
+ + {/* Balance threshold indicator */} +
+
+ Balance Status + {status.charAt(0).toUpperCase() + status.slice(1)} +
+
+
+
+
+ + {/* Transaction History Link */} + +
+
+ ); + })} +
+ + {/* ETH Refill Form */} +
+
+

+ Refill ETH Account +

+
+

+ Add ETH to the service wallet to ensure uninterrupted transaction processing. + Minimum recommended balance: 0.5 ETH. +

+
+
+
+ + setRefillAmount(e.target.value)} + className="shadow-sm focus:ring-blue-500 focus:border-blue-500 block w-full sm:text-sm border-gray-300 rounded-md" + placeholder="0.0" + disabled={isRefilling} + /> +
+ +
+ +
+

Note: This will initiate a transfer from the main treasury wallet to the service wallet.

+

Current gas fees will be calculated automatically.

+
+
+
+ + {/* Balance History */} +
+
+
+

+ Recent Balance Changes +

+

+ Track recent deposits, withdrawals, and service usage. +

+
+
    +
  • +
    +
    +
    +
    + +
    +
    +
    +
    ETH Refill
    +
    Treasury → Service Wallet
    +
    +
    +
    +
    +0.5 ETH
    +
    2 hours ago
    +
    +
    +
  • + +
  • +
    +
    +
    +
    + +
    +
    +
    +
    Transaction Fees
    +
    Bridge transactions (batch of 15)
    +
    +
    +
    +
    -0.045 ETH
    +
    4 hours ago
    +
    +
    +
  • + +
  • +
    +
    +
    +
    + +
    +
    +
    +
    NYM Distribution
    +
    Sent to users (batch of 8)
    +
    +
    +
    +
    -892.5 NYM
    +
    6 hours ago
    +
    +
    +
  • +
+
+
+
+ + + ); +} \ No newline at end of file diff --git a/pages/dashboard/downloads.tsx b/pages/dashboard/downloads.tsx new file mode 100644 index 0000000..b4c210a --- /dev/null +++ b/pages/dashboard/downloads.tsx @@ -0,0 +1,393 @@ +// Charts removed for compatibility +import { + CloudArrowDownIcon, + DevicePhoneMobileIcon, + ComputerDesktopIcon, + ArrowTopRightOnSquareIcon, +} from '@heroicons/react/24/outline'; +import Layout from '../../components/Layout'; +import ProtectedRoute from '../../components/ProtectedRoute'; +import { mockAppDownloads } from '../../data/mockData'; + +const COLORS = ['#3B82F6', '#10B981', '#F59E0B', '#EF4444', '#8B5CF6']; + +export default function Downloads() { + // Calculate summary stats + const totalDownloads = mockAppDownloads.reduce((sum, app) => sum + app.downloads, 0); + const androidDownloads = mockAppDownloads.filter(app => app.platform === 'Android').reduce((sum, app) => sum + app.downloads, 0); + const iosDownloads = mockAppDownloads.filter(app => app.platform === 'iOS').reduce((sum, app) => sum + app.downloads, 0); + + // Platform distribution data + const platformData = mockAppDownloads.reduce((acc, app) => { + const existing = acc.find(item => item.platform === app.platform); + if (existing) { + existing.downloads += app.downloads; + } else { + acc.push({ platform: app.platform, downloads: app.downloads }); + } + return acc; + }, [] as { platform: string; downloads: number }[]); + + // Growth trend data (simulated) + const growthData = [ + { month: 'Mar', downloads: 156 }, + { month: 'Apr', downloads: 289 }, + { month: 'May', downloads: 445 }, + { month: 'Jun', downloads: 623 }, + { month: 'Jul', downloads: 892 }, + { month: 'Aug', downloads: totalDownloads }, + ]; + + const getPlatformIcon = (platform: string) => { + switch (platform) { + case 'Android': + case 'iOS': + return DevicePhoneMobileIcon; + default: + return ComputerDesktopIcon; + } + }; + + const getFileSize = (sizeString: string) => { + const match = sizeString.match(/(\d+(?:\.\d+)?)\s*(MiB|MB|GiB|GB)/); + if (!match) return 0; + + const size = parseFloat(match[1]); + const unit = match[2]; + + // Convert to MB for comparison + switch (unit) { + case 'GiB': + case 'GB': + return size * 1024; + case 'MiB': + case 'MB': + default: + return size; + } + }; + + return ( + + +
+
+
+

App Downloads

+

+ Monitor MTM VPN application download metrics across all platforms. +

+
+
+ + {/* Summary Stats */} +
+
+
+
+
+ +
+
+
+
+ Total Downloads +
+
+ {totalDownloads.toLocaleString()} +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ Android Downloads +
+
+ {androidDownloads.toLocaleString()} +
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ iOS Downloads +
+
+ {iosDownloads.toLocaleString()} +
+
+
+
+
+
+
+ + {/* Charts */} +
+ {/* Downloads by Version */} +
+
+

Downloads by Version

+
+ {mockAppDownloads.map((app, index) => ( +
+
a.downloads))) * 100}%`, + minHeight: '4px' + }} + /> +
+ {app.version.replace('v', '').replace('-mtm-', ' ')} +
+
{app.downloads}
+
+ ))} +
+
+
+ + {/* Platform Distribution */} +
+
+

Platform Distribution

+
+ {platformData.map((platform, index) => { + const percentage = (platform.downloads / totalDownloads) * 100; + const colors = ['bg-blue-500', 'bg-green-500', 'bg-yellow-500', 'bg-red-500', 'bg-purple-500']; + return ( +
+
{platform.platform}
+
+
+
+
+
+
+ {platform.downloads.toLocaleString()} ({percentage.toFixed(1)}%) +
+
+ ); + })} +
+
+
+
+ + {/* Growth Trend */} +
+
+

Download Growth Trend

+
+
+ {growthData.map((data, index) => { + const height = (data.downloads / Math.max(...growthData.map(d => d.downloads))) * 100; + const prevHeight = index > 0 ? (growthData[index - 1].downloads / Math.max(...growthData.map(d => d.downloads))) * 100 : height; + return ( +
+ {index > 0 && ( + + + + )} +
+
{data.month}
+
{data.downloads.toLocaleString()}
+
+ ); + })} +
+
+
+
+ + {/* Version Details Table */} +
+
+
+
+ + + + + + + + + + + + + {mockAppDownloads + .sort((a, b) => new Date(b.releaseDate).getTime() - new Date(a.releaseDate).getTime()) + .map((app, index) => { + const PlatformIcon = getPlatformIcon(app.platform); + return ( + + + + + + + + + + + + + + ); + })} + +
+ Version + + Platform + + Downloads + + File Size + + Release Date + + Actions +
+ {app.version} + +
+ + {app.platform} +
+
+
+ + {app.downloads.toLocaleString()} + +
+
a.downloads))) * 100, 100)}%` + }} + /> +
+
+
+ {app.fileSize} + + {app.releaseDate.toLocaleDateString()} + + + View Release + + +
+
+
+
+
+ + {/* Additional Metrics */} +
+ {/* Top Performing Version */} +
+
+

Top Performing Version

+ {(() => { + const topVersion = mockAppDownloads.reduce((max, app) => + app.downloads > max.downloads ? app : max + ); + return ( +
+
+

Version

+

{topVersion.version}

+

{topVersion.platform}

+
+
+

Downloads

+

{topVersion.downloads.toLocaleString()}

+

+ {((topVersion.downloads / totalDownloads) * 100).toFixed(1)}% of total +

+
+
+ ); + })()} +
+
+ + {/* Average File Size */} +
+
+

File Size Distribution

+
+ {mockAppDownloads.map((app) => ( +
+ + {app.version} ({app.platform}) + + + {app.fileSize} + +
+ ))} +
+
+ Average Size + + {(mockAppDownloads.reduce((sum, app) => sum + getFileSize(app.fileSize), 0) / mockAppDownloads.length).toFixed(1)} MiB + +
+
+
+
+
+
+
+ + + ); +} \ No newline at end of file diff --git a/pages/dashboard/failed.tsx b/pages/dashboard/failed.tsx new file mode 100644 index 0000000..0a29fdd --- /dev/null +++ b/pages/dashboard/failed.tsx @@ -0,0 +1,286 @@ +import { useState } from 'react'; +import { + XCircleIcon, + ExclamationTriangleIcon, + ArrowPathIcon, + ArrowTopRightOnSquareIcon, +} from '@heroicons/react/24/outline'; +import Layout from '../../components/Layout'; +import ProtectedRoute from '../../components/ProtectedRoute'; +import { mockTransactions, mockBridgeTransactions, mockSwapTransactions } from '../../data/mockData'; + +export default function FailedTransactions() { + // Get only failed transactions + const failedTransactions = [ + ...mockTransactions.filter(tx => tx.error).map(tx => ({ ...tx, type: 'MTM to NYM' as const })), + ...mockBridgeTransactions.filter(tx => tx.error).map(tx => ({ ...tx, type: 'Bridge' as const })), + ...mockSwapTransactions.filter(tx => tx.error).map(tx => ({ ...tx, type: 'Swap' as const })), + ].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + + const [retryingTx, setRetryingTx] = useState(null); + + const handleRetry = async (txId: string | number, type: string) => { + setRetryingTx(`${type}-${txId}`); + + // Simulate retry operation + setTimeout(() => { + setRetryingTx(null); + // In a real app, you would trigger the retry logic here + alert(`Retry initiated for transaction ${txId} (${type})`); + }, 2000); + }; + + const getExplorerUrl = (hash: string, type: 'solana' | 'ethereum' | 'nym') => { + switch (type) { + case 'solana': + return `https://explorer.solana.com/tx/${hash}`; + case 'ethereum': + return `https://etherscan.io/tx/${hash}`; + case 'nym': + return `https://explorer.nyx.net/transactions/${hash}`; + default: + return '#'; + } + }; + + const getErrorCategory = (error: string) => { + if (error.toLowerCase().includes('insufficient')) { + return { category: 'Insufficient Funds', color: 'bg-red-100 text-red-800', icon: XCircleIcon }; + } else if (error.toLowerCase().includes('network') || error.toLowerCase().includes('timeout')) { + return { category: 'Network Issue', color: 'bg-yellow-100 text-yellow-800', icon: ExclamationTriangleIcon }; + } else if (error.toLowerCase().includes('slippage') || error.toLowerCase().includes('failed')) { + return { category: 'Transaction Failed', color: 'bg-orange-100 text-orange-800', icon: XCircleIcon }; + } else { + return { category: 'Other Error', color: 'bg-gray-100 text-gray-800', icon: ExclamationTriangleIcon }; + } + }; + + const errorCategories = failedTransactions.reduce((acc, tx) => { + const { category } = getErrorCategory(tx.error || ''); + acc[category] = (acc[category] || 0) + 1; + return acc; + }, {} as Record); + + return ( + + +
+
+
+

Failed Transactions

+

+ Monitor and troubleshoot failed transactions across all services. +

+
+
+ + {/* Error Categories Summary */} +
+ {Object.entries(errorCategories).map(([category, count]) => ( +
+
+
+
+ +
+
+
+
+ {category} +
+
+ {count} +
+
+
+
+
+
+ ))} +
+ + {/* Failed Transactions Table */} +
+
+
+
+ + + + + + + + + + + + + {failedTransactions.length > 0 ? ( + failedTransactions.map((tx, index) => { + const { category, color, icon: ErrorIcon } = getErrorCategory(tx.error || ''); + const txKey = `${tx.type}-${tx.id}`; + const isRetrying = retryingTx === txKey; + + return ( + + + + + + + + + + + + + + ); + }) + ) : ( + + + + )} + +
+ Type + + Transaction Details + + Error Category + + Error Details + + Failed At + + Actions +
+
+ + {tx.type} +
+
+
+ {/* Main transaction hash */} + {'transactionHash' in tx && tx.transactionHash && ( +
+ + {tx.transactionHash.slice(0, 12)}... + + + + +
+ )} + + {/* From address */} + {'fromAddress' in tx && tx.fromAddress && ( +
+ From: {tx.fromAddress.slice(0, 8)}... +
+ )} + + {/* Amount */} + {(('nymAmount' in tx && tx.nymAmount) || ('ethAmount' in tx && tx.ethAmount)) && ( +
+ Amount: {('nymAmount' in tx && tx.nymAmount) ? `${tx.nymAmount} NYM` : + ('ethAmount' in tx && tx.ethAmount) ? `${tx.ethAmount} ETH` : ''} +
+ )} +
+
+ + {category} + + +
+

+ {tx.error} +

+
+
+ {new Date(tx.createdAt).toLocaleString()} + + +
+
+ + + +

No failed transactions

+

+ All transactions are processing successfully. +

+
+
+
+
+
+
+ + {/* Error Analysis */} + {failedTransactions.length > 0 && ( +
+
+
+ +
+
+

+ Common Error Patterns +

+
+
    + {Object.entries(errorCategories).map(([category, count]) => ( +
  • + {category}: {count} occurrence{count !== 1 ? 's' : ''} + {category === 'Insufficient Funds' && ( + + - Consider refilling service wallets + + )} + {category === 'Network Issue' && ( + + - May resolve automatically or need retry + + )} +
  • + ))} +
+
+
+
+
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/pages/dashboard/index.tsx b/pages/dashboard/index.tsx new file mode 100644 index 0000000..d402140 --- /dev/null +++ b/pages/dashboard/index.tsx @@ -0,0 +1,217 @@ +// Charts removed for compatibility +import { + ArrowsRightLeftIcon, + CheckCircleIcon, + XCircleIcon, + CurrencyDollarIcon, + UsersIcon, + CloudArrowDownIcon, +} from '@heroicons/react/24/outline'; +import { ArrowUpIcon, ArrowDownIcon } from '@heroicons/react/20/solid'; +import Layout from '../../components/Layout'; +import ProtectedRoute from '../../components/ProtectedRoute'; +import { mockDashboardStats, mockTransactions, mockAppDownloads } from '../../data/mockData'; + +const stats = [ + { + id: 1, + name: 'Total Transactions', + stat: mockDashboardStats.totalTransactions.toLocaleString(), + icon: ArrowsRightLeftIcon, + change: '+4.5%', + changeType: 'increase', + }, + { + id: 2, + name: 'Successful', + stat: mockDashboardStats.successfulTransactions.toLocaleString(), + icon: CheckCircleIcon, + change: '+2.1%', + changeType: 'increase', + }, + { + id: 3, + name: 'Failed', + stat: mockDashboardStats.failedTransactions.toLocaleString(), + icon: XCircleIcon, + change: '-8.3%', + changeType: 'decrease', + }, + { + id: 4, + name: 'Total Volume', + stat: `$${mockDashboardStats.totalVolume}`, + icon: CurrencyDollarIcon, + change: '+12.2%', + changeType: 'increase', + }, + { + id: 5, + name: 'Active Users', + stat: mockDashboardStats.activeUsers.toLocaleString(), + icon: UsersIcon, + change: '+7.8%', + changeType: 'increase', + }, + { + id: 6, + name: 'Total Downloads', + stat: mockDashboardStats.totalDownloads.toLocaleString(), + icon: CloudArrowDownIcon, + change: '+15.1%', + changeType: 'increase', + }, +]; + +// Generate mock chart data +const transactionChartData = [ + { name: 'Jan', transactions: 850, volume: 125000 }, + { name: 'Feb', transactions: 920, volume: 142000 }, + { name: 'Mar', transactions: 1150, volume: 189000 }, + { name: 'Apr', transactions: 980, volume: 156000 }, + { name: 'May', transactions: 1200, volume: 198000 }, + { name: 'Jun', transactions: 1350, volume: 223000 }, + { name: 'Jul', transactions: 1180, volume: 201000 }, + { name: 'Aug', transactions: mockDashboardStats.totalTransactions, volume: 245000 }, +]; + +const downloadChartData = mockAppDownloads.map(download => ({ + version: download.version, + downloads: download.downloads, + platform: download.platform, +})); + +export default function Dashboard() { + return ( + + +
+

Dashboard Overview

+ + {/* Stats */} +
+ {stats.map((item) => ( +
+
+
+
+

{item.name}

+
+
+

{item.stat}

+

+ {item.changeType === 'increase' ? ( + + ) : ( + + )} + {item.changeType === 'increase' ? 'Increased' : 'Decreased'} by + {item.change} +

+
+
+ ))} +
+ + {/* Charts */} +
+ {/* Transaction Volume Chart */} +
+
+

Transaction Volume Trend

+
+ {transactionChartData.map((data, index) => ( +
+
d.transactions))) * 100}%`, + minHeight: '4px' + }} + /> +
{data.name}
+
{data.transactions.toLocaleString()}
+
+ ))} +
+
+
+ + {/* App Downloads Chart */} +
+
+

App Downloads by Version

+
+ {downloadChartData.map((data, index) => ( +
+
d.downloads))) * 100}%`, + minHeight: '4px' + }} + /> +
+ {data.version.replace('mtm-vpn-android-', '')} +
+
{data.downloads}
+
+ ))} +
+
+
+
+ + {/* Recent Transactions */} +
+
+

Recent Transactions

+ + View all → + +
+
    + {mockTransactions.slice(0, 5).map((transaction) => ( +
  • +
    +
    +
    + {transaction.error ? ( + + ) : ( + + )} +
    +
    +
    + {transaction.transactionHash.slice(0, 8)}...{transaction.transactionHash.slice(-8)} +
    +
    + From: {transaction.fromAddress.slice(0, 8)}...{transaction.fromAddress.slice(-8)} +
    +
    +
    +
    + {transaction.createdAt.toLocaleDateString()} +
    +
    +
  • + ))} +
+
+
+ + + ); +} \ No newline at end of file diff --git a/pages/dashboard/support.tsx b/pages/dashboard/support.tsx new file mode 100644 index 0000000..ca16241 --- /dev/null +++ b/pages/dashboard/support.tsx @@ -0,0 +1,368 @@ +import { useState } from 'react'; +import { + ChatBubbleLeftRightIcon, + ExclamationTriangleIcon, + CheckCircleIcon, + ClockIcon, + UserCircleIcon, +} from '@heroicons/react/24/outline'; +import Layout from '../../components/Layout'; +import ProtectedRoute from '../../components/ProtectedRoute'; +import { mockSupportTickets } from '../../data/mockData'; +import { SupportTicket } from '../../types'; + +export default function Support() { + const [selectedTicket, setSelectedTicket] = useState(null); + const [responseMessage, setResponseMessage] = useState(''); + const [filterStatus, setFilterStatus] = useState('all'); + + const filteredTickets = mockSupportTickets.filter(ticket => + filterStatus === 'all' || ticket.status === filterStatus + ); + + const getStatusIcon = (status: string) => { + switch (status) { + case 'open': + return ExclamationTriangleIcon; + case 'in-progress': + return ClockIcon; + case 'resolved': + case 'closed': + return CheckCircleIcon; + default: + return ChatBubbleLeftRightIcon; + } + }; + + const getStatusColor = (status: string) => { + switch (status) { + case 'open': + return 'bg-red-100 text-red-800'; + case 'in-progress': + return 'bg-yellow-100 text-yellow-800'; + case 'resolved': + return 'bg-green-100 text-green-800'; + case 'closed': + return 'bg-gray-100 text-gray-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + }; + + const getPriorityColor = (priority: string) => { + switch (priority) { + case 'critical': + return 'bg-red-100 text-red-800'; + case 'high': + return 'bg-orange-100 text-orange-800'; + case 'medium': + return 'bg-yellow-100 text-yellow-800'; + case 'low': + return 'bg-green-100 text-green-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + }; + + const handleSendResponse = (e: React.FormEvent) => { + e.preventDefault(); + if (!responseMessage.trim() || !selectedTicket) return; + + // In a real app, this would update the ticket with the new response + alert(`Response sent to ticket ${selectedTicket.id}`); + setResponseMessage(''); + }; + + const handleStatusChange = (ticketId: string, newStatus: string) => { + // In a real app, this would update the ticket status + alert(`Ticket ${ticketId} status changed to ${newStatus}`); + }; + + // Calculate stats + const stats = { + total: mockSupportTickets.length, + open: mockSupportTickets.filter(t => t.status === 'open').length, + inProgress: mockSupportTickets.filter(t => t.status === 'in-progress').length, + resolved: mockSupportTickets.filter(t => t.status === 'resolved').length, + }; + + return ( + + +
+
+
+

Customer Support

+

+ Manage customer support tickets and communications. +

+
+
+ + {/* Summary Stats */} +
+
+
+
+
+ +
+
+
+
Total Tickets
+
{stats.total}
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
Open
+
{stats.open}
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
In Progress
+
{stats.inProgress}
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+
Resolved
+
{stats.resolved}
+
+
+
+
+
+
+ +
+ {/* Ticket List */} +
+
+
+
+

Support Tickets

+ +
+ +
+ {filteredTickets.map((ticket) => { + const StatusIcon = getStatusIcon(ticket.status); + return ( +
setSelectedTicket(ticket)} + className={`p-4 border rounded-lg cursor-pointer transition-colors ${ + selectedTicket?.id === ticket.id + ? 'border-blue-500 bg-blue-50' + : 'border-gray-200 hover:border-gray-300' + }`} + > +
+
+
+ + + {ticket.status.replace('-', ' ')} + + + {ticket.priority} + +
+

+ {ticket.subject} +

+

+ {ticket.email} +

+

+ {new Date(ticket.createdAt).toLocaleDateString()} +

+
+
+
+ ); + })} +
+
+
+
+ + {/* Ticket Detail */} +
+ {selectedTicket ? ( +
+
+
+
+

+ {selectedTicket.subject} +

+

+ Ticket ID: {selectedTicket.id} • {selectedTicket.email} +

+
+
+ + {selectedTicket.status.replace('-', ' ')} + + + {selectedTicket.priority} + +
+
+ + {/* Status Actions */} +
+ +
+ {['open', 'in-progress', 'resolved', 'closed'].map((status) => ( + + ))} +
+
+ + {/* Conversation */} +
+ {/* Original Message */} +
+
+ + + {selectedTicket.email} + + + {new Date(selectedTicket.createdAt).toLocaleString()} + +
+

+ {selectedTicket.message} +

+
+ + {/* Responses */} + {selectedTicket.responses.map((response) => ( +
+
+ + + {response.isAdminResponse ? 'Admin (You)' : selectedTicket.email} + + + {new Date(response.createdAt).toLocaleString()} + +
+

+ {response.message} +

+
+ ))} +
+ + {/* Response Form */} +
+ +