diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..e425a981 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,40 @@ +name: Generate and Deploy Docs + +on: + workflow_dispatch: # Allow manual triggering + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch all history for proper branch detection + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Update TypeDoc config for current branch + run: | + CURRENT_BRANCH=${GITHUB_REF#refs/heads/} + echo "Current branch: $CURRENT_BRANCH" + # Update gitRevision in typedoc.json to use the current branch + sed -i "s|\"gitRevision\": \"qrigin/[^\"]*\"|\"gitRevision\": \"qrigin/$CURRENT_BRANCH\"|" typedoc.mjs + + - name: Generate documentation + run: yarn typedoc + + - name: Deploy to GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: docs + clean: true + token: ${{ secrets.ACTIONS_ONLY }} # Use the ACTION_ONLY token diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml deleted file mode 100644 index c3c3d7ac..00000000 --- a/.github/workflows/lint.yaml +++ /dev/null @@ -1,25 +0,0 @@ -name: Lint - -on: - pull_request: - push: - branches: - - main - -jobs: - lint: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: yarn - - name: Build libs - run: yarn workspace gql-client run build - - name: Linter check - run: yarn lint diff --git a/.github/workflows/test-app-deployment.yaml b/.github/workflows/test-app-deployment.yaml deleted file mode 100644 index d1c736a8..00000000 --- a/.github/workflows/test-app-deployment.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: Test webapp deployment - -on: - schedule: - - cron: '0 3 * * *' - workflow_dispatch: - -jobs: - test_app_deployment: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [20.x] - steps: - - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - name: Install dependencies - run: yarn - - name: Test webapp deployment - run: ./packages/deployer/test/test-webapp-deployment-undeployment.sh - - name: Notify Vulcanize Slack on CI failure - if: ${{ always() && github.ref_name == 'main' }} - uses: ravsamhq/notify-slack-action@v2 - with: - status: ${{ job.status }} - notify_when: 'failure' - env: - SLACK_WEBHOOK_URL: ${{ secrets.VULCANIZE_SLACK_CI_ALERTS_WEBHOOK }} - - name: Notify DeepStack Slack on CI failure - if: ${{ always() && github.ref_name == 'main' }} - uses: ravsamhq/notify-slack-action@v2 - with: - status: ${{ job.status }} - notify_when: 'failure' - env: - SLACK_WEBHOOK_URL: ${{ secrets.DEEPSTACK_SLACK_CI_ALERTS_WEBHOOK }} diff --git a/.gitignore b/.gitignore index d4a85489..2749f420 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ yarn-error.log .yarnrc.yml .yarn/ .yarnrc +.cursor packages/backend/environments/local.toml packages/backend/dev/ @@ -10,3 +11,8 @@ packages/frontend/dist/ # ignore all .DS_Store files **/.DS_Store +.vscode + +# TypeDoc generated documentation +docs/ +.cursor/ diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c831fde1..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - // IntelliSense for taiwind variants - "tailwindCSS.experimental.classRegex": [ - "tv\\('([^)]*)\\')", - "(?:'|\"|`)([^\"'`]*)(?:'|\"|`)" - ] -} diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md new file mode 100644 index 00000000..cd9978a1 --- /dev/null +++ b/DOCUMENTATION.md @@ -0,0 +1,165 @@ +# Documentation Guide for Snowball Tools + +This guide explains how to write and generate documentation for the Snowball Tools project. + +## TSDoc and TypeDoc + +We use [TSDoc](https://tsdoc.org/) for documenting our TypeScript code and [TypeDoc](https://typedoc.org/) for generating API documentation from those comments. + +## Writing Documentation + +### Basic Comment Structure + +TSDoc comments start with `/**` and end with `*/`. Each line within the comment block typically starts with a `*`. + +```typescript +/** + * This is a TSDoc comment. + */ +``` + +### Documenting Functions + +```typescript +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +function add(a: number, b: number): number { + return a + b; +} +``` + +### Documenting Classes + +```typescript +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } +} +``` + +### Documenting Interfaces + +```typescript +/** + * Configuration options for the application. + * + * @public + */ +interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} +``` + +## Common TSDoc Tags + +| Tag | Description | +|-----|-------------| +| `@param` | Documents a function parameter | +| `@returns` | Documents the return value | +| `@throws` | Documents exceptions that might be thrown | +| `@example` | Provides an example of usage | +| `@remarks` | Adds additional information | +| `@deprecated` | Marks an item as deprecated | +| `@see` | Refers to related documentation | +| `@default` | Documents the default value | +| `@public`, `@protected`, `@private` | Visibility modifiers | +| `@internal` | Marks an item as internal (not part of the public API) | +| `@beta` | Marks an item as in beta stage | +| `@alpha` | Marks an item as in alpha stage | +| `@experimental` | Marks an item as experimental | + +## Generating Documentation + +To generate documentation for the project, run: + +```bash +yarn docs +``` + +This will create a `docs` directory with the generated documentation. + +To watch for changes and regenerate documentation automatically: + +```bash +yarn docs:watch +``` + +## Best Practices + +1. **Document Public APIs**: Always document public APIs thoroughly. +2. **Include Examples**: Provide examples for complex functions or classes. +3. **Be Concise**: Keep documentation clear and to the point. +4. **Use Proper Grammar**: Use proper grammar and punctuation. +5. **Update Documentation**: Keep documentation in sync with code changes. +6. **Document Parameters**: Document all parameters, including their types and purpose. +7. **Document Return Values**: Document what a function returns. +8. **Document Exceptions**: Document any exceptions that might be thrown. + +## Example Files + +For reference, check out these example files that demonstrate proper TSDoc usage: + +- `packages/backend/src/utils/tsdoc-example.ts` +- `packages/frontend/src/utils/tsdoc-example.ts` + +## Resources + +- [TSDoc Official Documentation](https://tsdoc.org/) +- [TypeDoc Official Documentation](https://typedoc.org/) +- [TypeScript Documentation](https://www.typescriptlang.org/docs/) \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 7a28e9c7..00000000 --- a/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# snowballtools-base - -This is a [yarn workspace](https://yarnpkg.com/features/workspaces) monorepo for the dashboard. - -## Getting Started - -### Install dependencies - -In the root of the project, run: - -```zsh -yarn -``` - -### Build backend - -```zsh -yarn build --ignore frontend -``` - -### Environment variables, running the development server, and deployment - -Follow the instructions in the README.md files of the [backend](packages/backend/README.md) and [frontend](packages/frontend/README.md) packages. diff --git a/docs-readme.md b/docs-readme.md new file mode 100644 index 00000000..82ab9104 --- /dev/null +++ b/docs-readme.md @@ -0,0 +1,114 @@ +# Snowball Tools API Documentation + +This documentation is automatically generated using [TypeDoc](https://typedoc.org/) from TSDoc comments in the codebase. + +## Packages + +The monorepo contains the following packages: + +- **frontend**: The frontend web application +- **backend**: The backend API server +- **gql-client**: GraphQL client library +- **deployer**: Deployment utilities + +## How to Use This Documentation + +The documentation is organized by packages and modules. You can navigate through the sidebar to explore the different parts of the codebase. + +## Contributing to Documentation + +To contribute to the documentation: + +1. Add TSDoc comments to your code using the JSDoc syntax +2. Run `yarn docs` to generate the documentation +3. Preview the documentation in the `docs` directory + +## TSDoc Comment Examples + +### Function Documentation + +```typescript +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +function add(a: number, b: number): number { + return a + b; +} +``` + +### Class Documentation + +```typescript +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } +} +``` + +### Interface Documentation + +```typescript +/** + * Configuration options for the application. + * + * @public + */ +interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} +``` \ No newline at end of file diff --git a/package.json b/package.json index 4942f53a..8ed32163 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,30 @@ "packages/*" ], "devDependencies": { + "@microsoft/tsdoc": "^0.15.1", + "chalk": "^4.1.2", + "concurrently": "^8.2.0", "depcheck": "^1.4.2", "husky": "^8.0.3", - "lerna": "^8.0.0", - "patch-package": "^8.0.0" + "lerna": "^8.2.0", + "patch-package": "^8.0.0", + "rimraf": "^6.0.1", + "tsdoc": "^0.0.4", + "typedoc": "^0.27.9", + "typedoc-plugin-markdown": "^4.4.2", + "typedoc-unhoax-theme": "^0.4.6" }, "scripts": { "prepare": "husky install", "build": "lerna run build --stream", - "lint": "lerna run lint --stream" - } -} \ No newline at end of file + "lint": "lerna run lint --stream", + "start": "yarn kill:ports && yarn dev", + "dev": "yarn && yarn build --ignore frontend && concurrently --names \"BACKEND,FRONTEND\" --prefix-colors \"blue.bold,green.bold\" --prefix \"[{name}]\" \"yarn start:backend\" \"yarn start:frontend\"", + "start:backend": "yarn workspace backend start", + "start:frontend": "yarn workspace frontend dev", + "kill:ports": "node scripts/kill-ports.js", + "docs": "yarn typedoc", + "docs:watch": "yarn typedoc --watch" + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" +} diff --git a/packages/backend/package.json b/packages/backend/package.json index d76f430b..d53a96d3 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -54,7 +54,7 @@ "@types/cookie-session": "^2.0.49", "@types/express-session": "^1.17.10", "@types/fs-extra": "^11.0.4", - "better-sqlite3": "^9.2.2", + "better-sqlite3": "^9.4.1", "copyfiles": "^2.4.1", "prettier": "^3.1.1", "workspace": "^0.0.1-preview.1" diff --git a/packages/backend/src/server.ts b/packages/backend/src/server.ts index 734893e7..d9ed06c9 100644 --- a/packages/backend/src/server.ts +++ b/packages/backend/src/server.ts @@ -1,22 +1,22 @@ -import debug from 'debug'; -import express from 'express'; -import cors from 'cors'; -import { ApolloServer } from 'apollo-server-express'; -import { createServer } from 'http'; import { ApolloServerPluginDrainHttpServer, ApolloServerPluginLandingPageLocalDefault, AuthenticationError, } from 'apollo-server-core'; +import { ApolloServer } from 'apollo-server-express'; +import cors from 'cors'; +import debug from 'debug'; +import express from 'express'; import session from 'express-session'; +import { createServer } from 'http'; -import { TypeSource } from '@graphql-tools/utils'; import { makeExecutableSchema } from '@graphql-tools/schema'; +import { TypeSource } from '@graphql-tools/utils'; import { ServerConfig } from './config'; import { DEFAULT_GQL_PATH } from './constants'; -import githubRouter from './routes/github'; import authRouter from './routes/auth'; +import githubRouter from './routes/github'; import stagingRouter from './routes/staging'; import { Service } from './service'; @@ -101,7 +101,7 @@ export const createAndStartServer = async ( } app.use( - session(sessionOptions) + session(sessionOptions) as unknown as express.RequestHandler ); server.applyMiddleware({ @@ -116,9 +116,9 @@ export const createAndStartServer = async ( app.use(express.json()); app.set('service', service); - app.use('/auth', authRouter); - app.use('/api/github', githubRouter); - app.use('/staging', stagingRouter); + app.use('/auth', authRouter as express.RequestHandler); + app.use('/api/github', githubRouter as express.RequestHandler); + app.use('/staging', stagingRouter as express.RequestHandler); app.use((err: any, req: any, res: any, next: any) => { console.error(err); diff --git a/packages/backend/src/utils/tsdoc-example.ts b/packages/backend/src/utils/tsdoc-example.ts new file mode 100644 index 00000000..2d454d61 --- /dev/null +++ b/packages/backend/src/utils/tsdoc-example.ts @@ -0,0 +1,237 @@ +/** + * This file demonstrates proper TSDoc usage for the Snowball Tools project. + * + * @packageDocumentation + * @module Utils + */ + +/** + * Configuration options for the application. + * + * @public + */ +export interface AppConfig { + /** + * The port number the server should listen on. + * @default 3000 + */ + port: number; + + /** + * The host the server should bind to. + * @default "localhost" + */ + host: string; + + /** + * Whether to enable debug mode. + * @default false + */ + debug?: boolean; +} + +/** + * Represents a user in the system. + * + * @remarks + * This class is used throughout the application to represent user data. + * + * @example + * ```ts + * const user = new User('John', 'Doe'); + * console.log(user.fullName); // "John Doe" + * ``` + */ +export class User { + /** + * Creates a new User instance. + * + * @param firstName - The user's first name + * @param lastName - The user's last name + */ + constructor( + /** The user's first name */ + public firstName: string, + /** The user's last name */ + public lastName: string + ) {} + + /** + * Gets the user's full name. + * + * @returns The user's full name as a string + */ + get fullName(): string { + return `${this.firstName} ${this.lastName}`; + } + + /** + * Updates the user's name. + * + * @param firstName - The new first name + * @param lastName - The new last name + * @returns The updated user instance + * + * @throws {Error} If either name is empty + */ + updateName(firstName: string, lastName: string): User { + if (!firstName || !lastName) { + throw new Error('First name and last name cannot be empty'); + } + + this.firstName = firstName; + this.lastName = lastName; + return this; + } +} + +/** + * Calculates the sum of two numbers. + * + * @param a - The first number + * @param b - The second number + * @returns The sum of a and b + * + * @example + * ```ts + * const result = add(1, 2); + * console.log(result); // 3 + * ``` + */ +export function add(a: number, b: number): number { + return a + b; +} + +/** + * Formats a date according to the specified format. + * + * @param date - The date to format + * @param format - The format string (default: 'YYYY-MM-DD') + * @returns The formatted date string + * + * @remarks + * This function uses a simple format string where: + * - YYYY: 4-digit year + * - MM: 2-digit month + * - DD: 2-digit day + * + * @example + * ```ts + * const date = new Date('2023-01-15'); + * const formatted = formatDate(date); + * console.log(formatted); // "2023-01-15" + * ``` + */ +export function formatDate(date: Date, format: string = 'YYYY-MM-DD'): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + + return format + .replace('YYYY', year.toString()) + .replace('MM', month) + .replace('DD', day); +} + +/** + * Type of log level for the application. + */ +export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; + +/** + * A simple logger utility. + * + * @remarks + * This logger provides methods for different log levels and can be configured + * to filter logs based on minimum level. + */ +export class Logger { + /** + * Creates a new Logger instance. + * + * @param name - The name of the logger + * @param minLevel - The minimum log level to display (default: 'info') + */ + constructor( + private name: string, + private minLevel: LogLevel = 'info' + ) {} + + /** + * Logs a debug message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + debug(message: string, data?: unknown): void { + this.log('debug', message, data); + } + + /** + * Logs an info message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + info(message: string, data?: unknown): void { + this.log('info', message, data); + } + + /** + * Logs a warning message. + * + * @param message - The message to log + * @param data - Optional data to include + */ + warn(message: string, data?: unknown): void { + this.log('warn', message, data); + } + + /** + * Logs an error message. + * + * @param message - The message to log + * @param error - Optional error to include + */ + error(message: string, error?: Error): void { + this.log('error', message, error); + } + + /** + * Internal method to handle logging. + * + * @param level - The log level + * @param message - The message to log + * @param data - Optional data to include + * + * @internal + */ + private log(level: LogLevel, message: string, data?: unknown): void { + const levels: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3 + }; + + if (levels[level] >= levels[this.minLevel]) { + const timestamp = new Date().toISOString(); + const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] [${this.name}] ${message}`; + + switch (level) { + case 'debug': + console.debug(formattedMessage, data || ''); + break; + case 'info': + console.info(formattedMessage, data || ''); + break; + case 'warn': + console.warn(formattedMessage, data || ''); + break; + case 'error': + console.error(formattedMessage, data || ''); + break; + } + } + } +} \ No newline at end of file diff --git a/packages/frontend/.gitignore b/packages/frontend/.gitignore index 05a41290..4461d15e 100644 --- a/packages/frontend/.gitignore +++ b/packages/frontend/.gitignore @@ -16,10 +16,14 @@ .env .env.local .env.development.local + .env.test.local .env.production.local +.env.local.example npm-debug.log* yarn-debug.log* yarn-error.log* -*storybook.log \ No newline at end of file +*storybook.log + +.cursor/ \ No newline at end of file diff --git a/packages/frontend/components.json b/packages/frontend/components.json new file mode 100644 index 00000000..51d59d26 --- /dev/null +++ b/packages/frontend/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 266d8db3..c48ed173 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -20,16 +20,36 @@ "@emotion/styled": "^11.13.0", "@fontsource-variable/jetbrains-mono": "^5.0.19", "@fontsource/inter": "^5.0.16", + "@hookform/resolvers": "^4.1.0", "@mui/material": "^6.1.3", - "@radix-ui/react-avatar": "^1.0.4", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-popover": "^1.0.7", - "@radix-ui/react-radio-group": "^1.1.3", - "@radix-ui/react-switch": "^1.0.3", - "@radix-ui/react-tabs": "^1.0.4", - "@radix-ui/react-toast": "^1.1.5", - "@radix-ui/react-tooltip": "^1.0.7", + "@radix-ui/react-accordion": "^1.2.3", + "@radix-ui/react-alert-dialog": "^1.1.6", + "@radix-ui/react-aspect-ratio": "^1.1.2", + "@radix-ui/react-avatar": "^1.1.3", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-collapsible": "^1.1.3", + "@radix-ui/react-context-menu": "^2.2.6", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.6", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-label": "^2.1.2", + "@radix-ui/react-menubar": "^1.1.6", + "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-radio-group": "^1.2.3", + "@radix-ui/react-scroll-area": "^1.2.3", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-separator": "^1.1.2", + "@radix-ui/react-slider": "^1.2.3", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-switch": "^1.1.3", + "@radix-ui/react-tabs": "^1.1.3", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-toggle": "^1.1.2", + "@radix-ui/react-toggle-group": "^1.1.2", + "@radix-ui/react-tooltip": "^1.1.8", "@snowballtools/material-tailwind-react-fork": "^2.1.10", "@snowballtools/smartwallet-alchemy-light": "^0.2.0", "@snowballtools/types": "^0.2.0", @@ -44,31 +64,45 @@ "@web3modal/siwe": "4.0.5", "assert": "^2.1.0", "axios": "^1.6.7", - "clsx": "^2.1.0", - "date-fns": "^3.3.1", - "ethers": "^5.6.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.0.4", + "date-fns": "^4.1.0", "downshift": "^8.3.2", + "embla-carousel-react": "^8.5.2", + "ethers": "^5.6.2", "framer-motion": "^11.0.8", "gql-client": "^1.0.0", + "input-otp": "^1.4.2", "lottie-react": "^2.4.0", + "lucide-react": "^0.475.0", "luxon": "^3.4.4", + "next-themes": "^0.4.4", "octokit": "^3.1.2", "react": "^18.2.0", "react-calendar": "^4.8.0", "react-code-blocks": "^0.1.6", - "react-day-picker": "^8.9.1", + "react-day-picker": "8.10.1", "react-dom": "^18.2.0", "react-dropdown": "^1.11.0", - "react-hook-form": "^7.49.0", + "react-hook-form": "^7.54.2", "react-oauth-popup": "^1.0.5", + "react-resizable-panels": "^2.1.7", "react-router-dom": "^6.20.1", "react-timer-hook": "^3.0.7", + "recharts": "^2.15.1", "siwe": "2.1.4", + "sonner": "^2.0.0", + "tailwind-merge": "^3.0.1", "tailwind-variants": "^0.2.0", + "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^2.15.1", "uuid": "^9.0.1", + "vaul": "^1.1.2", "viem": "^2.7.11", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zod": "^3.24.2", + "zustand": "^5.0.3" }, "devDependencies": { "@chromatic-com/storybook": "^1.3.3", @@ -83,7 +117,7 @@ "@types/jest": "^27.5.2", "@types/lodash": "^4.17.0", "@types/luxon": "^3.3.7", - "@types/node": "^16.18.68", + "@types/node": "^22.13.5", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@types/uuid": "^9.0.8", diff --git a/packages/frontend/public/lottie/logo.json b/packages/frontend/public/lottie/logo.json deleted file mode 100644 index b0d7420f..00000000 --- a/packages/frontend/public/lottie/logo.json +++ /dev/null @@ -1,1838 +0,0 @@ -{ - "nm": "logo", - "v": "5.9.6", - "fr": 60, - "ip": 0, - "op": 128.60000000000002, - "w": 40, - "h": 40, - "ddd": 0, - "markers": [], - "assets": [ - { - "nm": "icon", - "fr": 60, - "id": "538:1272", - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 5, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 6, - "hd": false, - "nm": "logo - Shape Mask", - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [0, 0] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [12, 0], - [28, 0], - [40, 12], - [40, 28], - [28, 40], - [12, 40], - [0, 28], - [0, 12], - [12, 0], - [12, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, -6.6274], - [0, 0], - [6.6274, 0], - [0, 0], - [0, 6.6274], - [0, 0], - [-6.6274, 0], - [0, 0] - ], - "o": [ - [0, 0], - [6.627420000000001, 0], - [0, 0], - [0, 6.627420000000001], - [0, 0], - [-6.62742, 0], - [0, 0], - [0, -6.62742], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { "a": 0, "k": [0, 1, 0, 1] }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - } - ] - }, - { - "nm": "check, checmark", - "fr": 60, - "id": "538:1273", - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 7, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 8, - "hd": false, - "nm": "check, checmark - Null", - "parent": 7, - "ks": { - "a": { "a": 0, "k": [6, 6] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [14, 14] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 9, - "ty": 0, - "nm": "icon", - "td": 1, - "refId": "538:1272", - "sr": 1, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "ao": 0, - "w": 40, - "h": 40, - "ip": 0, - "op": 129.60000000000002, - "st": 0, - "hd": false, - "bm": 0 - }, - { - "ty": 4, - "ddd": 0, - "ind": 10, - "hd": false, - "nm": "check, checmark - Shape Mask", - "parent": 8, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [0, 0], - [12, 0], - [12, 0], - [12, 12], - [12, 12], - [0, 12], - [0, 12], - [0, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0] - ], - "o": [ - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { "a": 0, "k": [0, 1, 0, 1] }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ], - "tt": 1 - } - ] - }, - { - "nm": "check, checmark", - "fr": 60, - "id": "ltgojk9vajqo3xuqysw", - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 21, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 22, - "hd": false, - "nm": "check, checmark - Null", - "parent": 21, - "ks": { - "a": { "a": 0, "k": [6, 6] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [14, 14] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 15, - "hd": false, - "nm": "Icon - Null", - "parent": 22, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [1.5, 2] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 16, - "hd": false, - "nm": "Icon - Stroke", - "parent": 15, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1, - "ty": 4, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": false, - "v": [ - [0, 5.5], - [3.1471, 8], - [9, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, 0] - ], - "o": [ - [0, 0], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "st", - "o": { "a": 0, "k": 100 }, - "w": { "a": 0, "k": 2 }, - "c": { - "a": 0, - "k": [ - 0.24705882352941178, 0.5058823529411764, - 0.9254901960784314, 1 - ] - }, - "ml": 4, - "lc": 2, - "lj": 2, - "nm": "Stroke", - "hd": false - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - }, - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "rc", - "nm": "Rectangle", - "hd": false, - "p": { "a": 0, "k": [5.5, 5] }, - "s": { "a": 0, "k": [22, 20] }, - "r": { "a": 0, "k": 0 } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 0 }, - "c": { "a": 0, "k": [0, 1, 0, 1] }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - } - ] - }, - { - "nm": "[FRAME] icon - Null / icon - Stroke / icon - Null / check, checmark - Null / icon - Null / check, checmark - Null / check, checmark / check, checmark / icon", - "fr": 60, - "id": "ltgojk9svnf3mrgghfq", - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 17, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 18, - "hd": false, - "nm": "icon - Stroke", - "parent": 17, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1, - "ty": 4, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [14, 0], - [14, 0], - [28, 14], - [28, 14], - [14, 28], - [14, 28], - [0, 14], - [0, 14], - [14, 0], - [14, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, -7.732], - [0, 0], - [7.732, 0], - [0, 0], - [0, 7.732], - [0, 0], - [-7.732, 0], - [0, 0] - ], - "o": [ - [0, 0], - [7.73199, 0], - [0, 0], - [0, 7.73199], - [0, 0], - [-7.73199, 0], - [0, 0], - [0, -7.73199], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "st", - "o": { "a": 0, "k": 6 }, - "w": { "a": 0, "k": 2 }, - "c": { - "a": 0, - "k": [ - 0.03137254901960784, 0.1843137254901961, - 0.33725490196078434, 1 - ] - }, - "ml": 4, - "lc": 1, - "lj": 1, - "nm": "Stroke", - "hd": false - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - }, - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "rc", - "nm": "Rectangle", - "hd": false, - "p": { "a": 0, "k": [14.5, 14.5] }, - "s": { "a": 0, "k": [58, 58] }, - "r": { "a": 0, "k": 0 } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 0 }, - "c": { "a": 0, "k": [0, 1, 0, 1] }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ], - "hasMask": true, - "masksProperties": [ - { - "nm": "Mask", - "pt": { - "a": 0, - "k": { - "c": true, - "v": [ - [14, 0], - [14, 0], - [28, 14], - [28, 14], - [14, 28], - [14, 28], - [0, 14], - [0, 14], - [14, 0], - [14, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, -7.732], - [0, 0], - [7.732, 0], - [0, 0], - [0, 7.732], - [0, 0], - [-7.732, 0], - [0, 0] - ], - "o": [ - [0, 0], - [7.73199, 0], - [0, 0], - [0, 7.73199], - [0, 0], - [-7.73199, 0], - [0, 0], - [0, -7.73199], - [0, 0], - [0, 0] - ] - } - }, - "o": { "a": 0, "k": 100 }, - "mode": "a", - "x": { "a": 0, "k": 0 } - } - ] - }, - { - "ty": 3, - "ddd": 0, - "ind": 19, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 20, - "hd": false, - "nm": "check, checmark - Null", - "parent": 19, - "ks": { - "a": { "a": 0, "k": [6, 6] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [14, 14] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 21, - "hd": false, - "nm": "icon - Null", - "ks": { - "a": { "a": 0, "k": [14, 14] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [19.5, 20] }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 83.39999999999999, - "s": [-0.1, -0.1], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 129.60000000000002, "s": [100, 100] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 22, - "hd": false, - "nm": "check, checmark - Null", - "parent": 21, - "ks": { - "a": { "a": 0, "k": [6, 6] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [14, 14] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 23, - "ty": 0, - "nm": "check, checmark", - "td": 1, - "refId": "538:1273", - "sr": 1, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "ao": 0, - "w": 40, - "h": 40, - "ip": 0, - "op": 129.60000000000002, - "st": 0, - "hd": false, - "bm": 0 - }, - { - "ddd": 0, - "ind": 24, - "ty": 0, - "nm": "check, checmark", - "refId": "ltgojk9vajqo3xuqysw", - "sr": 1, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "ao": 0, - "w": 40, - "h": 40, - "ip": 0, - "op": 129.60000000000002, - "st": 0, - "hd": false, - "bm": 0, - "tt": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 25, - "hd": false, - "nm": "icon", - "parent": 17, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 75, - "op": 240, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [14, 0], - [14, 0], - [28, 14], - [28, 14], - [14, 28], - [14, 28], - [0, 14], - [0, 14], - [14, 0], - [14, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, -7.732], - [0, 0], - [7.732, 0], - [0, 0], - [0, 7.732], - [0, 0], - [-7.732, 0], - [0, 0] - ], - "o": [ - [0, 0], - [7.73199, 0], - [0, 0], - [0, 7.73199], - [0, 0], - [-7.73199, 0], - [0, 0], - [0, -7.73199], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { "a": 0, "k": [1, 1, 1, 1] }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - } - ] - }, - { - "nm": "logo", - "fr": 60, - "id": "ltgojk9qxrn7edmrf1", - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 26, - "hd": false, - "nm": "logo - Null", - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [0, 0] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 27, - "ty": 0, - "nm": "icon", - "refId": "ltgojk9svnf3mrgghfq", - "sr": 1, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "ao": 0, - "w": 40, - "h": 40, - "ip": 0, - "op": 129.60000000000002, - "st": 0, - "hd": false, - "bm": 0 - }, - { - "ty": 3, - "ddd": 0, - "ind": 28, - "hd": false, - "nm": "Group 7230 - Null", - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [5, 10] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ty": 3, - "ddd": 0, - "ind": 29, - "hd": false, - "nm": "Vector - Null", - "parent": 28, - "ks": { - "a": { "a": 0, "k": [10, 10] }, - "o": { "a": 0, "k": 100 }, - "p": { - "a": 1, - "k": [ - { - "t": 0, - "s": [19, 10], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] }, - "ti": [0, 0], - "to": [0, 0] - }, - { "t": 37.2, "s": [14.5, 10] } - ] - }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 39.6, - "s": [100, 100], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { - "t": 40.800000000000004, - "s": [98.17, 98.17], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 76.8, "s": [-0.1, -0.1] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 30, - "hd": false, - "nm": "Vector", - "parent": 29, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [9.3499, 0.0101], - [15.9234, 3.8396], - [19.9895, 10.3292], - [16.6625, 17.5485], - [9.3499, 19.9831], - [1.6132, 17.9671], - [0.0587, 10.3292], - [2.0807, 3.1528], - [9.3499, 0.0101], - [9.3499, 0.0101] - ], - "i": [ - [0, 0], - [-1.8867, -1.8299], - [-0.1424, -2.6405], - [2.0497, -1.8598], - [2.6731, -0.0744], - [1.9099, 1.9844], - [-0.0852, 2.6983], - [-1.737, 1.9292], - [-2.7464, -0.1363], - [0, 0] - ], - "o": [ - [2.641540000000001, 0.13106], - [1.9106500000000004, 1.8530800000000003], - [0.14798000000000044, 2.744489999999999], - [-1.9689200000000007, 1.7865199999999994], - [-2.7716399999999997, 0.0771000000000015], - [-1.88353, -1.9570800000000013], - [0.08142000000000002, -2.57993], - [1.8269200000000003, -2.02903], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { "a": 0, "k": [1, 1, 1, 1] }, - "nm": "Fill", - "hd": false, - "r": 2 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - }, - { - "ty": 3, - "ddd": 0, - "ind": 31, - "hd": false, - "nm": "Vector - Null", - "parent": 28, - "ks": { - "a": { "a": 0, "k": [10, 10] }, - "o": { "a": 0, "k": 100 }, - "p": { - "a": 1, - "k": [ - { - "t": 0, - "s": [16, 10], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] }, - "ti": [0, 0], - "to": [0, 0] - }, - { "t": 37.2, "s": [14.5, 10] } - ] - }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 37.8, - "s": [100, 100], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 73.8, "s": [-0.1, -0.1] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 32, - "hd": false, - "nm": "Vector", - "parent": 31, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [9.3499, 0.0101], - [15.9234, 3.8396], - [19.9895, 10.3292], - [16.6625, 17.5485], - [9.3499, 19.9831], - [1.6132, 17.9671], - [0.0587, 10.3292], - [2.0807, 3.1528], - [9.3499, 0.0101], - [9.3499, 0.0101] - ], - "i": [ - [0, 0], - [-1.8867, -1.8299], - [-0.1424, -2.6405], - [2.0497, -1.8598], - [2.6731, -0.0744], - [1.9099, 1.9844], - [-0.0852, 2.6983], - [-1.737, 1.9292], - [-2.7464, -0.1363], - [0, 0] - ], - "o": [ - [2.641540000000001, 0.13106], - [1.9106500000000004, 1.8530800000000003], - [0.14798000000000044, 2.744489999999999], - [-1.9689200000000007, 1.7865199999999994], - [-2.7716399999999997, 0.0771000000000015], - [-1.88353, -1.9570800000000013], - [0.08142000000000002, -2.57993], - [1.8269200000000003, -2.02903], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { - "a": 0, - "k": [ - 0.792156862745098, 0.8941176470588236, 0.9921568627450981, - 1 - ] - }, - "nm": "Fill", - "hd": false, - "r": 2 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - }, - { - "ty": 3, - "ddd": 0, - "ind": 33, - "hd": false, - "nm": "Vector - Null", - "parent": 28, - "ks": { - "a": { "a": 0, "k": [10, 10] }, - "o": { "a": 0, "k": 100 }, - "p": { - "a": 1, - "k": [ - { - "t": 0, - "s": [13, 10], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] }, - "ti": [0, 0], - "to": [0, 0] - }, - { "t": 37.2, "s": [14.5, 10] } - ] - }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 36, - "s": [100, 100], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 72, "s": [-0.1, -0.1] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 34, - "hd": false, - "nm": "Vector", - "parent": 33, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [9.3499, 0.0101], - [15.9234, 3.8396], - [19.9895, 10.3292], - [16.6625, 17.5485], - [9.3499, 19.9831], - [1.6132, 17.9671], - [0.0587, 10.3292], - [2.0807, 3.1528], - [9.3499, 0.0101], - [9.3499, 0.0101] - ], - "i": [ - [0, 0], - [-1.8867, -1.8299], - [-0.1424, -2.6405], - [2.0497, -1.8598], - [2.6731, -0.0744], - [1.9099, 1.9844], - [-0.0852, 2.6983], - [-1.737, 1.9292], - [-2.7464, -0.1363], - [0, 0] - ], - "o": [ - [2.641540000000001, 0.13106], - [1.9106500000000004, 1.8530800000000003], - [0.14798000000000044, 2.744489999999999], - [-1.9689200000000007, 1.7865199999999994], - [-2.7716399999999997, 0.0771000000000015], - [-1.88353, -1.9570800000000013], - [0.08142000000000002, -2.57993], - [1.8269200000000003, -2.02903], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { - "a": 0, - "k": [ - 0.5411764705882353, 0.7686274509803922, - 0.9803921568627451, 1 - ] - }, - "nm": "Fill", - "hd": false, - "r": 2 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - }, - { - "ty": 3, - "ddd": 0, - "ind": 35, - "hd": false, - "nm": "Vector - Null", - "parent": 28, - "ks": { - "a": { "a": 0, "k": [10, 10] }, - "o": { "a": 0, "k": 100 }, - "p": { - "a": 1, - "k": [ - { - "t": 0, - "s": [10, 10], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] }, - "ti": [0, 0], - "to": [0, 0] - }, - { "t": 37.2, "s": [14.5, 10] } - ] - }, - "r": { "a": 0, "k": 0 }, - "s": { - "a": 1, - "k": [ - { - "t": 46.800000000000004, - "s": [100, 100], - "o": { "x": [0.5], "y": [0.35] }, - "i": { "x": [0.15], "y": [1] } - }, - { "t": 82.8, "s": [-0.1, -0.1] } - ] - }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ty": 4, - "ddd": 0, - "ind": 36, - "hd": false, - "nm": "Vector", - "parent": 35, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [9.3499, 0.0101], - [15.9234, 3.8396], - [19.9895, 10.3292], - [16.6625, 17.5485], - [9.3499, 19.9831], - [1.6132, 17.9671], - [0.0587, 10.3292], - [2.0807, 3.1528], - [9.3499, 0.0101], - [9.3499, 0.0101] - ], - "i": [ - [0, 0], - [-1.8867, -1.8299], - [-0.1424, -2.6405], - [2.0497, -1.8598], - [2.6731, -0.0744], - [1.9099, 1.9844], - [-0.0852, 2.6983], - [-1.737, 1.9292], - [-2.7464, -0.1363], - [0, 0] - ], - "o": [ - [2.641540000000001, 0.13106], - [1.9106500000000004, 1.8530800000000003], - [0.14798000000000044, 2.744489999999999], - [-1.9689200000000007, 1.7865199999999994], - [-2.7716399999999997, 0.0771000000000015], - [-1.88353, -1.9570800000000013], - [0.08142000000000002, -2.57993], - [1.8269200000000003, -2.02903], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { - "a": 0, - "k": [ - 0.29411764705882354, 0.6431372549019608, - 0.9686274509803922, 1 - ] - }, - "nm": "Fill", - "hd": false, - "r": 2 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - }, - { - "ty": 4, - "ddd": 0, - "ind": 37, - "hd": false, - "nm": "logo", - "parent": 26, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1, - "shapes": [ - { - "ty": "gr", - "nm": "Group", - "hd": false, - "np": 3, - "it": [ - { - "ty": "sh", - "nm": "Path", - "hd": false, - "ks": { - "a": 0, - "k": { - "c": true, - "v": [ - [12, 0], - [28, 0], - [40, 12], - [40, 28], - [28, 40], - [12, 40], - [0, 28], - [0, 12], - [12, 0], - [12, 0] - ], - "i": [ - [0, 0], - [0, 0], - [0, -6.6274], - [0, 0], - [6.6274, 0], - [0, 0], - [0, 6.6274], - [0, 0], - [-6.6274, 0], - [0, 0] - ], - "o": [ - [0, 0], - [6.627420000000001, 0], - [0, 0], - [0, 6.627420000000001], - [0, 0], - [-6.62742, 0], - [0, 0], - [0, -6.62742], - [0, 0], - [0, 0] - ] - } - } - }, - { - "ty": "fl", - "o": { "a": 0, "k": 100 }, - "c": { - "a": 0, - "k": [ - 0.058823529411764705, 0.5254901960784314, - 0.9607843137254902, 1 - ] - }, - "nm": "Fill", - "hd": false, - "r": 1 - }, - { - "ty": "tr", - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - } - ] - } - ] - } - ] - } - ], - "layers": [ - { - "ty": 3, - "ddd": 0, - "ind": 26, - "hd": false, - "nm": "logo - Null", - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "o": { "a": 0, "k": 100 }, - "p": { "a": 0, "k": [0, 0] }, - "r": { "a": 0, "k": 0 }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 } - }, - "st": 0, - "ip": 0, - "op": 129.60000000000002, - "bm": 0, - "sr": 1 - }, - { - "ddd": 0, - "ind": 2, - "ty": 0, - "nm": "logo", - "refId": "ltgojk9qxrn7edmrf1", - "sr": 1, - "ks": { - "a": { "a": 0, "k": [0, 0] }, - "p": { "a": 0, "k": [0, 0] }, - "s": { "a": 0, "k": [100, 100] }, - "sk": { "a": 0, "k": 0 }, - "sa": { "a": 0, "k": 0 }, - "r": { "a": 0, "k": 0 }, - "o": { "a": 0, "k": 100 } - }, - "ao": 0, - "w": 40, - "h": 40, - "ip": 0, - "op": 129.60000000000002, - "st": 0, - "hd": false, - "bm": 0 - } - ], - "meta": { "a": "", "d": "", "tc": "", "g": "Aninix" } -} diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index 389702f3..90f6a48d 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,85 +1,171 @@ -import { useEffect } from 'react'; +import ProjectSearchLayout from '@/layouts/ProjectSearch'; +import ProjectsScreen from '@/pages/org-slug/ProjectsScreen'; +import { useEffect, useState } from 'react'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; - -import Projects from './pages/org-slug'; -import Settings from './pages/org-slug/Settings'; -import { - projectsRoutesWithSearch, - projectsRoutesWithoutSearch, -} from './pages/org-slug/projects/routes'; -import ProjectSearchLayout from './layouts/ProjectSearch'; +import { LoadingOverlay } from './components/loading/loading-overlay'; +import { DashboardLayout } from './layouts/DashboardLayout'; +import RootLayout from './layouts/RootLayout'; import Index from './pages'; import AuthPage from './pages/AuthPage'; -import { DashboardLayout } from './pages/org-slug/layout'; -import { BASE_URL } from 'utils/constants'; import BuyPrepaidService from './pages/BuyPrepaidService'; +import OnboardingDemoPage from './pages/OnboardingDemoPage'; +import OnboardingPage from './pages/OnboardingPage'; +import { + projectsRoutesWithoutSearch, + projectsRoutesWithSearch, +} from './pages/org-slug/projects/project-routes'; +import Settings from './pages/org-slug/Settings'; +import { BASE_URL } from './utils/constants'; + +/** + * IframeLoader component that ensures wallet iframe is loaded + * before rendering children components that depend on it. + * + * TEMPORARY SOLUTION: This is a quick fix for iframe loading issues. + * + * TODO: Future Refactoring Plan (Medium effort, 4-8 hours): + * - Move iframe management directly into WalletContextProvider + * - Handle multiple wallet-related iframes in a single location + */ +const IframeLoader = ({ children }: { children: React.ReactNode }) => { + const [iframeLoaded, setIframeLoaded] = useState(false); + + useEffect(() => { + const createIframe = () => { + // Check if iframe already exists + let iframe = document.getElementById( + 'wallet-iframe', + ) as HTMLIFrameElement; + if (!iframe) { + iframe = document.createElement('iframe'); + iframe.id = 'wallet-iframe'; + iframe.style.display = 'none'; + iframe.src = `${window.location.origin}/wallet-iframe.html`; + + iframe.onload = () => { + setIframeLoaded(true); + }; + + document.body.appendChild(iframe); + } else { + // If iframe already exists, consider it loaded + setIframeLoaded(true); + } + }; + + createIframe(); + + // Cleanup function + return () => { + const iframe = document.getElementById('wallet-iframe'); + if (iframe && iframe.parentNode) { + iframe.parentNode.removeChild(iframe); + } + }; + }, []); + + if (!iframeLoaded) { + return ; + } + + return <>{children}; +}; + +// Wrap RootLayout with IframeLoader +const LoaderWrappedRootLayout = () => ( + + + +); const router = createBrowserRouter([ { - path: ':orgSlug', - element: , + element: , children: [ { - element: , + path: ':orgSlug', + element: , children: [ { - path: '', - element: , + element: , + children: [ + { + path: '', + element: , + }, + { + path: 'projects', + children: projectsRoutesWithSearch, + }, + ], + }, + { + path: 'settings', + element: , }, { path: 'projects', - children: projectsRoutesWithSearch, + children: projectsRoutesWithoutSearch, }, ], }, { - path: 'settings', - element: , + path: '/', + element: , }, { - path: 'projects', - children: projectsRoutesWithoutSearch, + path: '/login', + element: , + }, + { + path: '/buy-prepaid-service', + element: , + errorElement:
Something went wrong!
, + }, + { + path: '/onboarding', + element: , + }, + { + path: '/onboarding-demo', + element: , }, ], }, - { - path: '/', - element: , - }, - { - path: '/login', - element: , - }, - { - path: '/buy-prepaid-service', - element: , - }, ]); +/** + * Main application component. + * Sets up routing and error handling. + * @returns {JSX.Element} The rendered application. + */ function App() { // Hacky way of checking session // TODO: Handle redirect backs + useEffect(() => { fetch(`${BASE_URL}/auth/session`, { credentials: 'include', }).then((res) => { const path = window.location.pathname; + const publicPaths = ['/login', '/onboarding', '/onboarding-demo', '/']; + console.log(res); + if (res.status !== 200) { localStorage.clear(); - - if (path !== '/login') { + if (!publicPaths.includes(path)) { window.location.pathname = '/login'; } } else { if (path === '/login') { - window.location.pathname = '/'; + window.location.pathname = '/deploy-tools'; } } }); }, []); return ( - + } /> ); } diff --git a/packages/frontend/src/assets/templates.ts b/packages/frontend/src/assets/templates.ts index 3dc7c4c8..6e7782a0 100644 --- a/packages/frontend/src/assets/templates.ts +++ b/packages/frontend/src/assets/templates.ts @@ -1,8 +1,8 @@ import { VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO, - VITE_GITHUB_PWA_TEMPLATE_REPO, VITE_GITHUB_NEXT_APP_TEMPLATE_REPO, -} from 'utils/constants'; + VITE_GITHUB_PWA_TEMPLATE_REPO, +} from '@/utils/constants'; export default [ { diff --git a/packages/frontend/src/components/CloudyFlow.tsx b/packages/frontend/src/components/CloudyFlow.tsx deleted file mode 100644 index 479779f8..00000000 --- a/packages/frontend/src/components/CloudyFlow.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import React from 'react'; - -type Props = React.PropsWithChildren<{ - className?: string; - snowZIndex?: number; -}>; - -export const CloudyFlow = ({ className, children, snowZIndex }: Props) => { - return ( -
- {children} -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); -}; diff --git a/packages/frontend/src/components/DatePicker.tsx b/packages/frontend/src/components/DatePicker.tsx deleted file mode 100644 index b8f024dc..00000000 --- a/packages/frontend/src/components/DatePicker.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import { useCallback, useMemo, useState } from 'react'; -import { format } from 'date-fns'; -import { - DayPicker, - SelectSingleEventHandler, - DateRange, -} from 'react-day-picker'; - -import { - Button, - Input, - Popover, - PopoverContent, - PopoverHandler, -} from '@snowballtools/material-tailwind-react-fork'; - -import HorizontalLine from './HorizontalLine'; - -// https://www.material-tailwind.com/docs/react/plugins/date-picker#date-picker -const DAY_PICKER_CLASS_NAMES = { - caption: 'flex justify-center py-2 mb-4 relative items-center', - caption_label: 'text-sm font-medium text-gray-900', - nav: 'flex items-center', - nav_button: - 'h-6 w-6 bg-transparent hover:bg-blue-gray-50 p-1 rounded-md transition-colors duration-300', - nav_button_previous: 'absolute left-1.5', - nav_button_next: 'absolute right-1.5', - table: 'w-full border-collapse', - head_row: 'flex font-medium text-gray-900', - head_cell: 'm-0.5 w-9 font-normal text-sm', - row: 'flex w-full mt-2', - cell: 'text-gray-600 rounded-md h-9 w-9 text-center text-sm p-0 m-0.5 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-gray-900/20 [&:has([aria-selected].day-outside)]:text-white [&:has([aria-selected])]:bg-gray-900/50 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20', - day: 'h-9 w-9 p-0 font-normal', - day_range_end: 'day-range-end', - day_selected: - 'rounded-md bg-gray-900 text-white hover:bg-gray-900 hover:text-white focus:bg-gray-900 focus:text-white', - day_today: 'rounded-md bg-gray-200 text-gray-900', - day_outside: - 'day-outside text-gray-500 opacity-50 aria-selected:bg-gray-500 aria-selected:text-gray-900 aria-selected:bg-opacity-10', - day_disabled: 'text-gray-500 opacity-50', - day_hidden: 'invisible', -}; - -type SingleDateHandler = (value: Date) => void; -type RangeDateHandler = (value: DateRange) => void; - -interface SingleDatePickerProps { - mode: 'single'; - selected?: Date; - onSelect: SingleDateHandler; -} - -interface RangeDatePickerProps { - mode: 'range'; - selected?: DateRange; - onSelect: RangeDateHandler; -} - -const DatePicker = ({ - mode = 'single', - selected, - onSelect, -}: SingleDatePickerProps | RangeDatePickerProps) => { - const [isOpen, setIsOpen] = useState(false); - const [rangeSelected, setRangeSelected] = useState(); - - const inputValue = useMemo(() => { - if (mode === 'single') { - return selected ? format(selected as Date, 'PPP') : 'Select Date'; - } - - if (mode === 'range') { - const selectedRange = selected as DateRange | undefined; - return selectedRange && selectedRange.from && selectedRange.to - ? format(selectedRange.from, 'PP') + - '-' + - format(selectedRange.to, 'PP') - : 'All time'; - } - }, [selected, mode]); - - const handleSingleSelect = useCallback((value) => { - if (value) { - (onSelect as SingleDateHandler)(value); - setIsOpen(false); - } - }, []); - - const handleRangeSelect = useCallback(() => { - if (rangeSelected?.to) { - (onSelect as RangeDateHandler)(rangeSelected); - setIsOpen(false); - } - }, [rangeSelected]); - - const components = { - IconLeft: ({ ...props }) => ( - - {'<'} - - ), - IconRight: ({ ...props }) => ( - - {'>'} - - ), - }; - - const commonDayPickerProps = { - components, - className: 'border-0', - classNames: DAY_PICKER_CLASS_NAMES, - showOutsideDays: true, - }; - - return ( - setIsOpen(value)} - > - - null} value={inputValue} /> - - {/* TODO: Figure out what placeholder is for */} - {/* @ts-ignore */} - - {mode === 'single' && ( - - )} - {mode === 'range' && ( - <> - - -
- {/* TODO: Figure out what placeholder is for */} - - {/* TODO: Figure out what placeholder is for */} - -
- - )} -
-
- ); -}; - -export default DatePicker; diff --git a/packages/frontend/src/components/Dropdown.tsx b/packages/frontend/src/components/Dropdown.tsx index 5660bea3..964bb384 100644 --- a/packages/frontend/src/components/Dropdown.tsx +++ b/packages/frontend/src/components/Dropdown.tsx @@ -9,6 +9,14 @@ export interface Option { label: string; } +/** + * Props for the Dropdown component. + * @interface DropdownProps + * @property {Option[]} options - The list of options to display in the dropdown. + * @property {(arg: ReactDropdownOption) => void} onChange - Callback fired when an option is selected. + * @property {string} [placeholder] - Placeholder text for the dropdown. + * @property {Option} [value] - The currently selected option. + */ interface DropdownProps { options: Option[]; onChange: (arg: ReactDropdownOption) => void; @@ -16,6 +24,22 @@ interface DropdownProps { value?: Option; } +/** + * A dropdown component that wraps the ReactDropdown library. + * + * @component + * @param {DropdownProps} props - The props for the Dropdown component. + * @returns {React.ReactElement} A dropdown element. + * + * @example + * ```tsx + * console.log(option)} + * placeholder="Select an option" + * /> + * ``` + */ const Dropdown = ({ placeholder, options, onChange, value }: DropdownProps) => { return ( { time: number; } +/** + * A component that formats a given time in milliseconds into a human-readable format. + * + * @component + * @param {FormatMilliSecondProps} props - The props for the FormatMillisecond component. + * @returns {React.ReactElement} A formatted time element. + * + * @example + * ```tsx + * + * ``` + */ const FormatMillisecond = ({ time, ...props }: FormatMilliSecondProps) => { const formatTime = Duration.fromMillis(time) .shiftTo('days', 'hours', 'minutes', 'seconds') diff --git a/packages/frontend/src/components/HorizontalLine.tsx b/packages/frontend/src/components/HorizontalLine.tsx index 62c6242f..0f917193 100644 --- a/packages/frontend/src/components/HorizontalLine.tsx +++ b/packages/frontend/src/components/HorizontalLine.tsx @@ -1,3 +1,14 @@ +/** + * A simple horizontal line component. + * + * @component + * @returns {React.ReactElement} A horizontal line element. + * + * @example + * ```tsx + * + * ``` + */ const HorizontalLine = () => { return
; }; diff --git a/packages/frontend/src/components/Logo.tsx b/packages/frontend/src/components/Logo.tsx index f4957f14..9c7f7808 100644 --- a/packages/frontend/src/components/Logo.tsx +++ b/packages/frontend/src/components/Logo.tsx @@ -1,9 +1,26 @@ import { Link } from 'react-router-dom'; +/** + * Props for the Logo component. + * @interface LogoProps + * @property {string} [orgSlug] - The organization slug used for the link. + */ interface LogoProps { orgSlug?: string; } +/** + * A component that renders the Snowball logo with a link to the organization's page. + * + * @component + * @param {LogoProps} props - The props for the Logo component. + * @returns {React.ReactElement} A logo element. + * + * @example + * ```tsx + * + * ``` + */ export const Logo = ({ orgSlug }: LogoProps) => { return ( diff --git a/packages/frontend/src/components/SearchBar.tsx b/packages/frontend/src/components/SearchBar.tsx index 23bed3fb..64329f3d 100644 --- a/packages/frontend/src/components/SearchBar.tsx +++ b/packages/frontend/src/components/SearchBar.tsx @@ -1,21 +1,33 @@ import React, { forwardRef, RefAttributes } from 'react'; -import { SearchIcon } from './shared/CustomIcon'; -import { Input, InputProps } from './shared/Input'; +import { IconInput, type IconInputProps } from '@/components/ui/extended/input-w-icons'; +import { Search } from 'lucide-react'; +/** + * A search bar component with an icon input. + * + * @component + * @param {InputProps & RefAttributes} props - The props for the SearchBar component. + * @returns {React.ReactElement} A search bar element. + * + * @example + * ```tsx + * console.log(e.target.value)} /> + * ``` + */ const SearchBar: React.ForwardRefRenderFunction< HTMLInputElement, - InputProps & RefAttributes + IconInputProps & RefAttributes > = ({ value, onChange, placeholder = 'Search', ...props }, ref) => { return (
- } + } onChange={onChange} value={value} type="search" placeholder={placeholder} - appearance="borderless" + // appearance="borderless" className="w-full lg:w-[459px]" {...props} ref={ref} diff --git a/packages/frontend/src/components/Stepper.tsx b/packages/frontend/src/components/Stepper.tsx index 31e7e717..f462bdf5 100644 --- a/packages/frontend/src/components/Stepper.tsx +++ b/packages/frontend/src/components/Stepper.tsx @@ -4,17 +4,42 @@ const COLOR_COMPLETED = '#059669'; const COLOR_ACTIVE = '#CFE6FC'; const COLOR_NOT_STARTED = '#F1F5F9'; +/** + * Represents a step in the stepper. + * @interface StepperValue + * @property {number} step - The step number. + * @property {string} route - The route associated with the step. + * @property {string} label - The label for the step. + */ interface StepperValue { step: number; route: string; label: string; } +/** + * Props for the Stepper component. + * @interface StepperProps + * @property {number} activeStep - The currently active step. + * @property {StepperValue[]} stepperValues - The values for each step. + */ interface StepperProps { activeStep: number; stepperValues: StepperValue[]; } +/** + * A stepper component that displays a series of steps with different states. + * + * @component + * @param {StepperProps} props - The props for the Stepper component. + * @returns {React.ReactElement} A stepper element. + * + * @example + * ```tsx + * + * ``` + */ const Stepper = ({ activeStep, stepperValues }: StepperProps) => { return ( { return currentTime; }; +/** + * Props for the Stopwatch component. + * @interface StopwatchProps + * @property {Date} offsetTimestamp - The initial timestamp for the stopwatch. + * @property {boolean} isPaused - Whether the stopwatch is paused. + */ interface StopwatchProps extends Omit { offsetTimestamp: Date; isPaused: boolean; } +/** + * A stopwatch component that tracks elapsed time. + * + * @component + * @param {StopwatchProps} props - The props for the Stopwatch component. + * @returns {React.ReactElement} A stopwatch element. + * + * @example + * ```tsx + * + * ``` + */ const Stopwatch = ({ offsetTimestamp, isPaused, ...props }: StopwatchProps) => { const { totalSeconds, pause, start } = useStopwatch({ autoStart: true, @@ -29,4 +47,4 @@ const Stopwatch = ({ offsetTimestamp, isPaused, ...props }: StopwatchProps) => { return ; }; -export { Stopwatch, setStopWatchOffset }; +export { setStopWatchOffset, Stopwatch }; diff --git a/packages/frontend/src/components/VerticalStepper.tsx b/packages/frontend/src/components/VerticalStepper.tsx index 43ff80a4..73aff806 100644 --- a/packages/frontend/src/components/VerticalStepper.tsx +++ b/packages/frontend/src/components/VerticalStepper.tsx @@ -3,6 +3,14 @@ import * as CSS from 'csstype'; // // Nav // +/** + * Describes a step in the stepper navigation. + * @interface IStepDescription + * @property {() => JSX.Element} stepContent - The content of the step. + * @property {string} [stepStateColor] - The color representing the step's state. + * @property {number} [stepStatusCircleSize] - The size of the status circle. + * @property {() => void} [onClickHandler] - Handler for click events on the step. + */ export interface IStepDescription { stepContent: () => JSX.Element; stepStateColor?: string; @@ -10,10 +18,27 @@ export interface IStepDescription { onClickHandler?: () => void | undefined; } +/** + * Props for the StepperNav component. + * @interface IStepperNavProps + * @property {IStepDescription[]} steps - The steps to display in the navigation. + */ export interface IStepperNavProps { steps: IStepDescription[]; } +/** + * A navigation component for displaying steps in a vertical layout. + * + * @component + * @param {IStepperNavProps} props - The props for the StepperNav component. + * @returns {React.ReactElement} A stepper navigation element. + * + * @example + * ```tsx + *
Step 1
}]} /> + * ``` + */ export const StepperNav = (props: IStepperNavProps): JSX.Element => { return (