diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7a73a41 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/EXPLAINER.md b/EXPLAINER.md new file mode 100644 index 0000000..918b241 --- /dev/null +++ b/EXPLAINER.md @@ -0,0 +1,248 @@ +# OPS-UI Ecosystem Explainer Guide + +This document is designed to help you easily explain what you are building, how the CLI works, and how to consume or contribute components to this ecosystem. + +--- + +## 1. What Are We Building? + +**OPS-UI** is a developer-centric, custom UI component ecosystem tailored for React applications. + +Unlike traditional UI libraries (e.g., Material UI, Chakra UI) that package components into opaque npm dependencies, OPS-UI uses a **distribution-by-code** approach (similar to `shadcn/ui`). + +### Core Concept: Ownership of Code +- **No Heavy Dependencies:** Instead of importing components from a large pre-compiled library, the **CLI downloads the source code directly** into the user's project (`/components/ui`). +- **Full Customizability:** Once added, the component code is 100% owned by the application developer. They can change styling, logic, or behavior directly in their local files. +- **Tailwind CSS & Framer Motion:** Out of the box, components use Tailwind for utility-first styling and Framer Motion / GSAP for smooth micro-animations. + +--- + +## 2. The Ecosystem Architecture + +Our repository is built as a **Turborepo monorepo**, containing three major building blocks: + +```mermaid +graph TD + subgraph Monorepo ["OPS-UI Monorepo (ot-react-packages)"] + UI["packages/ui (Core Primitives)"] + WWW["apps/www (Docs & Showcase)"] + RegistrySrc["apps/www/src/registry/"] + Scripts["apps/www/scripts/"] + CLI["packages/cli (@opstreepackage/opscli)"] + end + + subgraph Hosting ["Distribution & Registry"] + CDN["GitHub Raw / Deployed JSON Registry"] + end + + subgraph Client ["Client Project (Target Application)"] + UserApp["User's React/Next.js/Vite App"] + LocalComponents["components/ui/"] + end + + %% Build flow + WWW -->|Develop docs & custom components| RegistrySrc + Scripts -->|1. Scan components| RegistrySrc + Scripts -->|2. Build JSON manifests| CDN + + %% CLI distribution flow + CLI -->|3. Fetches component JSON| CDN + CLI -->|4. Writes source code to| LocalComponents + LocalComponents -->|5. Imported by| UserApp +``` + +### Monorepo Packages Breakdown + +1. **[apps/www](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/apps/www)**: The documentation site and the **source of truth for the registry**. Custom components are built here, cataloged, and compiled into JSON manifests. +2. **[packages/cli](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/packages/cli)**: The published CLI package (`@opstreepackage/opscli`). It acts as the pipeline that fetches components from the hosted registry and puts them into the user's local directory. +3. **[packages/ui](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/packages/ui)**: A package containing core shared UI primitives (buttons, accordions, badges, etc.) that can also be distributed. + +--- + +## 3. How the CLI Works Under the Hood + +The CLI (`@opstreepackage/opscli`) performs two major operations: **Initialization** and **Component Addition**. + +### Command A: `opscli init` +This command prepares a clean React project to receive OPS-UI components. + +``` +┌────────────────────────────────┐ +│ 1. Detect Environment │ Reads package.json, checks for TS, Tailwind, +│ & Framework │ and frameworks (Next.js, Vite, etc.) +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 2. Create Target Folders │ Creates components/ui, lib, hooks, utils, +│ │ styles, and types directories +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 3. Install Peer Dependencies │ Runs 'npm install clsx tailwind-merge ...' +│ │ to make sure peer requirements are met +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 4. Configure Alias & Utilities │ - Writes components.json +│ │ - Generates lib/utils.ts with a cn() helper +│ │ - Updates tsconfig.json with path aliases +└────────────────────────────────┘ +``` + +### Command B: `opscli add ` +This command downloads the component and integrates it into the project. + +``` +┌────────────────────────────────┐ +│ 1. Read Project Configuration │ Reads components.json to identify where to +│ │ place UI components (e.g. components/ui) +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 2. Fetch Manifest from Registry│ Fetches JSON metadata from: +│ │ https://.../registry/components/button.json +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 3. Resolve Dependencies │ - Installs required npm packages +│ │ - Recursively fetches sub-component manifests +└──────────────┬─────────────────┘ + │ + ▼ +┌────────────────────────────────┐ +│ 4. Write Component Source │ Extracts the raw code from the manifest +│ │ and writes it directly to the local folder +└────────────────────────────────┘ +``` + +--- + +## 4. How Users Add Components to Their Application + +For an end-user building an application, using OPS-UI is simple and takes three quick steps. + +### Step 1: Initialize the Project +Inside the root directory of the application, run: +```bash +# Initialize OPS-UI configuration +npx @opstreepackage/opscli@latest init +``` +*This prompts the user to choose directories for components and utilities, generates a `components.json` configuration file, and sets up the custom `cn(...)` utility.* + +### Step 2: Add Components +Install any desired component via the CLI: +```bash +# Add a single component +npx @opstreepackage/opscli@latest add button + +# Add multiple components +npx @opstreepackage/opscli@latest add badge accordion + +# Skip prompts and overwrite files automatically +npx @opstreepackage/opscli@latest add tabs -y -f +``` + +### Step 3: Import and Use +Once the command finishes, the component code is available locally in the project. Import it like any other local file: +```tsx +import { Button } from "@/components/ui/button" + +export default function MyWidget() { + return ( +
+ + +
+ ) +} +``` + +--- + +## 5. How Developers Add New Components to the UI Library +Adding a new component to the OPS-UI registry so that it becomes available via the `opscli add` command is automated using our monorepo build pipeline. + +### Step 1: Develop the Component +Create a new `.tsx` file in **[apps/www/src/components/docs/ts/](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/apps/www/src/components/docs)** (e.g., `MyCard.tsx`). + +Add standard JSDoc comments at the top of the file. The registry synchronization script will read these tags to auto-generate the description, title, and categories: +```tsx +/** + * @name MyCard + * @description A styled promotional card component with interactive hover states. + * @category ui + */ +import React from "react" +import { cn } from "@/lib/utils" + +export interface MyCardProps extends React.HTMLAttributes { + title: string + subtitle?: string +} + +export const MyCard = React.forwardRef( + ({ className, title, subtitle, children, ...props }, ref) => { + return ( +
+

{title}

+ {subtitle &&

{subtitle}

} +
{children}
+
+ ) + } +) +MyCard.displayName = "MyCard" +``` + +### Step 2: Synchronize the Registry +To tell the registry about the new component, run the synchronization script. This script scans the folder, parses the JSDoc tags, and updates the registry definition file: +```bash +# Run from the root of the monorepo +npm run registry:sync --workspace=docs +# Or change directory and run: +# cd apps/www && npm run registry:sync +``` +*This updates [apps/www/src/registry/components.ts](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/apps/www/src/registry/components.ts) automatically.* + +### Step 3: Build the JSON Manifests +Compile the registry file and components into JSON distribution files that the CLI reads: +```bash +# Run from the root of the monorepo +npm run registry:build --workspace=docs +# Or change directory and run: +# cd apps/www && npm run registry:build +``` +*This compiles the React code, transforms relative imports (e.g. `../../lib/utils` → `@/lib/utils`), and outputs JSON manifests to [apps/www/public/registry/components/](file:///c:/Users/Gourav%20singh/Desktop/ot-react-packages/apps/www/public/registry/components).* + +### Step 4: Verify Locally +Build the CLI and test it locally using node: +```bash +# 1. Build the CLI package +npm run build --workspace=@opstreepackage/opscli + +# 2. List the available components (your component should show up here) +node packages/cli/dist/index.js list + +# 3. Test installing it inside a local project +node packages/cli/dist/index.js add mycard --dry-run +``` + +Once verified, commit and push your new files. When the main branch is updated or deployed, your new component will be immediately installable by anyone using `opscli add mycard`. + +--- + +## 📝 Quick Pitch / Elevator Summary +> **"We are building a custom, highly developer-friendly React UI component platform called OPS-UI.** +> +> **Instead of force-installing large packages that can't be easily customized, we built a light CLI tool (`opscli`). When you run `opscli add button`, it downloads the raw source code of our beautiful, Tailwind-styled components directly into your own folder (`components/ui`). You have 100% control of the styling and code.** +> +> **For developers on our team, adding a new component is as simple as writing a regular `.tsx` component file, running our auto-sync command, and building the registry manifests. The CLI handles the rest."** diff --git a/README.md b/README.md index 0a41751..0f51720 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ ot-react-packages/ | `apps/www` | The documentation website **and** the source of truth for the component registry. Components listed here are what the CLI distributes. | | `apps/www/public/registry/` | **Generated output.** The CLI fetches component manifests and source files from this directory (served via GitHub raw URLs or a deployed site). | | `apps/www/src/registry/` | **Registry metadata.** Defines which components exist, their dependencies, and where their source files live. This drives the build scripts. | -| `apps/www/src/components/docs/` | Documentation-specific component implementations (Cards, Table, Sidebar, etc.) that are registered and distributed via the CLI. | +| `apps/www/src/components/docs/ts/` | Documentation-specific component implementations (Cards, Table, Sidebar, etc.) that are registered and distributed via the CLI. | | `packages/ui/` | Core UI primitives (button, accordion, badge, etc.) — shared across the monorepo and also registered for CLI distribution. | | `packages/cli/` | The published npm package `@opstreepackage/opscli`. Users install this to add components to their projects. | @@ -210,13 +210,13 @@ Place your `.tsx` file in one of these directories: | Location | When to Use | |---|---| -| `apps/www/src/components/docs/` | Custom/documentation components (recommended for new components) | +| `apps/www/src/components/docs/ts/` | Custom/documentation components (recommended for new components) | ### Step 2: Register It Open the corresponding registry file in `apps/www/src/registry/`: -- **`components.ts`** — for components in `apps/www/src/components/docs/` +- **`components.ts`** — for components in `apps/www/src/components/docs/ts/` Add an entry like this: @@ -230,7 +230,7 @@ Add an entry like this: registryDependencies: [], // other registry components it depends on files: [ { - path: "src/components/docs/MyComponent.tsx", // relative to apps/www + path: "src/components/docs/ts/MyComponent.tsx", // relative to apps/www type: "registry:ui", }, ], @@ -243,10 +243,10 @@ Add an entry like this: The registry has two scripts that work together as a pipeline: #### `npm run registry:sync` — Auto-discover components -Scans `apps/www/src/components/docs/` for `.tsx` files, extracts metadata (name, description, category from JSDoc comments), and **overwrites** `apps/www/src/registry/components.ts` with the discovered entries. This means you don't have to manually write the registry entry if you just drop a `.tsx` file in the `docs/` folder. +Scans `apps/www/src/components/docs/ts/` for `.tsx` files, extracts metadata (name, description, category from JSDoc comments), and **overwrites** `apps/www/src/registry/components.ts` with the discovered entries. This means you don't have to manually write the registry entry if you just drop a `.tsx` file in the `docs/` folder. #### `npm run registry:build` — Generate JSON manifests -Reads all entries from `apps/www/src/registry/components.ts`, then reads the actual source code directly from `src/components/docs/`, transforms imports (e.g., `@workspace/ui/lib/utils` → `@/lib/utils`), and embeds the source code into JSON manifest files in `apps/www/public/registry/`: +Reads all entries from `apps/www/src/registry/components.ts`, then reads the actual source code directly from `src/components/docs/ts/`, transforms imports (e.g., `@workspace/ui/lib/utils` → `@/lib/utils`), and embeds the source code into JSON manifest files in `apps/www/public/registry/`: | Generated File | Purpose | |---|---| @@ -258,7 +258,7 @@ Reads all entries from `apps/www/src/registry/components.ts`, then reads the act ``` ┌──────────────────────────┐ - │ src/components/docs/ │ .tsx files (your components) + │ src/components/docs/ts/ │ .tsx files (your components) └────────────┬─────────────┘ │ ▼ npm run registry:sync diff --git a/WORKFLOW.md b/WORKFLOW.md new file mode 100644 index 0000000..1d703f6 --- /dev/null +++ b/WORKFLOW.md @@ -0,0 +1,168 @@ +# OPS-UI Workflow — Registry & CLI + +--- + +## Part 1 — How the Docs Registry Works + +The docs site (`apps/www`) is both a **documentation website** and the **build machine** for the component registry. + +``` + DEVELOPER + │ + │ 1. Create a .tsx component file + │ apps/www/src/components/docs/ts/Button.tsx + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ npm run registry:sync │ +│ │ +│ Scans ──► apps/www/src/components/docs/ts/*.tsx │ +│ Reads ──► JSDoc tags (@name, @description, ...) │ +│ Writes ──► apps/www/src/registry/components.ts │ +└─────────────────────────────────────────────────────┘ + │ + │ 2. components.ts now knows about Button + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ npm run registry:build │ +│ │ +│ Reads ──► src/registry/components.ts │ +│ Reads ──► actual source code from docs/*.tsx │ +│ Fixes ──► imports (../../lib → @/lib/utils) │ +│ Embeds ──► raw source code inside JSON │ +│ Writes ──► public/registry/components/ │ +│ ├── button.json (with source) │ +│ ├── sidebar.json (with source) │ +│ └── ... │ +│ Writes ──► public/registry/registry.json │ +│ Writes ──► public/registry/index.json │ +└─────────────────────────────────────────────────────┘ + │ + │ 3. JSON files are now hosted on GitHub Raw / CDN + │ + ▼ + REGISTRY IS LIVE +``` + +### What a JSON manifest looks like + +```json +// public/registry/components/button.json +{ + "name": "button", + "type": "registry:ui", + "title": "Button", + "description": "A Button component.", + "dependencies": ["clsx", "tailwind-merge"], + "files": [ + { + "path": "Button.tsx", + "content": "import React from 'react'...", // ← full source code embedded here + "type": "registry:ui" + } + ] +} +``` + +--- + +## Part 2 — How the CLI Extracts Components + +When a user runs `opscli add button` in their own project, here is exactly what happens: + +``` + USER PROJECT + │ + │ opscli add button + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Step 1 — Check Initialization │ +│ │ +│ Does components.json exist in the project? │ +│ YES ──► continue │ +│ NO ──► throw error: "Run opscli init first" │ +└──────────────────────┬──────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Step 2 — Fetch Manifest from Registry │ +│ │ +│ GET https://raw.githubusercontent.com/.../ │ +│ registry/components/button.json │ +│ │ +│ Receives ──► JSON with embedded source code │ +└──────────────────────┬──────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Step 3 — Resolve Dependencies │ +│ │ +│ a. registryDependencies (other OPS-UI components) │ +│ ──► fetch & install those components first │ +│ │ +│ b. dependencies (npm packages) │ +│ ──► run: npm install clsx tailwind-merge ... │ +└──────────────────────┬──────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Step 4 — Write Source Code to User's Project │ +│ │ +│ Reads ──► components.json → aliases.ui path │ +│ Creates ──► components/ui/ (if missing) │ +│ Writes ──► components/ui/Button.tsx │ +│ (extracted from the JSON manifest) │ +└──────────────────────┬──────────────────────────────┘ + │ + ▼ + ✔ Component added successfully! + + You can now import it: + import { Button } from "@/components/ui/Button" +``` + +--- + +## Full End-to-End Picture + +``` + [Developer writes Button.tsx] + │ + │ registry:sync + ▼ + [src/registry/components.ts updated] + │ + │ registry:build + ▼ + [public/registry/components/button.json] ← source code embedded inside + │ + │ pushed to GitHub / deployed + ▼ + [GitHub Raw URL / CDN serves the JSON] + │ + │ user runs: opscli add button + ▼ + [CLI fetches button.json from the URL] + │ + │ extracts content field + ▼ + [writes Button.tsx into user's components/ui/] + │ + ▼ + [user imports and uses the component] ✅ +``` + +--- + +## Quick Reference — Commands + +| What you want to do | Command | Where to run | +|---|---|---| +| Add a new `.tsx` component to registry | `npm run registry:sync` | `apps/www/` | +| Build the JSON manifest files | `npm run registry:build` | `apps/www/` | +| Initialize a user project | `opscli init` | User's project root | +| Add a component to a user project | `opscli add button` | User's project root | +| List all available components | `opscli list` | Anywhere | +| Remove a component | `opscli remove button` | User's project root | diff --git a/apps/www/Dockerfile b/apps/www/Dockerfile index 9fbc7e0..043db27 100644 --- a/apps/www/Dockerfile +++ b/apps/www/Dockerfile @@ -3,28 +3,20 @@ RUN npm install -g turbo FROM base AS pruner WORKDIR /app + +# Copy the entire monorepo to ensure all workspace packages are available COPY . . -RUN turbo prune --scope=docs --docker -FROM base AS builder -WORKDIR /app -COPY --from=pruner /app/out/json/ . -COPY --from=pruner /app/out/package-lock.json ./package-lock.json +# Install dependencies from the root RUN npm install -# Copy pruned source code and turbo config -COPY --from=pruner /app/out/full/ . -COPY turbo.json turbo.json -RUN turbo run build --filter=docs... - +# Build the documentation app specifically +RUN npm run build --workspace=docs -FROM nginx:alpine AS runner -# Copy the build output and nginx config +FROM nginx:alpine +# Copy the built files from the apps/www/dist directory COPY --from=builder /app/apps/www/dist /usr/share/nginx/html COPY apps/www/nginx.conf /etc/nginx/conf.d/default.conf - - -EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] diff --git a/apps/www/app/docs/Sidebar.tsx b/apps/www/app/docs/Sidebar.tsx index 004e11c..8580d70 100644 --- a/apps/www/app/docs/Sidebar.tsx +++ b/apps/www/app/docs/Sidebar.tsx @@ -15,7 +15,9 @@ export const Sidebar = ({ isOpen, setIsOpen }: { isOpen: boolean, setIsOpen: (op exit={{ x: -100 }} transition={{ duration: 0.01, ease: "easeInOut" }} className={cn( - "[grid-sidebar] h-screen absolute top-0 lg:sticky lg:top-0 lg:pt-4 z-30 hidden shrink-0 md:block dark:bg-[#121212] bg-neutral-200/50 border-r-1 w-[var(--fd-sidebar-width)]")} + "[grid-sidebar] h-screen absolute top-0 lg:sticky lg:top-0 lg:pt-4 z-30 hidden shrink-0 md:block dark:bg-[#121212] bg-neutral-200/50 border-r-1 w-[var(--fd-sidebar-width)]", + "lg:w-[var(--lg-sidebar-width)] lg:flex lg:justify-start " + )} > diff --git a/apps/www/app/docs/component-api.ts b/apps/www/app/docs/component-api.ts index 3b8270a..c81ca55 100644 --- a/apps/www/app/docs/component-api.ts +++ b/apps/www/app/docs/component-api.ts @@ -11,6 +11,7 @@ export function componentApi() { const rootDir = process.cwd(); if (url.pathname === '/api/source' && req.method === 'GET') { + res.setHeader('Content-Type', 'application/json'); const src = url.searchParams.get('src'); if (!src) { res.statusCode = 400; @@ -19,7 +20,14 @@ export function componentApi() { } try { - const filePath = path.resolve(rootDir, src); + let filePath = path.resolve(rootDir, src); + if (!await fs.pathExists(filePath)) { + const altPath = path.resolve(rootDir, 'apps/www', src); + if (await fs.pathExists(altPath)) { + filePath = altPath; + } + } + if (!await fs.pathExists(filePath)) { res.statusCode = 404; res.end(JSON.stringify({ error: `File not found: ${src}` })); @@ -72,7 +80,7 @@ export function componentApi() { const id = name.toLowerCase().replace(/\s+/g, '-'); // 1. Save TSX file - const tsxPath = path.resolve(rootDir, `src/components/docs/${name}.tsx`); + const tsxPath = path.resolve(rootDir, `src/components/docs/ts/${name}.tsx`); await fs.writeFile(tsxPath, code); // 2. Create MDX file diff --git a/apps/www/app/docs/layout.tsx b/apps/www/app/docs/layout.tsx index 6928e12..9d6c7a6 100644 --- a/apps/www/app/docs/layout.tsx +++ b/apps/www/app/docs/layout.tsx @@ -30,6 +30,7 @@ export default function DocsLayout() { window.addEventListener("resize", handleResize) return () => window.removeEventListener("resize", handleResize) }, []) + return (
{!sidebarOpen && ( @@ -68,7 +69,9 @@ export default function DocsLayout() {
} - +
+ +
diff --git a/apps/www/app/docs/page.tsx b/apps/www/app/docs/page.tsx index f04b529..a8ada58 100644 --- a/apps/www/app/docs/page.tsx +++ b/apps/www/app/docs/page.tsx @@ -58,7 +58,7 @@ export default function Page() { return (
@@ -91,7 +91,7 @@ export default function Page() {
) : null}
-
+
{MDX ? ( Loading...
}> @@ -111,7 +111,7 @@ export default function Page() { : }
-
+
{neighbours.next ?
) }, figure: ({ className, ...props }: React.ComponentProps<"figure">) => { diff --git a/apps/www/public/Lapes.png b/apps/www/public/Lapes.png deleted file mode 100644 index 7b3306b..0000000 Binary files a/apps/www/public/Lapes.png and /dev/null differ diff --git a/apps/www/public/hero-preview.webp b/apps/www/public/hero-preview.webp deleted file mode 100644 index 44a9efb..0000000 Binary files a/apps/www/public/hero-preview.webp and /dev/null differ diff --git a/apps/www/public/img1.png b/apps/www/public/img1.png deleted file mode 100644 index 797a808..0000000 Binary files a/apps/www/public/img1.png and /dev/null differ diff --git a/apps/www/public/img2.png b/apps/www/public/img2.png deleted file mode 100644 index 1e58e02..0000000 Binary files a/apps/www/public/img2.png and /dev/null differ diff --git a/apps/www/public/img3.png b/apps/www/public/img3.png deleted file mode 100644 index 87b1bf2..0000000 Binary files a/apps/www/public/img3.png and /dev/null differ diff --git a/apps/www/public/img4.png b/apps/www/public/img4.png deleted file mode 100644 index 4cb594b..0000000 Binary files a/apps/www/public/img4.png and /dev/null differ diff --git a/apps/www/public/img5.png b/apps/www/public/img5.png deleted file mode 100644 index d773919..0000000 Binary files a/apps/www/public/img5.png and /dev/null differ diff --git a/apps/www/public/opstree_logo.jpeg b/apps/www/public/opstree_logo.jpeg new file mode 100644 index 0000000..b8eefaa Binary files /dev/null and b/apps/www/public/opstree_logo.jpeg differ diff --git a/apps/www/public/registry/components/badge.json b/apps/www/public/registry/components/badge.json index 774168a..1d8fa5c 100644 --- a/apps/www/public/registry/components/badge.json +++ b/apps/www/public/registry/components/badge.json @@ -9,7 +9,7 @@ ], "files": [ { - "path": "src/components/docs/Badge.tsx", + "path": "src/components/docs/ts/Badge.tsx", "type": "registry:ui" } ], diff --git a/apps/www/public/stage-1766235326982.png b/apps/www/public/stage-1766235326982.png deleted file mode 100644 index 13ae19a..0000000 Binary files a/apps/www/public/stage-1766235326982.png and /dev/null differ diff --git a/apps/www/src/components/docs/Cards.tsx b/apps/www/src/components/docs/Cards.tsx deleted file mode 100644 index fa1cf17..0000000 --- a/apps/www/src/components/docs/Cards.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { ArrowUp, DollarSign, Globe, TrendingUp, Zap } from "lucide-react"; -import { cn } from "@/lib/utils"; - -interface cardDetails { - title: string; - value: string; - trend: string; - positive: boolean; - icon: React.ReactNode; - background?: string; -} - -const carddata: cardDetails[] = [ - { title: "Last 30 Days", value: "€12,220.64", trend: "+22.2%", positive: true, icon: , background: "bg-neutral-50/50" }, -] - - -const Card = () => { - return ( -
- {carddata.map((kpi) => ( -
-
- {kpi.title} - - {kpi.icon} - -
-
- {kpi.value} -
- - {kpi.positive ? : } - {kpi.trend} - -
-
-
- ))} -
- ) -} - -export default Card - diff --git a/apps/www/src/components/docs/js/Addaccount.jsx b/apps/www/src/components/docs/js/Addaccount.jsx new file mode 100644 index 0000000..f4b4902 --- /dev/null +++ b/apps/www/src/components/docs/js/Addaccount.jsx @@ -0,0 +1,537 @@ +import { ArrowLeft } from "lucide-react"; +import { useState, useCallback, useMemo } from "react"; + +const EXT_ID = "budget-mgr-" + Math.floor(10000000 + Math.random() * 90000000); + +const PERMISSIONS = [ + "budgets:ViewBudget", + "ce:GetCostAndUsage", + "ce:GetCostForecast", + "ce:GetUsageForecast", + "organizations:ListAccounts", +]; + +const ENVIRONMENTS = ["PROD", "STAGING", "DEV", "SANDBOX"]; +const REGIONS = ["us-east-1", "us-west-2", "eu-west-1", "ap-southeast-1"]; + +// ── Types ──────────────────────────────────────────────────────────────────── +const initialForm = { + accountId: "", + alias: "", + environment: "PROD", + region: "us-east-1", + ownerEmail: "", + orgId: "", + roleArn: "", +}; + +const initialErrors = { + accountId: "", + alias: "", + ownerEmail: "", + roleArn: "", +}; + +// ── Sub-components ─────────────────────────────────────────────────────────── +function StepIndicator({ + step, + label, + status, +}) { + const numClasses = + status === "done" + ? "w-[26px] h-[26px] rounded-full flex items-center justify-center font-mono text-[0.72rem] font-semibold shrink-0 transition-all duration-[250ms] bg-emerald-500 text-black" + : status === "active" + ? "w-[26px] h-[26px] rounded-full flex items-center justify-center font-mono text-[0.72rem] font-semibold shrink-0 transition-all duration-[250ms] border-2 border-amber-600 text-amber-600 bg-transparent" + : "w-[26px] h-[26px] rounded-full flex items-center justify-center font-mono text-[0.72rem] font-semibold shrink-0 transition-all duration-[250ms] border-2 border-b border-neutral-300 text-[#6b7591]"; + + const labelClasses = + status === "done" + ? "text-xs font-medium transition-all duration-[250ms] whitespace-nowrap text-emerald-500" + : status === "active" + ? "text-xs font-medium transition-all duration-[250ms] whitespace-nowrap text-black" + : "text-xs font-medium transition-all duration-[250ms] whitespace-nowrap text-[#6b7591]"; + + return ( +
+
{status === "done" ? "✓" : step}
+ {label} +
+ ); +} + +function FormGroup({ + label, + required = false, + error = "", + children, +}) { + return ( +
+ + {children} + {error && {error}} +
+ ); +} + +function UpperText({ setVisible }) { + return ( +
+
+ +

+ Register account for budget management +

+
+ +
+ ); +} + +function Footer({ + goBack, + goNext, + nextBtnClass, + nextBtnLabel, + currentStep, + submitted, +}) { + const nextClasses = + nextBtnClass === "btn-next success" + ? "flex-[3] border-none rounded-lg text-sm font-semibold py-2.5 px-4 cursor-default flex items-center justify-center gap-1.5 transition-colors duration-150 bg-emerald-500 text-white" + : "flex-[3] border-none rounded-lg text-sm font-semibold py-2.5 px-4 cursor-pointer flex items-center justify-center gap-1.5 transition-colors duration-150 bg-amber-600 text-white hover:bg-amber-400"; + + return ( +
+ + +
+ ); +} + +// ── Step 1 ─────────────────────────────────────────────────────────────────── +function Step1({ + form, + errors, + onChange, +}) { + const inputBase = + "bg-neutral-100 border border-b border-neutral-300 rounded-[7px] text-black text-sm py-2 px-3 outline-none transition-colors duration-150 w-full appearance-none placeholder:text-[#3a4155] focus:border-amber-600"; + + return ( +
+ + onChange("accountId", e.target.value)} + placeholder="e.g. 123456789012" + maxLength={12} + autoComplete="off" + /> + + + + onChange("alias", e.target.value)} + placeholder="e.g. Production Core" + autoComplete="off" + /> + + +
+ + + + + + +
+ + + onChange("ownerEmail", e.target.value)} + placeholder="team@yourorg.com" + autoComplete="off" + /> + + + + onChange("orgId", e.target.value)} + placeholder="o-xxxxxxxxxx" + autoComplete="off" + /> + +
+ ); +} + +// ── Step 2 ─────────────────────────────────────────────────────────────────── +function Step2({ + form, + errors, + onChange, +}) { + const inputBase = + "bg-neutral-100 border border-b border-neutral-300 rounded-[7px] text-black text-sm py-2 px-3 outline-none transition-colors duration-150 w-full appearance-none placeholder:text-[#3a4155] focus:border-amber-600"; + + const trustPolicy = useMemo(() => { + const principal = form.accountId || "MGMT_ACCOUNT"; + return `{ + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::${principal}:root" + }, + "Action": "sts:AssumeRole", + "Condition": { + "StringEquals": { + "sts:ExternalId": "${EXT_ID}" + } + } + }] +}`; + }, [form.accountId]); + + return ( +
+
+
+
+

+ Cross-Account IAM Role Required +

+

+ Create an IAM role in the target account that grants Budget Manager + read access to AWS Cost Explorer, Budgets, and billing data. +

+
+
+ +
+
+ Trust Policy +
+
+ {trustPolicy} +
+
+ +
+
+ Required Permissions +
+
+ {PERMISSIONS.map((p) => ( + + {p} + + ))} +
+
+ + + onChange("roleArn", e.target.value)} + placeholder="arn:aws:iam::123456789012:role/BudgetManagerRole" + autoComplete="off" + /> + +
+ ); +} + +// ── Step 3 ─────────────────────────────────────────────────────────────────── +function Step3({ form }) { + const [testState, setTestState] = useState("idle"); + + const handleTest = () => { + if (testState !== "idle") return; + setTestState("testing"); + setTimeout(() => setTestState("success"), 1800); + }; + + const cells = [ + { label: "Account ID", value: form.accountId, mono: false }, + { label: "Alias", value: form.alias, mono: false }, + { label: "Environment", value: form.environment, mono: false }, + { label: "Region", value: form.region, mono: false }, + { label: "Owner", value: form.ownerEmail, mono: false }, + { label: "Role ARN", value: form.roleArn, mono: true }, + ]; + + const testBtnClasses = + testState === "testing" + ? "flex items-center gap-2 bg-transparent border border-blue-500 rounded-[7px] text-blue-500 text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start" + : testState === "success" + ? "flex items-center gap-2 bg-transparent border border-emerald-500 rounded-[7px] text-emerald-500 text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start" + : "flex items-center gap-2 bg-transparent border border-neutral-300 rounded-[7px] text-[#6b7591] text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start hover:border-blue-500 hover:text-blue-500"; + + return ( +
+
+
+ Summary +
+
+ {cells.map(({ label, value, mono }) => ( +
+
+ {label} +
+
+ {value || "—"} +
+
+ ))} +
+
+ + +
+ ); +} + +// ── Main Component ─────────────────────────────────────────────────────────── +export default function AWSOnboardModal() { + const [currentStep, setCurrentStep] = useState(1); + const [form, setForm] = useState(initialForm); + const [errors, setErrors] = useState(initialErrors); + const [submitted, setSubmitted] = useState(false); + const [visible, setVisible] = useState(false); + + const handleChange = useCallback((field, value) => { + setForm((prev) => ({ ...prev, [field]: value })); + setErrors((prev) => ({ ...prev, [field]: "" })); + }, []); + + const validate1 = () => { + const errs = { ...initialErrors }; + let ok = true; + + if (!form.accountId.trim()) { + errs.accountId = "AWS Account ID is required."; + ok = false; + } else if (!/^\d{8,12}$/.test(form.accountId.trim())) { + errs.accountId = "Must be a valid numeric account ID."; + ok = false; + } + + if (!form.alias.trim()) { + errs.alias = "Friendly name is required."; + ok = false; + } + + if ( + form.ownerEmail.trim() && + !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.ownerEmail.trim()) + ) { + errs.ownerEmail = "Enter a valid email address."; + ok = false; + } + + setErrors(errs); + return ok; + }; + + const validate2 = () => { + const errs = { ...initialErrors }; + let ok = true; + + if (!form.roleArn.trim()) { + errs.roleArn = "IAM Role ARN is required."; + ok = false; + } else if (!form.roleArn.trim().startsWith("arn:aws:iam::")) { + errs.roleArn = 'ARN must start with "arn:aws:iam::"'; + ok = false; + } + + setErrors(errs); + return ok; + }; + + const stepStatus = (n) => { + if (n < currentStep) return "done"; + if (n === currentStep) return "active"; + return ""; + }; + + const goNext = () => { + if (currentStep === 1 && !validate1()) return; + if (currentStep === 2 && !validate2()) return; + + if (currentStep < 3) { + setCurrentStep((s) => s + 1); + } else { + setSubmitted(true); + } + }; + + const goBack = () => { + if (currentStep > 1) setCurrentStep((s) => s - 1); + }; + + if (!visible) { + return ( +
+ +
+ ); + } + + const nextBtnClass = submitted ? "btn-next success" : "btn-next default"; + const nextBtnLabel = submitted + ? "✓ Account Added" + : currentStep === 3 + ? "✓ Add Account" + : "Continue →"; + + return ( +
+
+
+ + {/* Steps */} +
+
+ +
+ +
+ +
+ + {/* Body */} +
+ {currentStep === 1 && ( + + )} + {currentStep === 2 && ( + + )} + {currentStep === 3 && } +
+
+ +
+
+
+
+ ); +} diff --git a/apps/www/src/components/docs/js/Button.jsx b/apps/www/src/components/docs/js/Button.jsx new file mode 100644 index 0000000..1346582 --- /dev/null +++ b/apps/www/src/components/docs/js/Button.jsx @@ -0,0 +1,136 @@ +import React from "react"; +import NoiseBackground from "@/lib/NoiseBackground"; +import { cn } from "@/lib/utils"; + +const gradients = { + default: ["rgb(255,100,150)", "rgb(100,150,255)", "rgb(255,200,100)"], + emerald: ["rgb(52,211,153)", "rgb(34,211,238)", "rgb(163,230,53)"], + fire: ["rgb(251,146,60)", "rgb(239,68,68)", "rgb(217,70,239)"] +}; + +export default function NoiseBackgroundDemo({ children, className, icon, variant = "default" }) { + return ( + + + + + ); +} + + + +// Create component NoiseBackground + +// import { useEffect, useRef } from "react"; + +// function NoiseBackground({ +// children, +// containerClassName = "", +// gradientColors = ["rgb(255, 100, 150)", "rgb(100, 150, 255)", "rgb(255, 200, 100)"], +// speed = 0.003, +// noiseOpacity = 0.12, +// }) { +// const canvasRef = useRef(null); +// const angleRef = useRef(0); +// const rafRef = useRef(null); + +// useEffect(() => { +// const canvas = canvasRef.current; +// if (!canvas) return; +// const ctx = canvas.getContext("2d"); +// if (!ctx) return; + +// const resize = () => { +// canvas.width = canvas.offsetWidth; +// canvas.height = canvas.offsetHeight; +// }; +// resize(); +// const ro = new ResizeObserver(resize); +// ro.observe(canvas); + +// // Build a tiny noise texture once +// const NOISE_SIZE = 128; +// const noiseCanvas = document.createElement("canvas"); +// noiseCanvas.width = NOISE_SIZE; +// noiseCanvas.height = NOISE_SIZE; +// const nCtx = noiseCanvas.getContext("2d"); +// if (!nCtx) return; +// const imgData = nCtx.createImageData(NOISE_SIZE, NOISE_SIZE); +// for (let i = 0; i < imgData.data.length; i += 4) { +// const v = (Math.random() * 255) | 0; +// imgData.data[i] = v; +// imgData.data[i + 1] = v; +// imgData.data[i + 2] = v; +// imgData.data[i + 3] = 255; +// } +// nCtx.putImageData(imgData, 0, 0); +// const noisePattern = ctx.createPattern(noiseCanvas, "repeat"); + +// const draw = () => { +// const w = canvas.width; +// const h = canvas.height; +// if (!w || !h) { rafRef.current = requestAnimationFrame(draw); return; } + +// angleRef.current += speed; +// const t = angleRef.current; + +// // Animated conic gradient with color stops shifting over time +// const cx = w / 2 + Math.sin(t * 0.7) * w * 0.1; +// const cy = h / 2 + Math.cos(t * 0.5) * h * 0.1; + +// const grad = ctx.createConicGradient(t, cx, cy); +// const n = gradientColors.length; +// gradientColors.forEach((c, i) => { +// grad.addColorStop(i / n, c); +// }); +// grad.addColorStop(1, gradientColors[0]); + +// ctx.clearRect(0, 0, w, h); +// ctx.fillStyle = grad; +// ctx.fillRect(0, 0, w, h); + +// // Overlay noise +// ctx.globalAlpha = noiseOpacity; +// ctx.fillStyle = noisePattern || ""; +// ctx.fillRect(0, 0, w, h); +// ctx.globalAlpha = 1; + +// rafRef.current = requestAnimationFrame(draw); +// }; + +// draw(); +// return () => { +// cancelAnimationFrame(rafRef.current || 0); +// ro.disconnect(); +// }; +// }, [gradientColors, speed, noiseOpacity]); + +// return ( +//
+// +//
{children}
+//
+// ); +// } + +// export default NoiseBackground; \ No newline at end of file diff --git a/apps/www/src/components/docs/js/Chatbot.jsx b/apps/www/src/components/docs/js/Chatbot.jsx new file mode 100644 index 0000000..4fef899 --- /dev/null +++ b/apps/www/src/components/docs/js/Chatbot.jsx @@ -0,0 +1,90 @@ +import { Github, Monitor, Wand2, Paperclip, AtSign, SendHorizontal, Code, Calculator, X } from 'lucide-react' +import { motion } from 'motion/react' +import Badge from '../../../usage/Badge' +const Chatbot = () => { + return ( +
+
+
+

Chat with Command Opscli Chatbot

+

+ A conversational tool for web searches, citing sources, research, drafting, debugging, and more. +

+
+
+ + +
+
+ +
+ +
+
+ +
+ + +

Where knowledge begins

+

+ Uses multiple sources and tools to answer questions with citations. +

+ +
+ {[ + { title: 'STAY UPDATED', desc: 'Rental Prices in North American Cities.' }, + { title: 'RESEARCH', desc: 'Overview of the Solar Panel Industry.' }, + { title: 'LEARN A TOPIC', desc: 'Detailed explanation of trigonometry.' } + ].map((card, idx) => ( + + ))} +
+ +
+
+
+ + Python Runner + +
+
+ + Calculator + +
+
+ +
+
+
+ + + + +
+
+
+
+
+ ) +} + +export default Chatbot diff --git a/apps/www/src/components/docs/js/DataViewPanel.jsx b/apps/www/src/components/docs/js/DataViewPanel.jsx new file mode 100644 index 0000000..33b9d4d --- /dev/null +++ b/apps/www/src/components/docs/js/DataViewPanel.jsx @@ -0,0 +1,305 @@ +import { useState, useEffect } from "react"; +import { ChevronLeft, ChevronRight } from "lucide-react"; +import PaginationControls from "./Pagination"; + +// ── Sample Data (extended for pagination demo) ─────────────── +const SAMPLE_NODES = [ + { id: 1, name: "Auth Service", status: "healthy", region: "us-east-1", uptime: "99.98%", requests: "1.2M", latency: "12ms" }, + { id: 2, name: "API Gateway", status: "healthy", region: "eu-west-2", uptime: "99.95%", requests: "3.4M", latency: "8ms" }, +]; + +const CARDS_PER_PAGE = 6; +const ROWS_PER_PAGE = 5; + +// ── Status Config ──────────────────────────────────────────── +const STATUS = { + healthy: { label: "Healthy", dot: "bg-emerald-400", badge: "bg-emerald-50 text-emerald-700 ring-emerald-200" }, + warning: { label: "Warning", dot: "bg-amber-400", badge: "bg-amber-50 text-amber-700 ring-amber-200" }, + error: { label: "Error", dot: "bg-red-400", badge: "bg-red-50 text-red-700 ring-red-200" }, +}; + +// ── ViewToggle ─────────────────────────────────────────────── +function ViewToggle({ view, setView, nodeCount }) { + return ( +
+
+ {["cards", "table"].map((v) => ( + + ))} +
+
+ ); +} + +// ── NodeCard ───────────────────────────────────────────────── +function NodeCard({ node, index }) { + const s = STATUS[node.status]; + return ( +
+
+
+
+ + + +
+
+

{node.name}

+

{node.region}

+
+
+ + + {s.label} + +
+ +
+ {[ + { label: "Uptime", value: node.uptime }, + { label: "Requests", value: node.requests }, + { label: "Latency", value: node.latency }, + ].map(({ label, value }) => ( +
+

{label}

+

{value}

+
+ ))} +
+
+ ); +} + +// ── NodeCardView (with pagination) ─────────────────────────── +function NodeCardView({ nodes }) { + const [page, setPage] = useState(1); + const totalPages = Math.ceil(nodes.length / CARDS_PER_PAGE); + + useEffect(() => { setPage(1); }, [nodes.length]); + + const start = (page - 1) * CARDS_PER_PAGE; + const paged = nodes.slice(start, start + CARDS_PER_PAGE); + + return ( +
+
+ {paged.map((node, i) => ( + + ))} +
+
+ +
+
+ ); +} + +// ── NodeTableView (with pagination) ────────────────────────── +function NodeTableView({ nodes }) { + const [sortKey, setSortKey] = useState(null); + const [sortDir, setSortDir] = useState("asc"); + const [page, setPage] = useState(1); + + const toggleSort = (key) => { + setPage(1); + if (sortKey === key) setSortDir(d => d === "asc" ? "desc" : "asc"); + else { setSortKey(key); setSortDir("asc"); } + }; + + useEffect(() => { setPage(1); }, [nodes.length]); + + const sorted = [...nodes].sort((a, b) => { + if (!sortKey) return 0; + + const aVal = a[sortKey]; + const bVal = b[sortKey]; + + if (typeof aVal === "string" && typeof bVal === "string") { + return sortDir === "asc" + ? aVal.localeCompare(bVal) + : bVal.localeCompare(aVal); + } + + if (aVal < bVal) return sortDir === "asc" ? -1 : 1; + if (aVal > bVal) return sortDir === "asc" ? 1 : -1; + return 0; + }); + + const totalPages = Math.ceil(sorted.length / ROWS_PER_PAGE); + const start = (page - 1) * ROWS_PER_PAGE; + const paged = sorted.slice(start, start + ROWS_PER_PAGE); + + const cols = [ + { key: "name", label: "Node", sortable: true }, + { key: "status", label: "Status", sortable: true }, + { key: "region", label: "Region", sortable: true }, + { key: "uptime", label: "Uptime", sortable: false }, + { key: "requests", label: "Requests", sortable: false }, + { key: "latency", label: "Latency", sortable: false }, + ]; + + return ( +
+ + + + {cols.map(({ key, label, sortable }) => ( + + ))} + + + + + {paged.map((node) => { + const s = STATUS[node.status]; + return ( + + + + + + + + + ); + })} + + {/* Stable height: fill ghost rows on last page */} + {paged.length < ROWS_PER_PAGE && + Array.from({ length: ROWS_PER_PAGE - paged.length }).map((_, i) => ( + + {cols.map((c) => + )) + } + +
sortable && toggleSort(key)} + className={` + text-left px-5 py-3.5 text-xs font-bold text-slate-500 uppercase tracking-wider select-none + ${sortable ? "cursor-pointer hover:text-slate-800" : ""} + `} + > + + {label} + {sortable && ( + + + + )} + +
+
+
+ + + +
+ {node.name} +
+
+ + + {s.label} + + {node.region}{node.uptime}{node.requests} + 200 ? "text-red-600" + : parseFloat(node.latency) > 50 ? "text-amber-600" + : "text-emerald-600" + }`}> + {node.latency} + +
)} +
+ + {/* Footer */} +
+
+

+ {start + 1}–{Math.min(start + ROWS_PER_PAGE, sorted.length)} of {sorted.length} nodes +

+
+ {Object.entries(STATUS).map(([key, { dot, label }]) => ( + + + {nodes.filter(n => n.status === key).length} {label} + + ))} +
+
+ + +
+
+ ); +} + +// ── Main Page ──────────────────────────────────────────────── +export default function NodesPage() { + const [view, setView] = useState("cards"); + const nodes = SAMPLE_NODES; + + useEffect(() => { + if (nodes.length > 10) setView("table"); + }, [nodes.length]); + + useEffect(() => { + localStorage.setItem("nodeView", view); + }, [view]); + + useEffect(() => { + const saved = localStorage.getItem("nodeView"); + if (saved) setView(saved); + }, []); + + return ( +
+
+ + {view === "cards" ? ( + + ) : ( + + )} +
+
+ ); +} \ No newline at end of file diff --git a/apps/www/src/components/docs/js/Filterchip.jsx b/apps/www/src/components/docs/js/Filterchip.jsx new file mode 100644 index 0000000..2bdc867 --- /dev/null +++ b/apps/www/src/components/docs/js/Filterchip.jsx @@ -0,0 +1,33 @@ +import React from "react"; +import { motion } from "framer-motion"; +import { X } from "lucide-react"; + +export const FilterChip = ({ filterOptions, filters, removeValue }) => { + return ( +
+ {filterOptions.map(group => + filters[group.key].map((val) => { + const opt = group.options.find((o) => o.value === val); + return ( + + {group.group}: + {opt?.label || val} + + + ); + }) + )} +
+ ); +}; \ No newline at end of file diff --git a/apps/www/src/components/docs/js/Filters.jsx b/apps/www/src/components/docs/js/Filters.jsx new file mode 100644 index 0000000..12e65a0 --- /dev/null +++ b/apps/www/src/components/docs/js/Filters.jsx @@ -0,0 +1,296 @@ +import React, { useEffect, useState } from 'react' +import Header from './Header'; +import { Link, useSearchParams } from 'react-router-dom'; +import Upgradefilter from './MultipleFilter'; +import PaginationControls from './Pagination'; +import Skeleton from './Skeleton'; +import { RotateCcw, X } from 'lucide-react'; +import { FilterChip } from './Filterchip'; + + +export const Upgradecluster = () => { + const [accounts, setAccounts] = useState([]); + const [filteredData, setFilteredData] = useState([]); + const [selectedAccount, setSelectedAccount] = useState(""); + const [loading, setLoading] = useState(false); + const [filters, setFilters] = useState({ + details: [], + version: [], + cloud: [], + }); + const [expandedId, setExpandedId] = useState(null); + const [searchParams] = useSearchParams(); + const [currentPage, setCurrentPage] = useState(1); + + const accountIdParam = searchParams.get("account_id"); + const rowsPerPage = 10; + + useEffect(() => { + const fetchData = async () => { + setLoading(true); + // try { + // const data = await apiRequest(API_ENDPOINTS.getEKSClusters, "GET"); + // const accountsArray = Array.isArray(data) ? data : []; + + // setAccounts(accountsArray); + + // if (accountIdParam) { + // setSelectedAccount(accountIdParam); + // const filtered = accountsArray.filter( + // (c) => String(c.id) === String(accountIdParam) + // ); + // setFilteredData(filtered); + // } else { + // setFilteredData(accountsArray); + // } + // } catch (error) { + // console.error("Error fetching clusters:", error); + // setAccounts([]); + // setFilteredData([]); + // setCurrentPage(1); + // } finally { + // setLoading(false); + // } + }; + + fetchData(); + }, [accountIdParam]); + + useEffect(() => { + let filtered = [...accounts]; + + if (filters.details.length > 0) { + filtered = filtered.filter((item) => + item.lifecycle?.status && filters.details?.includes(item.lifecycle?.status?.toLowerCase()) + ); + } + if (filters.version.length > 0) { + filtered = filtered.filter((item) => + item.current_version && filters.version?.includes(item.current_version?.toLowerCase()) + ); + } + if (filters.cloud.length > 0) { + filtered = filtered.filter((item) => + item.provider && filters.cloud?.includes(item.provider?.toLowerCase()) + ); + } + setFilteredData(filtered); + setCurrentPage(1); + }, [filters, accounts]); + + const handleReset = () => { + setFilters({ + details: [], + version: [], + cloud: [], + }); + setSelectedAccount(""); + setFilteredData(accounts); + setCurrentPage(1); + }; + + + const totalPages = Math.ceil(filteredData.length / rowsPerPage); + const paginatedData = filteredData.slice( + (currentPage - 1) * rowsPerPage, + currentPage * rowsPerPage + ); + + + + const filterOptions = React.useMemo(() => { + const uniqueDetails = [...new Set(accounts.map(item => item?.lifecycle?.status?.toLowerCase()).filter(Boolean))]; + const uniqueVersions = [...new Set(accounts.map(item => item?.current_version?.toLowerCase()).filter(Boolean))]; + const uniqueClouds = [...new Set(accounts.map(item => item?.provider?.toLowerCase()).filter(Boolean))]; + + return [ + { + group: "Details", + key: "details", + // method for fetching data from api + + // options: uniqueDetails.map((status: any) => ({ + // label: status.charAt(0).toUpperCase() + status.slice(1) || "N/A", + // value: status + // })) + + // dummy data + options: [ + { label: "Active", value: "active" }, + { label: "Inactive", value: "inactive" }, + { label: "Pending", value: "pending" }, + { label: "Unknown", value: "unknown" }, + ] + + }, + { + group: "Version", + key: "version", + // method for fetching data from api + + // options: uniqueVersions.map(version => ({ + // label: `v${version}`, + // value: version + // })) + + // dummy data + options: [ + { label: "v1.35", value: "v1.35" }, + { label: "v1.34", value: "v1.34" }, + { label: "v1.33", value: "v1.33" }, + { label: "v1.32", value: "v1.32" }, + ] + }, + { + group: "Cloud", + key: "cloud", + + // method for fetching data from api + + // options: uniqueClouds.map(cloud => ({ + // label: cloud.toUpperCase(), + // value: cloud + // })) + + options: [ + { label: "AWS", value: "aws" }, + { label: "GCP", value: "gcp" }, + { label: "Azure", value: "azure" }, + ] + } + ]; + }, [accounts]); + + + + const removeValue = (groupKey, value) => { + setFilters(prev => ({ + ...prev, + [groupKey]: prev[groupKey].filter((v) => v !== value) + })); + }; + + return ( +
+
+
+
+
+ +
+
+ + +
+
+ +
+ {loading ? ( + [...Array(5)].map((_, index) => ( +
+
+
+ + +
+
+ + +
+
+
+ )) + ) : paginatedData.length > 0 ? ( + paginatedData.map((items, id) => ( + +
+
setExpandedId(String(expandedId) === String(items.id) ? null : items.id)} + className='flex flex-col gap-2 bg-neutral-50 p-3 shadow-sm rounded-md cursor-pointer hover:bg-neutral-100 transition-colors group' + > +
+ +

+ {items.name} +

+ + + {items.status} + +
+
+
+ Version: + + {items.current_version || 'N/A'} + +
+
+ Standard Support End: + + {items.lifecycle?.end_of_standard_support + ? new Date(items.lifecycle.end_of_standard_support).toLocaleDateString() + : "—"} + +
+
+
+ {String(expandedId) === String(items.id) && ( +
+

Previous Builds

+ {items.prevbuilds && items.prevbuilds.length > 0 ? ( +
+ {items.prevbuilds.map((build, idx) => ( +
+ {build.id || `Build #${idx + 1}`} + {build.date || 'No Date'} + + {build.status || 'Unknown'} + +
+ ))} +
+ ) : ( +
No previous builds found for this cluster.
+ )} +
+ )} +
+
+ )) + ) : ( +
+ No clusters found matching your criteria. +
+ )} +
+
+ +
+
+
+
+ ); +}; diff --git a/apps/www/src/components/docs/js/Header.jsx b/apps/www/src/components/docs/js/Header.jsx new file mode 100644 index 0000000..3dd024c --- /dev/null +++ b/apps/www/src/components/docs/js/Header.jsx @@ -0,0 +1,24 @@ +import { motion } from "framer-motion"; +import React from "react"; + +const Header = ({ page, subPage, action }) => { + return ( + + {page && ( +
+

{page}

+ {subPage && {subPage}} +
+ )} +
+ {action} +
+
+ ) +} + +export default Header diff --git a/apps/www/src/components/docs/js/MultipleFilter.jsx b/apps/www/src/components/docs/js/MultipleFilter.jsx new file mode 100644 index 0000000..78b6219 --- /dev/null +++ b/apps/www/src/components/docs/js/MultipleFilter.jsx @@ -0,0 +1,89 @@ +import React, { useEffect, useRef, useState } from "react"; +import { ChevronDown, Check, X } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const Upgradefilter = ({ + filterOptions, + filters, + onFilterChange, + defaultLabel = "Filter", +}) => { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(null); + + const totalSelected = Object.values(filters).flat().length; + + useEffect(() => { + const handleClickOutside = (e) => { + if (dropdownRef.current && !dropdownRef.current?.contains(e.target)) { + setIsOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => + document.removeEventListener("mousedown", handleClickOutside); + }, []); + + const toggleValue = (groupKey, value) => { + onFilterChange((prev) => { + const exists = prev[groupKey].includes(value); + return { + ...prev, + [groupKey]: exists + ? prev[groupKey].filter((v) => v !== value) + : [...prev[groupKey], value], + }; + }); + }; + + return ( +
+ + + {isOpen && ( +
+ {filterOptions.map((group) => ( +
+

+ {group.group} +

+ {group.options.map((opt) => { + const isSelected = filters[group.key].includes(opt.value); + return ( + + ); + })} +
+ ))} +
+ )} +
+ ); +}; + +export default Upgradefilter \ No newline at end of file diff --git a/apps/www/src/components/docs/js/Navbar.jsx b/apps/www/src/components/docs/js/Navbar.jsx new file mode 100644 index 0000000..39a9aa8 --- /dev/null +++ b/apps/www/src/components/docs/js/Navbar.jsx @@ -0,0 +1,67 @@ +import React, { useLayoutEffect, useRef, useState } from "react" +import { motion } from "motion/react" +import { cn } from "@workspace/ui/lib/utils" +import { Link } from "react-router-dom" + +const NavbarLinks = ({ items = [], className, initialActive = "home" }) => { + const [active, setActive] = useState(initialActive ?? items?.[0]?.id ?? "") + const itemRefs = useRef({}) + const containerRef = useRef(null) + const [indicator, setIndicator] = useState({ width: 0, left: 0 }) + + useLayoutEffect(() => { + const el = itemRefs.current[active] + const container = containerRef.current + + if (el && container) { + const elRect = el.getBoundingClientRect() + const parentRect = container.getBoundingClientRect() + + setIndicator({ + width: elRect.width, + left: elRect.left - parentRect.left, + }) + } else { + setIndicator({ width: 0, left: 0 }) + } + }, [active]) + + return ( +
+
+ + + {items.map((item) => ( +
{ itemRefs.current[item.id] = el; }} + onClick={() => setActive(item.id)} + className={cn( + "relative flex items-center gap-2 z-10 cursor-pointer px-3 py-1 text-sm font-medium select-none transition-colors", + active === item.id ? "text-neutral-300" : "text-gray-400" + )} + > + {item.icon && {item.icon}} + {item.href ? ( + + {item.title} + + ) : ( + {item.title} + )} +
+ ))} +
+
+ ) +} + +export default NavbarLinks diff --git a/apps/www/src/components/docs/js/Pagination.jsx b/apps/www/src/components/docs/js/Pagination.jsx new file mode 100644 index 0000000..fa914f7 --- /dev/null +++ b/apps/www/src/components/docs/js/Pagination.jsx @@ -0,0 +1,40 @@ +import { ChevronLeft, ChevronRight } from "lucide-react"; +import React from "react"; + +const PaginationControls = ({ currentPage, totalPages = 6, onPageChange }) => { + if (!totalPages || totalPages <= 0) return null; + + return ( +
+ + +
+ + {currentPage} + + of + + {totalPages} + +
+ + +
+ ); +}; + +export default PaginationControls; diff --git a/apps/www/src/components/docs/js/Sidebar.jsx b/apps/www/src/components/docs/js/Sidebar.jsx new file mode 100644 index 0000000..b77ce12 --- /dev/null +++ b/apps/www/src/components/docs/js/Sidebar.jsx @@ -0,0 +1,272 @@ +import React from "react" +import { cn } from "@workspace/ui/lib/utils" +import { ScrollArea } from "@workspace/ui/components/ui/scroll-area" +import { motion, AnimatePresence } from "framer-motion" +import { Link, useLocation } from "react-router-dom" +import { PanelLeftClose, PanelRightClose } from "lucide-react"; +import { IconFile, IconFolders, IconStarSparkle } from "nucleo-glass" + +export const docsConfig = { + codeTheme: (process.env.CODE_THEME) || "nord", + sidebarNav: [ + { + title: "Getting Started", + items: [ + { + title: "Introduction", + href: "/docs/introduction", + items: [] + }, + { + title: "Installation", + href: "/docs/installation", + items: [], + }, + { + title: "Style", + href: "/docs/style/style", + items: [], + icon: + } + ], + icon: + }, + { + title: "Components", + items: [ + { + title: "Addaccount", + href: "/docs/components/addaccount", + items: [], + }, + { + title: "Badge", + href: "/docs/components/badge", + items: [], + }, + { + title: "Button", + href: "/docs/components/button", + items: [], + }, + { + title: "Chatbot", + href: "/docs/components/chatbot", + items: [], + }, + { + title: "Filterchip", + href: "/docs/components/filterchip", + items: [], + }, + { + title: "Filters", + href: "/docs/components/filters", + items: [], + }, + { + title: "Header", + href: "/docs/components/header", + items: [], + }, + { + title: "Multiple Filter", + href: "/docs/components/multiple-filter", + items: [], + }, + { + title: "Pagination", + href: "/docs/components/pagination", + items: [], + }, + { + title: "Sidebar", + href: "/docs/components/sidebar", + items: [], + }, + { + title: "Skeleton", + href: "/docs/components/skeleton", + items: [], + }, + { + title: "SpiderView", + href: "/docs/components/spiderview", + items: [], + }, + { + title: "Table", + href: "/docs/components/table", + items: [], + }, + { + title: "Timeline", + href: "/docs/components/timeline", + items: [], + }, + { + title: "Toggle", + href: "/docs/components/toggle", + items: [], + } + ], + icon: + }, + ], +} + +export const Sidebar = ({ isOpen, setIsOpen }) => { + return ( + <> + + {isOpen && ( + + )} + + + {!isOpen && ( +
+ +
+ )} + + ) +} + +export function DocsSidebarNav({ items, className, setIsOpen }) { + const { pathname } = useLocation(); + return items.length ? ( +
+
+
+
+ + + Opsdocs + +
+ setIsOpen(false)} + size={16} + className="cursor-pointer text-neutral-500 transition-colors" + /> +
+
+ {items.map((section, index) => ( +
+

+ {section.title} +

+ +
+ ))} +
+
+
+ ) : null +} + + +export function DocsSidebarNavItems({ + items, + pathname, +}) { + return items?.length ? ( +
+ {items.map((item, index) => ( + + ))} +
+ ) : null +} + + +function NavItem({ item, pathname, className }) { + const isActive = pathname === item.href + const hasChildren = item.items && item.items.length > 0 + + if (hasChildren) { + return ( +
+ + {item.title} + +
+ +
+
+ ) + } + + if (item.href && !item.disabled) { + return ( + +

{item.title}

+ {item.label === "new" && ( + + {item.label} + + )} + {item.label === "recent" && ( + + {item.label} + + )} + {item.label === "updated" && ( + + {item.label} + + )} + + ) + } + + return ( + + {item.title} + {item.label && ( + + {item.label} + + )} + + ) +} diff --git a/apps/www/src/components/docs/Skeleton.tsx b/apps/www/src/components/docs/js/Skeleton.jsx similarity index 81% rename from apps/www/src/components/docs/Skeleton.tsx rename to apps/www/src/components/docs/js/Skeleton.jsx index 0e2798f..3da597a 100644 --- a/apps/www/src/components/docs/Skeleton.tsx +++ b/apps/www/src/components/docs/js/Skeleton.jsx @@ -1,18 +1,11 @@ import React from 'react' -interface SkeletonProps { - variant?: "text" | "rectangular" | "circular", - width?: string | number, - height?: string | number, - className?: string -} - const Skeleton = ({ variant = "text", width = '100%', height = '16px', className = '' -}: SkeletonProps) => { +}) => { const baseStyles = 'bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 bg-[length:200%_100%] animate-pulse' const variantStyles = { diff --git a/apps/www/src/components/docs/SpiderView.tsx b/apps/www/src/components/docs/js/SpiderView.jsx similarity index 100% rename from apps/www/src/components/docs/SpiderView.tsx rename to apps/www/src/components/docs/js/SpiderView.jsx diff --git a/apps/www/src/components/docs/js/Table.jsx b/apps/www/src/components/docs/js/Table.jsx new file mode 100644 index 0000000..7f27fca --- /dev/null +++ b/apps/www/src/components/docs/js/Table.jsx @@ -0,0 +1,266 @@ +import { ArrowDown, ArrowUp, ArrowUpFromLine, ChevronDown, ChevronRight, Loader } from "lucide-react"; +import React from "react"; +import { NavLink } from "react-router-dom"; +import { cn } from "@/lib/utils"; +import Skeleton from "./Skeleton"; + + +export default function Table({ + columns = [], + data = [], + isLoading = false, + actions = [], + sortConfig = { key: null, direction: null }, + onSort, + emptyState, + className, + expandable = false, + renderExpandedContent, + rowKey = (row, index) => row.id || index +}) { + const [expandedRows, setExpandedRows] = React.useState(new Set()); + + const toggleRow = (key, e) => { + e.stopPropagation(); + const newExpandedRows = new Set(expandedRows); + if (newExpandedRows.has(key)) { + newExpandedRows.delete(key); + } else { + newExpandedRows.add(key); + } + setExpandedRows(newExpandedRows); + }; + + const handleSort = (key) => { + if (onSort) { + onSort(key); + } + }; + + return ( +
+
+ + + + {expandable && ( + + ))} + {actions.length > 0 && ( + + )} + + + + + {isLoading ? ( + [...Array(5)].map((_, index) => ( + + {columns.map((col, colIndex) => ( + + ))} + {actions.length > 0 && ( + + )} + + )) + ) : data.length > 0 ? ( + data.map((row, rowIndex) => { + const key = rowKey(row, rowIndex); + const isExpanded = expandedRows.has(key); + return ( + + + {expandable && ( + + )} + {columns.map((col) => { + const cellValue = row[col.key]; + let displayValue = cellValue; + + + if (col.key === 'created_at' && cellValue) { + displayValue = cellValue.length > 10 ? cellValue.slice(0, 4) : cellValue; + } + + if (col.render) { + displayValue = col.render(cellValue, row); + } + + if (col.key === 'provider' && cellValue) { + const val = String(cellValue).toLowerCase(); + if (val.includes('eks') || val.includes('aws')) { + displayValue = ( + + AWS + + ); + } else if (val.includes('azure')) { + displayValue = ( + + Azure + + ); + } else { + displayValue = ( + + {cellValue} + + ); + } + } + + if (displayValue === undefined || displayValue === null || displayValue === "") { + displayValue = "-"; + } + + return ( + + ); + })} + {actions.length > 0 && ( + + )} + + {isExpanded && renderExpandedContent && ( + + + + )} + + ); + }) + ) : ( + + emptyState && ( + + + + ) + + )} + +
+ )} + {columns.map((col) => ( + +
col.sortable && handleSort(col.key)} + > + {col.label} + {col.sortable && ( + + {sortConfig.key === col.key ? ( + sortConfig.direction === "asc" ? ( + + ) : ( + + ) + ) : ( +
+ )} + + )} +
+
+ Actions +
+ + + +
+ + + {displayValue} + +
+ {actions.map((action, actionIndex) => { + const isDisabled = action.disabled ? action.disabled(row) : false; + const ActionIcon = action.icon; + const linkPath = typeof action.link === 'function' ? action.link(row) : action.link; + const tooltipTitle = typeof action.tooltip === 'function' ? action.tooltip(row) : (action.tooltip || action.label || ''); + + const ButtonContent = ( +
{ + e.stopPropagation(); + action.onClick?.(row); + }} + className={cn( + "p-1.5 transition-all duration-200 hover:bg-white/60", + isDisabled && "opacity-40 cursor-not-allowed", + action.color === "error" && !isDisabled && "text-red-500 hover:text-red-700 hover:bg-red-50", + action.color === "success" && !isDisabled && "text-green-600 hover:text-green-800 hover:bg-green-50", + (!action.color || action.color === "default") && !isDisabled && "text-slate-600 hover:text-blue-600 hover:bg-blue-50" + )} + > + {typeof action.icon === 'function' ? ( + + ) : action.icon ? ( + + ) : ( + + )} +
+ ); + + return ( +
+ {linkPath ? ( + e.stopPropagation()}> + {ButtonContent} + + ) : ( + {ButtonContent} + )} +
+ ); + })} +
+
0 ? 1 : 0) + (expandable ? 1 : 0)} + className="px-8 py-4 bg-slate-50/50 border-y border-slate-100" + > + {renderExpandedContent(row)} +
0 ? 1 : 0) + (expandable ? 1 : 0)} + className="h-32 text-center text-slate-500" + > + {emptyState} +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/www/src/components/docs/Timeline.tsx b/apps/www/src/components/docs/js/Timeline.jsx similarity index 100% rename from apps/www/src/components/docs/Timeline.tsx rename to apps/www/src/components/docs/js/Timeline.jsx diff --git a/apps/www/src/components/docs/js/Toggle.jsx b/apps/www/src/components/docs/js/Toggle.jsx new file mode 100644 index 0000000..a2c2f0e --- /dev/null +++ b/apps/www/src/components/docs/js/Toggle.jsx @@ -0,0 +1,51 @@ +import React from "react" +import { Moon, Sun } from "lucide-react" +import { motion, AnimatePresence } from "framer-motion" +import { useTheme } from "../../theme-provider" +import { cn } from "@workspace/ui/lib/utils" + +export const Toggle = () => { + const { theme, setTheme } = useTheme() + + const toggleTheme = () => { + setTheme(theme === "dark" ? "light" : "dark") + } + + return ( + + ) +} + diff --git a/apps/www/src/components/docs/js/notification.jsx b/apps/www/src/components/docs/js/notification.jsx new file mode 100644 index 0000000..620b17f --- /dev/null +++ b/apps/www/src/components/docs/js/notification.jsx @@ -0,0 +1,164 @@ +import { AnimatePresence, motion } from "framer-motion"; +import * as React from "react"; +import { cn } from "@workspace/ui/lib/utils"; +import { LogOut } from "lucide-react"; + +export function NotificationMenu(props) { + const { + avatarSrc, + avatarAlt = "User", + userLabel, + userSubLabel, + menuItems, + workspaces = [], + onCreateWorkspace, + onLogout, + className, + open, + onOpenChange, + } = props; + + const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false); + const isControlled = open !== undefined; + const isOpen = isControlled ? open : uncontrolledOpen; + const setOpen = (next) => { + if (!isControlled) setUncontrolledOpen(next); + onOpenChange?.(next); + }; + + return ( + +
+
+ +
+ + + {isOpen && ( + +
+
+
+ +
+
+

{userLabel}

+ {userSubLabel ? ( +

{userSubLabel}

+ ) : null} +
+
+ +
+ + {(workspaces.length > 0 || onCreateWorkspace) && ( +
+
+

Switch Workspaces

+ {workspaces.map((ws) => ( +
+
+ {ws.avatar ?? ( +
+ )} +

{ws.label}

+
+
+ ))} + {onCreateWorkspace && ( + + )} +
+
+ )} + +
+ +
+ {menuItems.map((item) => ( + + ))} +
+
+
+ {onLogout && ( + + )} +
+
+ + )} + +
+ + ); +} + +export default NotificationMenu; + + diff --git a/apps/www/src/components/docs/Addaccount.tsx b/apps/www/src/components/docs/ts/Addaccount.tsx similarity index 99% rename from apps/www/src/components/docs/Addaccount.tsx rename to apps/www/src/components/docs/ts/Addaccount.tsx index 4637866..780c74a 100644 --- a/apps/www/src/components/docs/Addaccount.tsx +++ b/apps/www/src/components/docs/ts/Addaccount.tsx @@ -373,7 +373,7 @@ function Step3({ form }: { form: typeof initialForm }) { ? "flex items-center gap-2 bg-transparent border border-blue-500 rounded-[7px] text-blue-500 text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start" : testState === "success" ? "flex items-center gap-2 bg-transparent border border-emerald-500 rounded-[7px] text-emerald-500 text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start" - : "flex items-center gap-2 bg-transparent border border-neutral-300 rounded-[7px] text-[#6b7591] text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start hover:lue-500 hover:text-blue-500"; + : "flex items-center gap-2 bg-transparent border border-neutral-300 rounded-[7px] text-[#6b7591] text-[0.8rem] py-2 px-4 cursor-pointer transition-colors duration-150 self-start hover:border-blue-500 hover:text-blue-500"; return (
diff --git a/apps/www/src/components/docs/Button.tsx b/apps/www/src/components/docs/ts/Button.tsx similarity index 100% rename from apps/www/src/components/docs/Button.tsx rename to apps/www/src/components/docs/ts/Button.tsx diff --git a/apps/www/src/components/docs/Chatbot.tsx b/apps/www/src/components/docs/ts/Chatbot.tsx similarity index 99% rename from apps/www/src/components/docs/Chatbot.tsx rename to apps/www/src/components/docs/ts/Chatbot.tsx index f111ffc..4fef899 100644 --- a/apps/www/src/components/docs/Chatbot.tsx +++ b/apps/www/src/components/docs/ts/Chatbot.tsx @@ -1,6 +1,6 @@ import { Github, Monitor, Wand2, Paperclip, AtSign, SendHorizontal, Code, Calculator, X } from 'lucide-react' import { motion } from 'motion/react' -import Badge from '../../usage/Badge' +import Badge from '../../../usage/Badge' const Chatbot = () => { return (
diff --git a/apps/www/src/components/docs/DataViewPanel.tsx b/apps/www/src/components/docs/ts/DataViewPanel.tsx similarity index 93% rename from apps/www/src/components/docs/DataViewPanel.tsx rename to apps/www/src/components/docs/ts/DataViewPanel.tsx index 92f681c..dc4610a 100644 --- a/apps/www/src/components/docs/DataViewPanel.tsx +++ b/apps/www/src/components/docs/ts/DataViewPanel.tsx @@ -1,5 +1,4 @@ import { useState, useEffect } from "react"; -import { ChevronLeft, ChevronRight } from "lucide-react"; import PaginationControls from "./Pagination"; interface Node { @@ -12,7 +11,6 @@ interface Node { latency: string; } -// ── Sample Data (extended for pagination demo) ─────────────── const SAMPLE_NODES: Node[] = [ { id: 1, name: "Auth Service", status: "healthy", region: "us-east-1", uptime: "99.98%", requests: "1.2M", latency: "12ms" }, { id: 2, name: "API Gateway", status: "healthy", region: "eu-west-2", uptime: "99.95%", requests: "3.4M", latency: "8ms" }, @@ -21,14 +19,12 @@ const SAMPLE_NODES: Node[] = [ const CARDS_PER_PAGE = 6; const ROWS_PER_PAGE = 5; -// ── Status Config ──────────────────────────────────────────── const STATUS = { healthy: { label: "Healthy", dot: "bg-emerald-400", badge: "bg-emerald-50 text-emerald-700 ring-emerald-200" }, warning: { label: "Warning", dot: "bg-amber-400", badge: "bg-amber-50 text-amber-700 ring-amber-200" }, error: { label: "Error", dot: "bg-red-400", badge: "bg-red-50 text-red-700 ring-red-200" }, }; -// ── ViewToggle ─────────────────────────────────────────────── interface ViewToggleProps { view: string; setView: (view: string) => void; @@ -68,7 +64,6 @@ function ViewToggle({ view, setView, nodeCount }: ViewToggleProps) { ); } -// ── NodeCard ───────────────────────────────────────────────── interface NodeCardProps { node: Node; index: number; @@ -114,7 +109,6 @@ function NodeCard({ node, index }: NodeCardProps) { ); } -// ── NodeCardView (with pagination) ─────────────────────────── function NodeCardView({ nodes }: { nodes: Node[] }) { const [page, setPage] = useState(1); const totalPages = Math.ceil(nodes.length / CARDS_PER_PAGE); @@ -142,7 +136,6 @@ function NodeCardView({ nodes }: { nodes: Node[] }) { ); } -// ── NodeTableView (with pagination) ────────────────────────── function NodeTableView({ nodes }: { nodes: Node[] }) { const [sortKey, setSortKey] = useState(null); const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); @@ -254,7 +247,6 @@ function NodeTableView({ nodes }: { nodes: Node[] }) { ); })} - {/* Stable height: fill ghost rows on last page */} {paged.length < ROWS_PER_PAGE && Array.from({ length: ROWS_PER_PAGE - paged.length }).map((_, i) => ( @@ -265,7 +257,6 @@ function NodeTableView({ nodes }: { nodes: Node[] }) { - {/* Footer */}

diff --git a/apps/www/src/components/docs/Filterchip.tsx b/apps/www/src/components/docs/ts/Filterchip.tsx similarity index 99% rename from apps/www/src/components/docs/Filterchip.tsx rename to apps/www/src/components/docs/ts/Filterchip.tsx index 5123028..c1bb3ca 100644 --- a/apps/www/src/components/docs/Filterchip.tsx +++ b/apps/www/src/components/docs/ts/Filterchip.tsx @@ -40,4 +40,4 @@ export const FilterChip = ({ filterOptions, filters, removeValue }: FilterChipPr )}

); -}; \ No newline at end of file +}; diff --git a/apps/www/src/components/docs/Filters.tsx b/apps/www/src/components/docs/ts/Filters.tsx similarity index 99% rename from apps/www/src/components/docs/Filters.tsx rename to apps/www/src/components/docs/ts/Filters.tsx index f50fde8..006e4ae 100644 --- a/apps/www/src/components/docs/Filters.tsx +++ b/apps/www/src/components/docs/ts/Filters.tsx @@ -1,5 +1,4 @@ import React, { useEffect, useState } from 'react' -import Header from './Header'; import { Link, useSearchParams } from 'react-router-dom'; import Upgradefilter from './MultipleFilter'; import PaginationControls from './Pagination'; diff --git a/apps/www/src/components/docs/Header.tsx b/apps/www/src/components/docs/ts/Header.tsx similarity index 100% rename from apps/www/src/components/docs/Header.tsx rename to apps/www/src/components/docs/ts/Header.tsx diff --git a/apps/www/src/components/docs/MultipleFilter.tsx b/apps/www/src/components/docs/ts/MultipleFilter.tsx similarity index 100% rename from apps/www/src/components/docs/MultipleFilter.tsx rename to apps/www/src/components/docs/ts/MultipleFilter.tsx diff --git a/apps/www/src/components/docs/ts/Navbar.tsx b/apps/www/src/components/docs/ts/Navbar.tsx new file mode 100644 index 0000000..0191777 --- /dev/null +++ b/apps/www/src/components/docs/ts/Navbar.tsx @@ -0,0 +1,81 @@ +import React, { useLayoutEffect, useRef, useState } from "react" +import { motion } from "motion/react" +import { cn } from "@workspace/ui/lib/utils" +import { Link } from "react-router-dom" + + +type NavItem = { + id: string + title: string + href?: string + icon?: React.ReactNode +} + +export interface NavbarLinksProps { + items?: NavItem[] + className?: string + initialActive?: string +} + +const NavbarLinks: React.FC = ({ items = [], className, initialActive = "home" }) => { + const [active, setActive] = useState(initialActive ?? items?.[0]?.id ?? "") + const itemRefs = useRef>({}) + const containerRef = useRef(null) + const [indicator, setIndicator] = useState({ width: 0, left: 0 }) + + useLayoutEffect(() => { + const el = itemRefs.current[active] + const container = containerRef.current + + if (el && container) { + const elRect = el.getBoundingClientRect() + const parentRect = container.getBoundingClientRect() + + setIndicator({ + width: elRect.width, + left: elRect.left - parentRect.left, + }) + } else { + setIndicator({ width: 0, left: 0 }) + } + }, [active]) + + return ( +
+
+ + + {items.map((item) => ( +
{ itemRefs.current[item.id] = el; }} + onClick={() => setActive(item.id)} + className={cn( + "relative flex items-center gap-2 z-10 cursor-pointer px-3 py-1 text-sm font-medium select-none transition-colors", + active === item.id ? "text-neutral-300" : "text-gray-400" + )} + > + {item.icon && {item.icon}} + {item.href ? ( + + {item.title} + + ) : ( + {item.title} + )} +
+ ))} +
+
+ ) +} + +export default NavbarLinks diff --git a/apps/www/src/components/docs/Pagination.tsx b/apps/www/src/components/docs/ts/Pagination.tsx similarity index 100% rename from apps/www/src/components/docs/Pagination.tsx rename to apps/www/src/components/docs/ts/Pagination.tsx diff --git a/apps/www/src/components/docs/Sidebar.tsx b/apps/www/src/components/docs/ts/Sidebar.tsx similarity index 98% rename from apps/www/src/components/docs/Sidebar.tsx rename to apps/www/src/components/docs/ts/Sidebar.tsx index c32c609..842c472 100644 --- a/apps/www/src/components/docs/Sidebar.tsx +++ b/apps/www/src/components/docs/ts/Sidebar.tsx @@ -77,11 +77,6 @@ export const docsConfig: DocsConfig = { href: "/docs/components/button", items: [], }, - { - title: "Cards", - href: "/docs/components/cards", - items: [], - }, { title: "Chatbot", href: "/docs/components/chatbot", diff --git a/apps/www/src/components/docs/ts/Skeleton.tsx b/apps/www/src/components/docs/ts/Skeleton.tsx new file mode 100644 index 0000000..673bf8c --- /dev/null +++ b/apps/www/src/components/docs/ts/Skeleton.tsx @@ -0,0 +1,37 @@ +import { cn } from '@workspace/ui/lib/utils' +import React from 'react' + +interface SkeletonProps { + variant?: "text" | "rectangular" | "circular" + width?: string | number + height?: string | number + className?: string +} + +const Skeleton = ({ + variant = "text", + width = '100%', + height = '16px', + className = '' +}: SkeletonProps) => { + const variantStyles = { + text: 'rounded', + rectangular: 'rounded-md', + circular: 'rounded-full', + } + + const selectedVariant = variantStyles[variant] || variantStyles.text + const circularHeight = variant === 'circular' ? width : height + + return ( +
+ ) +} + +export default Skeleton \ No newline at end of file diff --git a/apps/www/src/components/docs/ts/SpiderView.tsx b/apps/www/src/components/docs/ts/SpiderView.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/www/src/components/docs/Table.tsx b/apps/www/src/components/docs/ts/Table.tsx similarity index 97% rename from apps/www/src/components/docs/Table.tsx rename to apps/www/src/components/docs/ts/Table.tsx index af39b72..1654e4c 100644 --- a/apps/www/src/components/docs/Table.tsx +++ b/apps/www/src/components/docs/ts/Table.tsx @@ -2,8 +2,23 @@ import { ArrowDown, ArrowUp, ArrowUpFromLine, ChevronDown, ChevronRight, Loader import React from "react"; import { NavLink } from "react-router-dom"; import { cn } from "@/lib/utils"; -import { TableProps } from "@/types/Table"; import Skeleton from "./Skeleton"; + +export interface TableProps { + columns: any[]; + data: any[]; + isLoading?: boolean; + actions?: any[]; + sortConfig?: any; + onSort?: (key: string) => void; + emptyState?: any; + onRowClick?: (row: any) => void; + className?: string; + expandable?: boolean; + renderExpandedContent?: (row: any) => React.ReactNode; + rowKey?: (row: any, index: number) => string | number; +} + export default function Table({ columns = [], data = [], diff --git a/apps/www/src/components/docs/ts/Timeline.tsx b/apps/www/src/components/docs/ts/Timeline.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/www/src/components/docs/Toggle.tsx b/apps/www/src/components/docs/ts/Toggle.tsx similarity index 97% rename from apps/www/src/components/docs/Toggle.tsx rename to apps/www/src/components/docs/ts/Toggle.tsx index 01d68a2..a2c2f0e 100644 --- a/apps/www/src/components/docs/Toggle.tsx +++ b/apps/www/src/components/docs/ts/Toggle.tsx @@ -1,7 +1,7 @@ import React from "react" import { Moon, Sun } from "lucide-react" import { motion, AnimatePresence } from "framer-motion" -import { useTheme } from "../theme-provider" +import { useTheme } from "../../theme-provider" import { cn } from "@workspace/ui/lib/utils" export const Toggle = () => { diff --git a/apps/www/src/components/docs/notification.tsx b/apps/www/src/components/docs/ts/notification.tsx similarity index 100% rename from apps/www/src/components/docs/notification.tsx rename to apps/www/src/components/docs/ts/notification.tsx diff --git a/apps/www/src/components/docspagescomponent/ComponentPreview.tsx b/apps/www/src/components/docspagescomponent/ComponentPreview.tsx index aef6015..bb9941f 100644 --- a/apps/www/src/components/docspagescomponent/ComponentPreview.tsx +++ b/apps/www/src/components/docspagescomponent/ComponentPreview.tsx @@ -2,6 +2,14 @@ import * as React from "react" import { cn } from "@workspace/ui/lib/utils" import { Tabs, TabsList, TabsTrigger } from "@workspace/ui/components/ui/tabs" +export const LanguageContext = React.createContext<{ + langType: "ts" | "js" + setLangType: (lang: "ts" | "js") => void +}>({ + langType: "ts", + setLangType: () => { }, +}) + export function ComponentPreviewTabs({ className, align = "center", @@ -18,66 +26,92 @@ export function ComponentPreviewTabs({ }) { const [tab, setTab] = React.useState("preview") + const [langType, setLangType] = React.useState<"ts" | "js">("ts") return ( -
- -
- {!hideCode && ( - - - Preview - - - Code - - - )} -
-
+
- { - tab === "preview" ? ( -
+ +
+ {!hideCode && ( + + + Preview + + + Code + + + )} + {tab === "code" && ( +
+ + +
+ )} +
+
+
+ { + tab === "preview" ? (
- {component} +
+ {component} +
-
- ) : -
- {source} -
- } + ) : +
+ {source} +
+ } + +
-
+ ) } \ No newline at end of file diff --git a/apps/www/src/components/docspagescomponent/ComponentSource.tsx b/apps/www/src/components/docspagescomponent/ComponentSource.tsx index 71918f1..2f2f562 100644 --- a/apps/www/src/components/docspagescomponent/ComponentSource.tsx +++ b/apps/www/src/components/docspagescomponent/ComponentSource.tsx @@ -2,6 +2,7 @@ import * as React from "react" import { cn } from "@workspace/ui/lib/utils" import { CodeCollapsibleWrapper } from "./CodeCollapse" import { CopyButton } from "./Copy-button" +import { LanguageContext } from "./ComponentPreview" export function ComponentSource({ name, @@ -20,19 +21,55 @@ export function ComponentSource({ const [data, setData] = React.useState<{ code: string; highlightedCode: string } | null>(null) const [error, setError] = React.useState(null) const [loading, setLoading] = React.useState(false) + const { langType } = React.useContext(LanguageContext) React.useEffect(() => { if (!src) return setLoading(true) + setError(null) const fetchSource = async () => { try { - const lang = language ?? title?.split(".").pop() ?? "tsx" - const res = await fetch(`/api/source?src=${encodeURIComponent(src)}&lang=${lang}`) + let currentSrc = src + let currentLang = language ?? title?.split(".").pop() ?? "tsx" + + if (langType === "js") { + currentSrc = src.replace("/ts/", "/js/").replace(".tsx", ".jsx") + currentLang = "jsx" + } + + const res = await fetch(`/api/source?src=${encodeURIComponent(currentSrc)}&lang=${currentLang}`) + const contentType = res.headers.get("content-type") + if (!res.ok) { - const err = await res.json() - throw new Error(err.error || "Failed to fetch source") + let errMsg = "Failed to fetch source" + if (contentType && contentType.includes("application/json")) { + try { + const err = await res.json() + errMsg = err.error || errMsg + } catch { + // Ignore and use default error msg + } + } else { + try { + const text = await res.text() + errMsg = `${res.status} ${res.statusText}${text ? `: ${text.slice(0, 100)}` : ""}` + } catch { + errMsg = `${res.status} ${res.statusText}` + } + } + throw new Error(errMsg) } + + if (!contentType || !contentType.includes("application/json")) { + try { + const text = await res.text() + throw new Error(`Expected JSON response, but received content type "${contentType}" with body: ${text.slice(0, 100)}`) + } catch { + throw new Error(`Expected JSON response but received content type "${contentType}"`) + } + } + const json = await res.json() setData(json) } catch (err: any) { @@ -44,14 +81,14 @@ export function ComponentSource({ } fetchSource() - }, [src, language, title]) + }, [src, language, title, langType]) if (!name && !src) { return null } if (loading) { - return
Loading source...
+ return
Loading source...
} if (error) { diff --git a/apps/www/src/components/docspagescomponent/Doc-toc-mobile.tsx b/apps/www/src/components/docspagescomponent/Doc-toc-mobile.tsx index ae0d626..f99a74c 100644 --- a/apps/www/src/components/docspagescomponent/Doc-toc-mobile.tsx +++ b/apps/www/src/components/docspagescomponent/Doc-toc-mobile.tsx @@ -1,50 +1,21 @@ +import React from "react" import { cn } from "@workspace/ui/lib/utils" -import { List } from "lucide-react" -import { motion } from "framer-motion" +import { TOCProvider, TOCScrollArea } from "../toc/index" +import { TOCItems } from "../toc/clerk" + interface DocsTableOfContentsProps { - toc: Array<{ - title: React.ReactNode - url: string - children?: Array<{ - title: React.ReactNode - url: string - }> - }> + toc: any[] } export function DocsTableOfContentsMobile({ toc }: DocsTableOfContentsProps) { return ( -
- +
+ + + + +
) } + diff --git a/apps/www/src/components/docspagescomponent/Doc-toc.tsx b/apps/www/src/components/docspagescomponent/Doc-toc.tsx index 218534b..950849c 100644 --- a/apps/www/src/components/docspagescomponent/Doc-toc.tsx +++ b/apps/www/src/components/docspagescomponent/Doc-toc.tsx @@ -1,54 +1,26 @@ +import React from "react" import { cn } from "@workspace/ui/lib/utils" import { List } from "lucide-react" +import { TOCProvider, TOCScrollArea } from "../toc/index" +import { TOCItems } from "../toc/clerk" interface DocsTableOfContentsProps { - toc: Array<{ - title: React.ReactNode - url: string - children?: Array<{ - title: React.ReactNode - url: string - }> - }> + toc: any[] } export function DocsTableOfContents({ toc }: DocsTableOfContentsProps) { return ( -
-

+
+

On this page

- +
+ + + +
) } + diff --git a/apps/www/src/components/home/Home.tsx b/apps/www/src/components/home/Home.tsx index c408e18..3ea86a0 100644 --- a/apps/www/src/components/home/Home.tsx +++ b/apps/www/src/components/home/Home.tsx @@ -3,7 +3,7 @@ import { Link } from 'react-router-dom' import { motion } from 'motion/react' import { buttonVariants, containerVariants, textVariants } from '../../lib/variants' import { cn } from "@/lib/utils"; -import NoiseBackgroundDemo from '../docs/Button'; +import NoiseBackgroundDemo from '../docs/ts/Button'; import { LandingImg } from './LandingImg'; const Home = () => { diff --git a/apps/www/src/components/sidebar/Sidenav.tsx b/apps/www/src/components/sidebar/Sidenav.tsx index b700e1c..ac491e0 100644 --- a/apps/www/src/components/sidebar/Sidenav.tsx +++ b/apps/www/src/components/sidebar/Sidenav.tsx @@ -25,7 +25,9 @@ interface DocsSidebarNavProps { export function DocsSidebarNav({ items, className, setIsOpen }: DocsSidebarNavProps) { const { pathname } = useLocation(); return items.length ? ( -
+
+ ) : null } @@ -99,8 +101,8 @@ function NavItem({ item, pathname, className }: NavItemProps) { className={cn( "group flex w-full items-center rounded-md border border-transparent px-2 py-1.5 transition-all duration-200", item.disabled && "cursor-not-allowed opacity-60", - isActive - ? "font-medium bg-white text-black shadow-sm ring-1 ring-black/5 dark:bg-zinc-800 dark:text-white" + isActive + ? "font-medium bg-white text-black shadow-sm ring-1 ring-black/5 dark:bg-zinc-800 dark:text-white" : "text-zinc-500 hover:text-zinc-900 hover:bg-zinc-100/80 dark:hover:bg-zinc-800/50", className )} diff --git a/apps/www/src/components/toc/clerk.tsx b/apps/www/src/components/toc/clerk.tsx index 768a89a..51f16fc 100644 --- a/apps/www/src/components/toc/clerk.tsx +++ b/apps/www/src/components/toc/clerk.tsx @@ -85,7 +85,7 @@ export function TOCItems({ ref, className, ...props }: ComponentProps<'div'>) { >

)} @@ -132,12 +132,13 @@ function TOCItem({ style={{ paddingInlineStart: getItemOffset(item.depth), }} - className="prose relative py-1.5 text-sm text-fd-muted-foreground hover:text-fd-accent-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-fd-primary" + className="prose relative py-1.5 text-sm text-fd-muted-foreground hover:text-fd-accent-foreground transition-colors wrap-anywhere first:pt-0 last:pb-0 data-[active=true]:text-purple-900" > {offset !== upperOffset && ( { diff --git a/apps/www/src/components/usage/Header.tsx b/apps/www/src/components/usage/Header.tsx index 9fcf414..0457f74 100644 --- a/apps/www/src/components/usage/Header.tsx +++ b/apps/www/src/components/usage/Header.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Header from '../docs/Header'; +import Header from '../docs/ts/Header'; import { Button } from '@workspace/ui/components/ui/button'; import { Plus } from 'lucide-react'; diff --git a/apps/www/src/components/usage/MultipleFilter.tsx b/apps/www/src/components/usage/MultipleFilter.tsx index 35f3716..61c45d8 100644 --- a/apps/www/src/components/usage/MultipleFilter.tsx +++ b/apps/www/src/components/usage/MultipleFilter.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import Upgradefilter from '../docs/MultipleFilter'; +import Upgradefilter from '../docs/ts/MultipleFilter'; const MultipleFilterUsage = () => { const [filters, setFilters] = useState({ diff --git a/apps/www/src/components/usage/Pagination.tsx b/apps/www/src/components/usage/Pagination.tsx index 7787b85..30418b1 100644 --- a/apps/www/src/components/usage/Pagination.tsx +++ b/apps/www/src/components/usage/Pagination.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import PaginationControls from '../docs/Pagination'; +import PaginationControls from '../docs/ts/Pagination'; const PaginationUsage = () => { const [currentPage, setCurrentPage] = useState(1); diff --git a/apps/www/src/components/usage/Sidebar.tsx b/apps/www/src/components/usage/Sidebar.tsx index 769533c..b88d6af 100644 --- a/apps/www/src/components/usage/Sidebar.tsx +++ b/apps/www/src/components/usage/Sidebar.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Sidebar } from '../docs/Sidebar'; +import { Sidebar } from '../docs/ts/Sidebar'; import { MemoryRouter } from 'react-router-dom'; const SidebarUsage = () => { diff --git a/apps/www/src/components/usage/Toggle.tsx b/apps/www/src/components/usage/Toggle.tsx index 1ad6a5a..3713f78 100644 --- a/apps/www/src/components/usage/Toggle.tsx +++ b/apps/www/src/components/usage/Toggle.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Toggle } from '../docs/Toggle'; +import { Toggle } from '../docs/ts/Toggle'; const ToggleUsage = () => { return ( diff --git a/apps/www/src/config/docs.tsx b/apps/www/src/config/docs.tsx index f4455cd..2b6a2f8 100644 --- a/apps/www/src/config/docs.tsx +++ b/apps/www/src/config/docs.tsx @@ -86,11 +86,11 @@ export const docsConfig: DocsConfig = { href: "/docs/components/sidebar", items: [], }, - { - title: "DataViewPanel", - href: "/docs/components/dataviewpanel", - items: [], - }, + // { + // title: "DataViewPanel", + // href: "/docs/components/dataviewpanel", + // items: [], + // }, ], icon: }, diff --git a/apps/www/src/content/docs/components/addaccount.mdx b/apps/www/src/content/docs/components/addaccount.mdx index fe23640..1db7aeb 100644 --- a/apps/www/src/content/docs/components/addaccount.mdx +++ b/apps/www/src/content/docs/components/addaccount.mdx @@ -22,7 +22,7 @@ The Addaccount component provides a structured, multi-step process for registeri component={} source={ diff --git a/apps/www/src/content/docs/components/button.mdx b/apps/www/src/content/docs/components/button.mdx index 1620f51..a30e555 100644 --- a/apps/www/src/content/docs/components/button.mdx +++ b/apps/www/src/content/docs/components/button.mdx @@ -22,7 +22,7 @@ The Button component is designed for maximum visual impact. It utilizes a custom component={} source={ diff --git a/apps/www/src/content/docs/components/cards.mdx b/apps/www/src/content/docs/components/cards.mdx deleted file mode 100644 index 83d1781..0000000 --- a/apps/www/src/content/docs/components/cards.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Cards -description: A collection of data display cards used for dashboards and metric overviews, featuring glassmorphism styles and interactive animations. ---- - -import { Callout } from "@/components/docspagescomponent/Callout"; -import { ComponentPreviewTabs } from "@/components/docspagescomponent/ComponentPreview"; -import { ComponentSource } from "@/components/docspagescomponent/ComponentSource"; -import CardsUsage from "@/usage/Cards"; -import { InstallationCli } from "../../../components/docspagescomponent/cli-install-button" - -## Overview - -The Card component is a visually appealing data display element designed for dashboard metrics, status summaries, and highlighted information. It features a modern glassmorphism effect, customizable icons, and hover animations with gradient glows. - -## Installation - - - -## Usage - -} - source={ - - } - className="lg:w-[55vw] w-[90vw]" - align="center" -/> - -## Props - -### CardDetails Interface - -```typescript -interface CardDetails { - name: string; // Header label for the card - icon: React.ReactNode; // Icon element displayed in the top right - number: string; // Main metric/value text - sidenumber?: string; // Sub-metric or percentage change text - info?: string; // Description text at the bottom - className?: string; // Additional CSS classes for the container - textcolor?: string; // Text color class for the info text -} -``` - -## Features - - - Key features of the Card component: - - **Glassmorphism Design**: Semi-transparent background with blur effects. - - **Responsive**: Adapts perfectly to grid layouts for dashboards. - - **Dynamic Visuals**: Animated background glow on hover for a premium feel. - - **Flexible Information**: Supports multiple data points including main value, sub-values, and info text. - - **Icon Support**: Easily integrate any icon library like Lucide React. - - -## Examples - -### Dashboard Metric - -```jsx -import Card from "@/components/docs/Cards"; -import { Home } from "lucide-react"; - -} - number="$45,231.89" - sidenumber="+20.1%" - info="from last month" - textcolor="text-green-500" -/> -``` - -### Alert Card - -```jsx -} - number="Critical" - info="3 server nodes are currently down" - className="border-red-500/50 bg-red-500/10" - textcolor="text-red-600" -/> -``` - -## Best Practices - -1. **Grid Layout**: Use cards within a CSS Grid (e.g., `grid grid-cols-1 md:grid-cols-4`) for consistent dashboard layouts. -2. **Icon Consistency**: Use consistent icon sizes (usually `w-4 h-4`) across all cards in a group. -3. **Color Semantic**: Use `textcolor` and `className` to convey status color-coding (e.g., green for success, red for alerts). -4. **Information Density**: Keep text short and focused to maintain readability across different screen sizes. - -## Related Components - -- [Table](/docs/components/table) - Detailed list representation -- [Badge](/docs/components/badge) - Status indicators diff --git a/apps/www/src/content/docs/components/chatbot.mdx b/apps/www/src/content/docs/components/chatbot.mdx index c4b7ec2..042ca64 100644 --- a/apps/www/src/content/docs/components/chatbot.mdx +++ b/apps/www/src/content/docs/components/chatbot.mdx @@ -27,7 +27,7 @@ The Chatbot component provides a conversational interface for users to interact component={} source={ diff --git a/apps/www/src/content/docs/components/dataviewpanel.mdx b/apps/www/src/content/docs/components/dataviewpanel.mdx index c228772..c01af98 100644 --- a/apps/www/src/content/docs/components/dataviewpanel.mdx +++ b/apps/www/src/content/docs/components/dataviewpanel.mdx @@ -22,7 +22,7 @@ The `DataViewPanel` component provides a robust interface for managing and viewi component={} source={ diff --git a/apps/www/src/content/docs/components/filterchip.mdx b/apps/www/src/content/docs/components/filterchip.mdx index cf8bdf1..48a386e 100644 --- a/apps/www/src/content/docs/components/filterchip.mdx +++ b/apps/www/src/content/docs/components/filterchip.mdx @@ -23,7 +23,7 @@ The FilterChip component provides a compact, visual representation of active fil component={} source={ @@ -36,6 +36,7 @@ The FilterChip component provides a compact, visual representation of active fil ### Filterchip Interface +
```typescript interface Filterchip { filterOptions: { @@ -47,6 +48,7 @@ interface Filterchip { removeValue: (groupKey: string, value: string) => void; // Handler for removing a filter } ``` +
## Features diff --git a/apps/www/src/content/docs/components/filters.mdx b/apps/www/src/content/docs/components/filters.mdx index 56e117d..a4759e1 100644 --- a/apps/www/src/content/docs/components/filters.mdx +++ b/apps/www/src/content/docs/components/filters.mdx @@ -23,7 +23,7 @@ The Filters component (internal name `Upgradecluster`) is a high-level orchestra component={} source={ diff --git a/apps/www/src/content/docs/components/header.mdx b/apps/www/src/content/docs/components/header.mdx index cf08fdb..ceb6d52 100644 --- a/apps/www/src/content/docs/components/header.mdx +++ b/apps/www/src/content/docs/components/header.mdx @@ -23,7 +23,7 @@ The Header component provides consistent page-anchored branding and navigation i component={} source={ @@ -36,6 +36,7 @@ The Header component provides consistent page-anchored branding and navigation i ### HeaderProps Interface +
```typescript interface HeaderProps { page: string; // Main title of the current page @@ -43,6 +44,7 @@ interface HeaderProps { action?: React.ReactNode; // Slot for action buttons or components in the top-right } ``` +
## Features @@ -58,15 +60,18 @@ interface HeaderProps { ### Dashboard Header +
```jsx
``` +
### Action-Oriented Header +
```jsx
Create Cluster} /> ``` +
## Best Practices diff --git a/apps/www/src/content/docs/components/multiple-filter.mdx b/apps/www/src/content/docs/components/multiple-filter.mdx index e3a176f..f39ceac 100644 --- a/apps/www/src/content/docs/components/multiple-filter.mdx +++ b/apps/www/src/content/docs/components/multiple-filter.mdx @@ -23,7 +23,7 @@ The MultipleFilter (internal name `Upgradefilter`) is a specialized dropdown com component={} source={ @@ -36,6 +36,7 @@ The MultipleFilter (internal name `Upgradefilter`) is a specialized dropdown com ### UpgradefilterDetails Interface +
```typescript interface UpgradefilterDetails { filterOptions: { @@ -51,6 +52,7 @@ interface UpgradefilterDetails { defaultLabel?: string; } ``` +
## Features @@ -68,6 +70,7 @@ interface UpgradefilterDetails { ### Complex Data Filtering +
```jsx const options = [ { @@ -86,6 +89,7 @@ const options = [ onFilterChange={setFilters} /> ``` +
## Best Practices diff --git a/apps/www/src/content/docs/components/pagination.mdx b/apps/www/src/content/docs/components/pagination.mdx index 4e3cc77..e895f7f 100644 --- a/apps/www/src/content/docs/components/pagination.mdx +++ b/apps/www/src/content/docs/components/pagination.mdx @@ -23,7 +23,7 @@ The Pagination component (internal name `PaginationControls`) provides a simple component={} source={ @@ -32,18 +32,6 @@ The Pagination component (internal name `PaginationControls`) provides a simple align="center" /> -## Props - -### PaginationDetails Interface - -```typescript -interface PaginationDetails { - currentPage: number; // The currently active page (1-indexed) - totalPages: number; // Total number of pages available - onPageChange: (page: number) => void; // Callback function called when a page is changed -} -``` - ## Features @@ -57,7 +45,7 @@ interface PaginationDetails { ## Examples ### Basic Pagination - +
```jsx console.log('New page:', page)} /> ``` +
## Best Practices diff --git a/apps/www/src/content/docs/components/sidebar.mdx b/apps/www/src/content/docs/components/sidebar.mdx index b326bbb..d5b9c1b 100644 --- a/apps/www/src/content/docs/components/sidebar.mdx +++ b/apps/www/src/content/docs/components/sidebar.mdx @@ -23,7 +23,7 @@ The Sidebar is the central navigational hub for the documentation site. It rende component={} source={ @@ -36,6 +36,7 @@ The Sidebar is the central navigational hub for the documentation site. It rende The sidebar is driven by the `docsConfig` object: +
```typescript export const docsConfig: DocsConfig = { sidebarNav: [ @@ -53,17 +54,20 @@ export const docsConfig: DocsConfig = { ] } ``` +
## Props ### Sidebar Component +
```typescript interface SidebarProps { isOpen: boolean; // Controls the visibility of the sidebar setIsOpen: (open: boolean) => void; // Callback for toggling the sidebar } ``` +
## Features diff --git a/apps/www/src/content/docs/components/skeleton.mdx b/apps/www/src/content/docs/components/skeleton.mdx index fa22840..8c04bb3 100644 --- a/apps/www/src/content/docs/components/skeleton.mdx +++ b/apps/www/src/content/docs/components/skeleton.mdx @@ -23,7 +23,7 @@ The Skeleton component is a versatile loading placeholder that displays animated component={} source={ @@ -36,6 +36,7 @@ The Skeleton component is a versatile loading placeholder that displays animated ### SkeletonDetails Interface +
```typescript interface SkeletonDetails { variant?: "text" | "rectangular" | "circular"; @@ -44,6 +45,7 @@ interface SkeletonDetails { className?: string; } ``` +
### Props Details @@ -60,6 +62,7 @@ interface SkeletonDetails { Used for loading text content like titles, descriptions, and paragraphs. +
```jsx
{/* Title */} @@ -67,11 +70,13 @@ Used for loading text content like titles, descriptions, and paragraphs. {/* Paragraph */}
``` +
### Rectangular Skeleton Ideal for loading card components, images, and block-level content. +
```jsx
``` +
### Circular Skeleton Perfect for loading user avatars and profile images. +
```jsx
@@ -98,9 +105,11 @@ Perfect for loading user avatars and profile images.
``` +
### Loading Card Component +
```jsx import Skeleton from "@/components/docs/Skeleton"; import { useState, useEffect } from "react"; @@ -151,9 +160,11 @@ export default function CardLoader() { ); } ``` +
### Loading User List +
```jsx import Skeleton from "@/components/docs/Skeleton"; import { useState, useEffect } from "react"; @@ -205,9 +216,11 @@ export default function UserListLoader() { ); } ``` +
### Complex Loading Layout +
```jsx import Skeleton from "@/components/docs/Skeleton"; @@ -257,9 +270,11 @@ export default function ComplexLoader() { ); } ``` +
### Grid of Skeletons +
```jsx import Skeleton from "@/components/docs/Skeleton"; @@ -290,11 +305,13 @@ export default function GridLoader() { ); } ``` +
## Styling ### With Tailwind Classes +
```jsx ``` +
### Custom Styling +
```jsx ``` +
## Features @@ -332,25 +352,33 @@ export default function GridLoader() { 1. **Match Content Dimensions**: Use skeleton dimensions that match the actual content size for smooth transitions. +
```jsx // Good: Skeleton matches final content size {/* Matches h2 size */} - +``` +
+
+```jsx // Avoid: Mismatched dimensions {/* Too small */} ``` +
2. **Use Loading State Duration**: Keep loading states visible for at least 300-500ms for better UX. +
```jsx useEffect(() => { const timer = setTimeout(() => setIsLoading(false), Math.max(500, loadTime)); return () => clearTimeout(timer); }, [loadTime]); ``` +
3. **Combine Multiple Skeletons**: Use multiple skeletons to represent complex layouts. +
```jsx
{/* Title */} @@ -359,9 +387,11 @@ useEffect(() => { {/* Image */}
``` +
4. **Responsive Layouts**: Adapt skeletons to different screen sizes. +
```jsx
{Array.from({ length: 6 }).map((_, idx) => ( @@ -369,9 +399,11 @@ useEffect(() => { ))}
``` +
5. **Accessibility**: Provide loading text for screen readers. +
```jsx
{isLoading ? ( @@ -384,6 +416,7 @@ useEffect(() => { )}
``` +
## Performance Tips diff --git a/apps/www/src/content/docs/components/spiderview.mdx b/apps/www/src/content/docs/components/spiderview.mdx index b96aa1b..02c6e95 100644 --- a/apps/www/src/content/docs/components/spiderview.mdx +++ b/apps/www/src/content/docs/components/spiderview.mdx @@ -27,7 +27,7 @@ The SpiderView component is ideal for comparing multiple quantitative variables. component={} source={ diff --git a/apps/www/src/content/docs/components/table.mdx b/apps/www/src/content/docs/components/table.mdx index b5c9398..a01aebd 100644 --- a/apps/www/src/content/docs/components/table.mdx +++ b/apps/www/src/content/docs/components/table.mdx @@ -23,7 +23,7 @@ The Table component is a powerful data display component that provides features component={} source={ @@ -39,6 +39,7 @@ The Table component is a powerful data display component that provides features The Table component accepts the following props: +
```typescript interface TableDetails { columns?: ColumnConfig[]; @@ -58,11 +59,13 @@ interface TableDetails { rowKey?: (row: any, index: number) => string | number; } ``` +
### Column Configuration Each column in the `columns` array should have: +
```typescript { key: string; // Unique identifier for the column @@ -74,11 +77,13 @@ Each column in the `columns` array should have: render?: (value: any, row: any) => React.ReactNode; // Custom render function } ``` +
### Action Configuration Each action in the `actions` array should have: +
```typescript { icon: React.ComponentType; // Icon component to display @@ -90,11 +95,13 @@ Each action in the `actions` array should have: onClick?: (row: any) => void; // Click handler } ``` +
## Examples ### Basic Table +
```jsx import { Table } from '@workspace/ui'; @@ -119,9 +126,11 @@ export default function BasicTable() { ); } ``` +
### Table with Sorting +
```jsx import { useState } from 'react'; import { Table } from '@workspace/ui'; @@ -151,9 +160,11 @@ export default function SortableTable() { ); } ``` +
### Table with Custom Rendering +
```jsx import { Table } from '@workspace/ui'; @@ -181,9 +192,11 @@ export default function CustomTable() { return ; } ``` + ### Table with Actions +
```jsx import { Edit, Trash2 } from 'lucide-react'; import { Table } from '@workspace/ui'; @@ -217,9 +230,11 @@ export default function TableWithActions() { ); } ``` +
### Table with Loading State +
```jsx import { Table } from '@workspace/ui'; import { useState } from 'react'; @@ -246,9 +261,11 @@ export default function LoadingTable() { ); } ``` +
### Table with Expandable Rows +
```jsx import { Table } from '@workspace/ui'; @@ -278,6 +295,7 @@ export default function ExpandableTable() { ); } ``` +
## Features @@ -311,6 +329,7 @@ export default function ExpandableTable() { 7. **Provider Badges**: The table automatically formats provider columns (AWS, Azure) with appropriate badge styling. Use `provider` as the column key to enable this feature. +
```jsx // Good: Using unique rowKey for expandable rows
``` + ## Accessibility @@ -351,6 +371,7 @@ The Table component follows WAI-ARIA guidelines: You can customize the table appearance using the `className` prop to override default styles: +
```jsx
``` + The table supports custom styling for: - **Table container**: Use `className` prop for wrapper styles diff --git a/apps/www/src/content/docs/components/timeline.mdx b/apps/www/src/content/docs/components/timeline.mdx index 1dd220f..d5ff7e4 100644 --- a/apps/www/src/content/docs/components/timeline.mdx +++ b/apps/www/src/content/docs/components/timeline.mdx @@ -27,7 +27,7 @@ The Timeline component is designed to visualize a sequence of events or status c component={} source={ diff --git a/apps/www/src/content/docs/components/toggle.mdx b/apps/www/src/content/docs/components/toggle.mdx index 7b31397..54a6a2a 100644 --- a/apps/www/src/content/docs/components/toggle.mdx +++ b/apps/www/src/content/docs/components/toggle.mdx @@ -23,7 +23,7 @@ The Toggle component is a specialized theme switcher button. It features individ component={} source={ diff --git a/apps/www/src/content/docs/style/style.mdx b/apps/www/src/content/docs/style/style.mdx index f1eeb59..1c21439 100644 --- a/apps/www/src/content/docs/style/style.mdx +++ b/apps/www/src/content/docs/style/style.mdx @@ -7,6 +7,7 @@ description: global styles for the app Add these styles to your `style.css` file before using components. So you can modify the styles as per your need. +
```css :root { --foreground: #121212; @@ -25,3 +26,4 @@ Add these styles to your `style.css` file before using components. So you can mo --colora: #ffffff; } ``` +
\ No newline at end of file diff --git a/apps/www/src/lib/registry/components.ts b/apps/www/src/lib/registry/components.ts index 6e11ffe..725e32c 100644 --- a/apps/www/src/lib/registry/components.ts +++ b/apps/www/src/lib/registry/components.ts @@ -13,7 +13,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Addaccount.tsx", + "path": "src/components/docs/ts/ts/Addaccount.tsx", "type": "registry:ui" } ], @@ -32,7 +32,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Badge.tsx", + "path": "src/components/docs/ts/Badge.tsx", "type": "registry:ui" } ], @@ -51,26 +51,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Button.tsx", - "type": "registry:ui" - } - ], - "categories": [ - "ui" - ] - }, - { - "name": "cards", - "type": "registry:ui", - "title": "Cards", - "description": "A Cards component.", - "dependencies": [ - "clsx", - "tailwind-merge" - ], - "files": [ - { - "path": "src/components/docs/Cards.tsx", + "path": "src/components/docs/ts/Button.tsx", "type": "registry:ui" } ], @@ -89,7 +70,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Chatbot.tsx", + "path": "src/components/docs/ts/Chatbot.tsx", "type": "registry:ui" } ], @@ -108,7 +89,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Filterchip.tsx", + "path": "src/components/docs/ts/Filterchip.tsx", "type": "registry:ui" } ], @@ -127,7 +108,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Filters.tsx", + "path": "src/components/docs/ts/Filters.tsx", "type": "registry:ui" } ], @@ -146,7 +127,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Header.tsx", + "path": "src/components/docs/ts/Header.tsx", "type": "registry:ui" } ], @@ -165,7 +146,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/MultipleFilter.tsx", + "path": "src/components/docs/ts/MultipleFilter.tsx", "type": "registry:ui" } ], @@ -184,7 +165,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Pagination.tsx", + "path": "src/components/docs/ts/Pagination.tsx", "type": "registry:ui" } ], @@ -203,7 +184,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Sidebar.tsx", + "path": "src/components/docs/ts/Sidebar.tsx", "type": "registry:ui" } ], @@ -222,7 +203,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Skeleton.tsx", + "path": "src/components/docs/ts/Skeleton.tsx", "type": "registry:ui" } ], @@ -241,7 +222,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/SpiderView.tsx", + "path": "src/components/docs/ts/SpiderView.tsx", "type": "registry:ui" } ], @@ -260,7 +241,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Table.tsx", + "path": "src/components/docs/ts/Table.tsx", "type": "registry:ui" } ], @@ -279,7 +260,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Timeline.tsx", + "path": "src/components/docs/ts/Timeline.tsx", "type": "registry:ui" } ], @@ -298,7 +279,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Toggle.tsx", + "path": "src/components/docs/ts/Toggle.tsx", "type": "registry:ui" } ], diff --git a/apps/www/src/lib/source.ts b/apps/www/src/lib/source.ts index ca6f3e5..883d45e 100644 --- a/apps/www/src/lib/source.ts +++ b/apps/www/src/lib/source.ts @@ -48,7 +48,6 @@ export const source = loader({ export function getPageImage(page: InferPageType) { const segments = [...page.slugs, 'image.png']; - console.log(page); return { segments, url: `/og/docs/${segments.join('/')}`, diff --git a/apps/www/src/registry/components.ts b/apps/www/src/registry/components.ts index b7acf93..0435852 100644 --- a/apps/www/src/registry/components.ts +++ b/apps/www/src/registry/components.ts @@ -13,7 +13,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Addaccount.tsx", + "path": "src/components/docs/ts/ts/Addaccount.tsx", "type": "registry:ui" } ], @@ -32,26 +32,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Button.tsx", - "type": "registry:ui" - } - ], - "categories": [ - "ui" - ] - }, - { - "name": "cards", - "type": "registry:ui", - "title": "Cards", - "description": "A Cards component.", - "dependencies": [ - "clsx", - "tailwind-merge" - ], - "files": [ - { - "path": "src/components/docs/Cards.tsx", + "path": "src/components/docs/ts/Button.tsx", "type": "registry:ui" } ], @@ -70,7 +51,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Chatbot.tsx", + "path": "src/components/docs/ts/Chatbot.tsx", "type": "registry:ui" } ], @@ -89,7 +70,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/DataViewPanel.tsx", + "path": "src/components/docs/ts/DataViewPanel.tsx", "type": "registry:ui" } ], @@ -108,7 +89,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Filterchip.tsx", + "path": "src/components/docs/ts/Filterchip.tsx", "type": "registry:ui" } ], @@ -127,7 +108,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Filters.tsx", + "path": "src/components/docs/ts/Filters.tsx", "type": "registry:ui" } ], @@ -146,7 +127,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Header.tsx", + "path": "src/components/docs/ts/Header.tsx", "type": "registry:ui" } ], @@ -165,7 +146,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/MultipleFilter.tsx", + "path": "src/components/docs/ts/MultipleFilter.tsx", "type": "registry:ui" } ], @@ -184,7 +165,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Pagination.tsx", + "path": "src/components/docs/ts/Pagination.tsx", "type": "registry:ui" } ], @@ -203,7 +184,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Sidebar.tsx", + "path": "src/components/docs/ts/Sidebar.tsx", "type": "registry:ui" } ], @@ -222,7 +203,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Skeleton.tsx", + "path": "src/components/docs/ts/Skeleton.tsx", "type": "registry:ui" } ], @@ -241,7 +222,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/SpiderView.tsx", + "path": "src/components/docs/ts/SpiderView.tsx", "type": "registry:ui" } ], @@ -260,7 +241,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Table.tsx", + "path": "src/components/docs/ts/Table.tsx", "type": "registry:ui" } ], @@ -279,7 +260,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Timeline.tsx", + "path": "src/components/docs/ts/Timeline.tsx", "type": "registry:ui" } ], @@ -298,7 +279,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/Toggle.tsx", + "path": "src/components/docs/ts/Toggle.tsx", "type": "registry:ui" } ], @@ -317,7 +298,7 @@ export const components: RegistryEntry[] = [ ], "files": [ { - "path": "src/components/docs/notification.tsx", + "path": "src/components/docs/ts/notification.tsx", "type": "registry:ui" } ], diff --git a/apps/www/src/style/index.css b/apps/www/src/style/index.css index 9ba583f..246acfd 100644 --- a/apps/www/src/style/index.css +++ b/apps/www/src/style/index.css @@ -24,7 +24,8 @@ :root { --fd-layout-width: 0px; - --fd-sidebar-width: 268px; + --lg-sidebar-width:300px; + --fd-sidebar-width: 260px; --fd-sidebar-col: 268px; --fd-toc-width: 268px; --fd-docs-height: 100dvh; @@ -37,6 +38,10 @@ body { scrollbar-width: none; } +.web-logo{ + border-radius: 9999px; +} + ::-webkit-scrollbar { display: none; } @@ -91,4 +96,142 @@ body { .grid-toc { grid-area: toc; +} + +/* Code Loading Spinner Animation */ +.spinner_zWVm { + animation: spinner_5QiW 1.2s linear infinite, spinner_PnZo 1.2s linear infinite; +} +.spinner_gfyD { + animation: spinner_5QiW 1.2s linear infinite, spinner_4j7o 1.2s linear infinite; + animation-delay: 0.1s; +} +.spinner_T5JJ { + animation: spinner_5QiW 1.2s linear infinite, spinner_fLK4 1.2s linear infinite; + animation-delay: 0.1s; +} +.spinner_E3Wz { + animation: spinner_5QiW 1.2s linear infinite, spinner_tDji 1.2s linear infinite; + animation-delay: 0.2s; +} +.spinner_g2vs { + animation: spinner_5QiW 1.2s linear infinite, spinner_CMiT 1.2s linear infinite; + animation-delay: 0.2s; +} +.spinner_ctYB { + animation: spinner_5QiW 1.2s linear infinite, spinner_cHKR 1.2s linear infinite; + animation-delay: 0.2s; +} +.spinner_BDNj { + animation: spinner_5QiW 1.2s linear infinite, spinner_Re6e 1.2s linear infinite; + animation-delay: 0.3s; +} +.spinner_rCw3 { + animation: spinner_5QiW 1.2s linear infinite, spinner_EJmJ 1.2s linear infinite; + animation-delay: 0.3s; +} +.spinner_Rszm { + animation: spinner_5QiW 1.2s linear infinite, spinner_YJOP 1.2s linear infinite; + animation-delay: 0.4s; +} + +@keyframes spinner_5QiW { + 0%, 50% { + width: 7.33px; + height: 7.33px; + } + 25% { + width: 1.33px; + height: 1.33px; + } +} +@keyframes spinner_PnZo { + 0%, 50% { + x: 1px; + y: 1px; + } + 25% { + x: 4px; + y: 4px; + } +} +@keyframes spinner_4j7o { + 0%, 50% { + x: 8.33px; + y: 1px; + } + 25% { + x: 11.33px; + y: 4px; + } +} +@keyframes spinner_fLK4 { + 0%, 50% { + x: 1px; + y: 8.33px; + } + 25% { + x: 4px; + y: 11.33px; + } +} +@keyframes spinner_tDji { + 0%, 50% { + x: 15.66px; + y: 1px; + } + 25% { + x: 18.66px; + y: 4px; + } +} +@keyframes spinner_CMiT { + 0%, 50% { + x: 8.33px; + y: 8.33px; + } + 25% { + x: 11.33px; + y: 11.33px; + } +} +@keyframes spinner_cHKR { + 0%, 50% { + x: 1px; + y: 15.66px; + } + 25% { + x: 4px; + y: 18.66px; + } +} +@keyframes spinner_Re6e { + 0%, 50% { + x: 15.66px; + y: 8.33px; + } + 25% { + x: 18.66px; + y: 11.33px; + } +} +@keyframes spinner_EJmJ { + 0%, 50% { + x: 8.33px; + y: 15.66px; + } + 25% { + x: 11.33px; + y: 18.66px; + } +} +@keyframes spinner_YJOP { + 0%, 50% { + x: 15.66px; + y: 15.66px; + } + 25% { + x: 18.66px; + y: 18.66px; + } } \ No newline at end of file diff --git a/apps/www/src/style/theme.css b/apps/www/src/style/theme.css new file mode 100644 index 0000000..e483591 --- /dev/null +++ b/apps/www/src/style/theme.css @@ -0,0 +1,834 @@ +@theme { + /* FONT FAMILY */ + --font-body: var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif; + --font-display: var(--font-inter, "Inter"), -apple-system, "Segoe UI", Roboto, Arial, sans-serif; + --font-mono: ui-monospace, "Roboto Mono", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + + /* FONT SIZE */ + --text-xs: calc(var(--spacing) * 3); + --text-xs--line-height: calc(var(--spacing) * 4.5); + + --text-sm: calc(var(--spacing) * 3.5); + --text-sm--line-height: calc(var(--spacing) * 5); + + --text-md: calc(var(--spacing) * 4); + --text-md--line-height: calc(var(--spacing) * 6); + + --text-lg: calc(var(--spacing) * 4.5); + --text-lg--line-height: calc(var(--spacing) * 7); + + --text-xl: calc(var(--spacing) * 5); + --text-xl--line-height: calc(var(--spacing) * 7.5); + + --text-display-xs: calc(var(--spacing) * 6); + --text-display-xs--line-height: calc(var(--spacing) * 8); + + --text-display-sm: calc(var(--spacing) * 7.5); + --text-display-sm--line-height: calc(var(--spacing) * 9.5); + + --text-display-md: calc(var(--spacing) * 9); + --text-display-md--line-height: calc(var(--spacing) * 11); + --text-display-md--letter-spacing: -0.72px; + + --text-display-lg: calc(var(--spacing) * 12); + --text-display-lg--line-height: calc(var(--spacing) * 15); + --text-display-lg--letter-spacing: -0.96px; + + --text-display-xl: calc(var(--spacing) * 15); + --text-display-xl--line-height: calc(var(--spacing) * 18); + --text-display-xl--letter-spacing: -1.2px; + + --text-display-2xl: calc(var(--spacing) * 18); + --text-display-2xl--line-height: calc(var(--spacing) * 22.5); + --text-display-2xl--letter-spacing: -1.44px; + + /* MAX WIDTH */ + --max-width-container: 1280px; + + /* BREAKPOINTS */ + --breakpoint-xxs: 320px; + /* This must match the breakpoint in Sonner: https://github.com/emilkowalski/sonner/blob/main/src/styles.css */ + --breakpoint-xs: 600px; + + /* SHADOW */ + --shadow-xs: 0px 1px 2px rgba(0, 0, 0, 0.05); + --shadow-sm: 0px 1px 3px rgba(0, 0, 0, 0.1), 0px 1px 2px -1px rgba(0, 0, 0, 0.1); + --shadow-md: 0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.06); + --shadow-lg: 0px 12px 16px -4px rgba(0, 0, 0, 0.08), 0px 4px 6px -2px rgba(0, 0, 0, 0.03), 0px 2px 2px -1px rgba(0, 0, 0, 0.04); + --shadow-xl: 0px 20px 24px -4px rgba(0, 0, 0, 0.08), 0px 8px 8px -4px rgba(0, 0, 0, 0.03), 0px 3px 3px -1.5px rgba(0, 0, 0, 0.04); + --shadow-2xl: 0px 24px 48px -12px rgba(0, 0, 0, 0.18), 0px 4px 4px -2px rgba(0, 0, 0, 0.04); + --shadow-3xl: 0px 32px 64px -12px rgba(0, 0, 0, 0.14), 0px 5px 5px -2.5px rgba(0, 0, 0, 0.04); + + --shadow-skeuomorphic: 0px 0px 0px 1px rgba(0, 0, 0, 0.18) inset, 0px -2px 0px 0px rgba(0, 0, 0, 0.05) inset; + --shadow-xs-skeuomorphic: var(--shadow-skeuomorphic), var(--shadow-xs); + + --shadow-modern-mockup-inner-lg: 0px 0px 3.765px 1.255px rgba(10, 13, 18, 0.08) inset, 0px 0px 2.51px 1.255px rgba(10, 13, 18, 0.03) inset; + --shadow-modern-mockup-inner-md: 0px 0px 1.692px 0.564px rgba(10, 13, 18, 0.08) inset, 0px 0px 1.128px 0.564px rgba(10, 13, 18, 0.03) inset; + --shadow-modern-mockup-inner-sm: 0px 0px 4.48px 1.493px rgba(10, 13, 18, 0.08) inset, 0px 0px 2.987px 1.493px rgba(10, 13, 18, 0.03) inset; + + --shadow-modern-mockup-outer-lg: + 0px 7.529px 10.039px -2.51px rgba(10, 13, 18, 0.08), 0px 2.51px 3.765px -1.255px rgba(10, 13, 18, 0.03), + 0px 1.255px 1.255px -0.627px rgba(10, 13, 18, 0.04); + --shadow-modern-mockup-outer-md: + 0px 3.385px 4.513px -1.128px rgba(10, 13, 18, 0.08), 0px 1.128px 1.692px -0.564px rgba(10, 13, 18, 0.03), + 0px 0.564px 0.564px -0.282px rgba(10, 13, 18, 0.04); + + --drop-shadow-iphone-mockup: 20px 12px 18px rgba(16, 24, 40, 0.2); + + /* ANIMATIONS */ + --animate-marquee: marquee 60s linear infinite; + --animate-caret-blink: caret-blink 1s infinite; + + @keyframes marquee { + 0% { + transform: translateX(0); + } + 100% { + transform: translateX(-100%); + } + } + + @keyframes caret-blink { + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } + } + + /* BASE COLOR */ + --color-transparent: rgb(0 0 0 / 0); + + /* These will be inverted in dark mode. */ + --color-alpha-white: rgb(255 255 255); + --color-alpha-black: rgb(0 0 0); + + --color-brand-50: rgb(249 245 255); + --color-brand-100: rgb(244 235 255); + --color-brand-200: rgb(233 215 254); + --color-brand-300: rgb(214 187 251); + --color-brand-400: rgb(182 146 246); + --color-brand-500: rgb(158 119 237); + --color-brand-600: rgb(127 86 217); + --color-brand-700: rgb(105 65 198); + --color-brand-800: rgb(83 56 158); + --color-brand-900: rgb(66 48 125); + --color-brand-950: rgb(44 28 95); + + /* LIGHT MODE VARIABLES */ + + --color-alpha-white: rgb(255 255 255); + --color-alpha-black: rgb(0 0 0); + + /* UTILITY COLORS */ + --color-utility-blue-50: var(--color-blue-50); + --color-utility-blue-100: var(--color-blue-100); + --color-utility-blue-200: var(--color-blue-200); + --color-utility-blue-300: var(--color-blue-300); + --color-utility-blue-400: var(--color-blue-400); + --color-utility-blue-500: var(--color-blue-500); + --color-utility-blue-600: var(--color-blue-600); + --color-utility-blue-700: var(--color-blue-700); + + --color-utility-brand-50: var(--color-brand-50); + --color-utility-brand-50_alt: var(--color-brand-50); + --color-utility-brand-100: var(--color-brand-100); + --color-utility-brand-100_alt: var(--color-brand-100); + --color-utility-brand-200: var(--color-brand-200); + --color-utility-brand-200_alt: var(--color-brand-200); + --color-utility-brand-300: var(--color-brand-300); + --color-utility-brand-300_alt: var(--color-brand-300); + --color-utility-brand-400: var(--color-brand-400); + --color-utility-brand-400_alt: var(--color-brand-400); + --color-utility-brand-500: var(--color-brand-500); + --color-utility-brand-500_alt: var(--color-brand-500); + --color-utility-brand-600: var(--color-brand-600); + --color-utility-brand-600_alt: var(--color-brand-600); + --color-utility-brand-700: var(--color-brand-700); + --color-utility-brand-700_alt: var(--color-brand-700); + --color-utility-brand-800: var(--color-brand-800); + --color-utility-brand-800_alt: var(--color-brand-800); + --color-utility-brand-900: var(--color-brand-900); + --color-utility-brand-900_alt: var(--color-brand-900); + + --color-utility-neutral-50: var(--color-neutral-50); + --color-utility-neutral-100: var(--color-neutral-100); + --color-utility-neutral-200: var(--color-neutral-200); + --color-utility-neutral-300: var(--color-neutral-300); + --color-utility-neutral-400: var(--color-neutral-400); + --color-utility-neutral-500: var(--color-neutral-500); + --color-utility-neutral-600: var(--color-neutral-600); + --color-utility-neutral-700: var(--color-neutral-700); + --color-utility-neutral-800: var(--color-neutral-800); + --color-utility-neutral-900: var(--color-neutral-900); + + --color-utility-red-50: var(--color-red-50); + --color-utility-red-100: var(--color-red-100); + --color-utility-red-200: var(--color-red-200); + --color-utility-red-300: var(--color-red-300); + --color-utility-red-400: var(--color-red-400); + --color-utility-red-500: var(--color-red-500); + --color-utility-red-600: var(--color-red-600); + --color-utility-red-700: var(--color-red-700); + + --color-utility-yellow-50: var(--color-yellow-50); + --color-utility-yellow-100: var(--color-yellow-100); + --color-utility-yellow-200: var(--color-yellow-200); + --color-utility-yellow-300: var(--color-yellow-300); + --color-utility-yellow-400: var(--color-yellow-400); + --color-utility-yellow-500: var(--color-yellow-500); + --color-utility-yellow-600: var(--color-yellow-600); + --color-utility-yellow-700: var(--color-yellow-700); + + --color-utility-green-50: var(--color-green-50); + --color-utility-green-100: var(--color-green-100); + --color-utility-green-200: var(--color-green-200); + --color-utility-green-300: var(--color-green-300); + --color-utility-green-400: var(--color-green-400); + --color-utility-green-500: var(--color-green-500); + --color-utility-green-600: var(--color-green-600); + --color-utility-green-700: var(--color-green-700); + + --color-utility-orange-50: var(--color-orange-50); + --color-utility-orange-100: var(--color-orange-100); + --color-utility-orange-200: var(--color-orange-200); + --color-utility-orange-300: var(--color-orange-300); + --color-utility-orange-400: var(--color-orange-400); + --color-utility-orange-500: var(--color-orange-500); + --color-utility-orange-600: var(--color-orange-600); + --color-utility-orange-700: var(--color-orange-700); + + --color-utility-indigo-50: var(--color-indigo-50); + --color-utility-indigo-100: var(--color-indigo-100); + --color-utility-indigo-200: var(--color-indigo-200); + --color-utility-indigo-300: var(--color-indigo-300); + --color-utility-indigo-400: var(--color-indigo-400); + --color-utility-indigo-500: var(--color-indigo-500); + --color-utility-indigo-600: var(--color-indigo-600); + --color-utility-indigo-700: var(--color-indigo-700); + + --color-utility-fuchsia-50: var(--color-fuchsia-50); + --color-utility-fuchsia-100: var(--color-fuchsia-100); + --color-utility-fuchsia-200: var(--color-fuchsia-200); + --color-utility-fuchsia-300: var(--color-fuchsia-300); + --color-utility-fuchsia-400: var(--color-fuchsia-400); + --color-utility-fuchsia-500: var(--color-fuchsia-500); + --color-utility-fuchsia-600: var(--color-fuchsia-600); + --color-utility-fuchsia-700: var(--color-fuchsia-700); + + --color-utility-pink-50: var(--color-pink-50); + --color-utility-pink-100: var(--color-pink-100); + --color-utility-pink-200: var(--color-pink-200); + --color-utility-pink-300: var(--color-pink-300); + --color-utility-pink-400: var(--color-pink-400); + --color-utility-pink-500: var(--color-pink-500); + --color-utility-pink-600: var(--color-pink-600); + --color-utility-pink-700: var(--color-pink-700); + + --color-utility-purple-50: var(--color-purple-50); + --color-utility-purple-100: var(--color-purple-100); + --color-utility-purple-200: var(--color-purple-200); + --color-utility-purple-300: var(--color-purple-300); + --color-utility-purple-400: var(--color-purple-400); + --color-utility-purple-500: var(--color-purple-500); + --color-utility-purple-600: var(--color-purple-600); + --color-utility-purple-700: var(--color-purple-700); + + --color-utility-sky-50: var(--color-sky-50); + --color-utility-sky-100: var(--color-sky-100); + --color-utility-sky-200: var(--color-sky-200); + --color-utility-sky-300: var(--color-sky-300); + --color-utility-sky-400: var(--color-sky-400); + --color-utility-sky-500: var(--color-sky-500); + --color-utility-sky-600: var(--color-sky-600); + --color-utility-sky-700: var(--color-sky-700); + + --color-utility-slate-50: var(--color-slate-50); + --color-utility-slate-100: var(--color-slate-100); + --color-utility-slate-200: var(--color-slate-200); + --color-utility-slate-300: var(--color-slate-300); + --color-utility-slate-400: var(--color-slate-400); + --color-utility-slate-500: var(--color-slate-500); + --color-utility-slate-600: var(--color-slate-600); + --color-utility-slate-700: var(--color-slate-700); + + --color-utility-emerald-50: var(--color-emerald-50); + --color-utility-emerald-100: var(--color-emerald-100); + --color-utility-emerald-200: var(--color-emerald-200); + --color-utility-emerald-300: var(--color-emerald-300); + --color-utility-emerald-400: var(--color-emerald-400); + --color-utility-emerald-500: var(--color-emerald-500); + --color-utility-emerald-600: var(--color-emerald-600); + --color-utility-emerald-700: var(--color-emerald-700); + + --color-utility-amber-50: var(--color-amber-50); + --color-utility-amber-100: var(--color-amber-100); + --color-utility-amber-200: var(--color-amber-200); + --color-utility-amber-300: var(--color-amber-300); + --color-utility-amber-400: var(--color-amber-400); + --color-utility-amber-500: var(--color-amber-500); + --color-utility-amber-600: var(--color-amber-600); + --color-utility-amber-700: var(--color-amber-700); + + /* TEXT COLORS */ + --color-text-primary: var(--color-neutral-900); + --color-text-tertiary: var(--color-neutral-600); + --color-text-error-primary: var(--color-red-600); + --color-text-warning-primary: var(--color-yellow-600); + --color-text-success-primary: var(--color-green-600); + --color-text-secondary: var(--color-neutral-700); + --color-text-secondary_hover: var(--color-neutral-800); + --color-text-tertiary_hover: var(--color-neutral-700); + --color-text-brand-secondary: var(--color-brand-700); + --color-text-placeholder: var(--color-neutral-500); + --color-text-brand-tertiary: var(--color-brand-600); + --color-text-editor-icon-fg: var(--color-neutral-400); + --color-text-editor-icon-fg_active: var(--color-neutral-500); + --color-text-quaternary: var(--color-neutral-500); + --color-text-brand-primary: var(--color-brand-900); + --color-text-primary_on-brand: var(--color-white); + --color-text-secondary_on-brand: var(--color-brand-200); + --color-text-tertiary_on-brand: var(--color-brand-200); + --color-text-quaternary_on-brand: var(--color-brand-300); + --color-text-brand-tertiary_alt: var(--color-brand-600); + --color-text-error-primary_hover: var(--color-red-700); + --color-text-brand-secondary_hover: var(--color-brand-800); + + /* BORDER COLORS */ + --color-border-primary: var(--color-neutral-300); + --color-border-secondary: var(--color-neutral-200); + --color-border-secondary_alt: rgb(0 0 0 / 0.1); + --color-border-tertiary: var(--color-neutral-100); + --color-border-error: var(--color-red-500); + --color-border-error_subtle: var(--color-red-300); + --color-border-brand: var(--color-brand-500); + --color-border-brand_alt: var(--color-brand-600); + + /* FOREGROUND COLORS */ + --color-fg-secondary: var(--color-neutral-700); + --color-fg-warning-primary: var(--color-yellow-600); + --color-fg-success-primary: var(--color-green-600); + --color-fg-white: var(--color-white); + --color-fg-success-secondary: var(--color-green-500); + --color-fg-secondary_hover: var(--color-neutral-800); + --color-fg-primary: var(--color-neutral-900); + --color-fg-brand-secondary: var(--color-brand-500); + --color-fg-brand-primary: var(--color-brand-600); + --color-fg-quaternary: var(--color-neutral-400); + --color-fg-quaternary_hover: var(--color-neutral-500); + --color-fg-error-primary: var(--color-red-600); + --color-fg-warning-secondary: var(--color-yellow-500); + --color-fg-error-secondary: var(--color-red-500); + --color-fg-tertiary: var(--color-neutral-600); + --color-fg-tertiary_hover: var(--color-neutral-700); + --color-fg-brand-primary_alt: var(--color-fg-brand-primary); + --color-fg-brand-secondary_alt: var(--color-fg-brand-secondary); + --color-fg-brand-secondary_hover: var(--color-brand-600); + + /* BACKGROUND COLORS */ + --color-bg-primary: var(--color-white); + --color-bg-tertiary: var(--color-neutral-100); + --color-bg-brand-primary: var(--color-brand-50); + --color-bg-error-secondary: var(--color-red-100); + --color-bg-warning-primary: var(--color-yellow-50); + --color-bg-warning-secondary: var(--color-yellow-100); + --color-bg-success-primary: var(--color-green-50); + --color-bg-success-secondary: var(--color-green-100); + --color-bg-brand-solid: var(--color-brand-600); + --color-bg-secondary-solid: var(--color-neutral-600); + --color-bg-error-solid: var(--color-red-600); + --color-bg-warning-solid: var(--color-yellow-600); + --color-bg-success-solid: var(--color-green-600); + --color-bg-secondary_hover: var(--color-neutral-100); + --color-bg-primary_hover: var(--color-neutral-50); + --color-bg-brand-solid_hover: var(--color-brand-700); + --color-bg-error-primary: var(--color-red-50); + --color-bg-brand-secondary: var(--color-brand-100); + --color-bg-secondary: var(--color-neutral-50); + --color-bg-quaternary: var(--color-neutral-200); + --color-bg-primary_alt: var(--color-white); + --color-bg-brand-primary_alt: var(--color-brand-50); + --color-bg-secondary_alt: var(--color-neutral-50); + --color-bg-overlay: var(--color-neutral-950); + --color-bg-brand-section: var(--color-brand-800); + --color-bg-brand-section_subtle: var(--color-brand-700); + --color-bg-primary-solid: var(--color-neutral-950); + --color-bg-error-solid_hover: var(--color-red-700); + + /* COMPONENT COLORS */ + --color-app-store-badge-border: 166 166 166 1; + --color-avatar-styles-bg-neutral: 224 224 224 1; + --color-featured-icon-light-fg-brand: var(--color-brand-600); + --color-featured-icon-light-fg-error: var(--color-red-600); + --color-featured-icon-light-fg-gray: var(--color-neutral-500); + --color-featured-icon-light-fg-success: var(--color-green-600); + --color-featured-icon-light-fg-warning: var(--color-yellow-600); + --color-focus-ring-error: var(--color-red-500); + --color-focus-ring: var(--color-brand-500); + --color-footer-button-fg: var(--color-brand-200); + --color-footer-button-fg_hover: var(--color-white); + --color-icon-fg-brand: var(--color-brand-600); + --color-icon-fg-brand_on-brand: var(--color-brand-200); + --color-screen-mockup-border: var(--color-neutral-900); + + --color-slider-handle-bg: var(--color-white); + --color-slider-handle-border: var(--color-brand-600); + --color-toggle-border: var(--color-neutral-300); + --color-toggle-slim-border_pressed-hover: var(--color-bg-brand-solid_hover); + --color-toggle-slim-border_pressed: var(--color-bg-brand-solid); + --color-tooltip-supporting-text: var(--color-neutral-300); + + /* BACKGROUND PROPERTY COLORS */ + --background-color-primary: var(--color-bg-primary); + --background-color-tertiary: var(--color-bg-tertiary); + --background-color-brand-primary: var(--color-bg-brand-primary); + --background-color-error-secondary: var(--color-bg-error-secondary); + --background-color-warning-primary: var(--color-bg-warning-primary); + --background-color-warning-secondary: var(--color-bg-warning-secondary); + --background-color-success-primary: var(--color-bg-success-primary); + --background-color-success-secondary: var(--color-bg-success-secondary); + --background-color-brand-solid: var(--color-bg-brand-solid); + --background-color-secondary-solid: var(--color-bg-secondary-solid); + --background-color-error-solid: var(--color-bg-error-solid); + --background-color-warning-solid: var(--color-bg-warning-solid); + --background-color-success-solid: var(--color-bg-success-solid); + --background-color-secondary_hover: var(--color-bg-secondary_hover); + --background-color-primary_hover: var(--color-bg-primary_hover); + --background-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + --background-color-error-primary: var(--color-bg-error-primary); + --background-color-brand-secondary: var(--color-bg-brand-secondary); + --background-color-secondary: var(--color-bg-secondary); + --background-color-quaternary: var(--color-bg-quaternary); + --background-color-primary_alt: var(--color-bg-primary_alt); + --background-color-brand-primary_alt: var(--color-bg-brand-primary_alt); + --background-color-secondary_alt: var(--color-bg-secondary_alt); + --background-color-overlay: var(--color-bg-overlay); + --background-color-brand-section: var(--color-bg-brand-section); + --background-color-brand-section_subtle: var(--color-bg-brand-section_subtle); + --background-color-primary-solid: var(--color-bg-primary-solid); + --background-color-error-solid_hover: var(--color-bg-error-solid_hover); + --background-color-border-brand: var(--color-border-brand); + --background-color-border-brand_alt: var(--color-border-brand_alt); + + /* TEXT PROPERTY COLORS */ + --color-text-white: var(--color-white); + --text-color-primary: var(--color-text-primary); + --text-color-secondary: var(--color-text-secondary); + --text-color-secondary_hover: var(--color-text-secondary_hover); + --text-color-tertiary: var(--color-text-tertiary); + --text-color-tertiary_hover: var(--color-text-tertiary_hover); + --text-color-error-primary: var(--color-text-error-primary); + --text-color-warning-primary: var(--color-text-warning-primary); + --text-color-success-primary: var(--color-text-success-primary); + --text-color-brand-secondary: var(--color-text-brand-secondary); + --text-color-placeholder: var(--color-text-placeholder); + --text-color-brand-tertiary: var(--color-text-brand-tertiary); + --text-color-editor-icon-fg: var(--color-text-editor-icon-fg); + --text-color-editor-icon-fg_active: var(--color-text-editor-icon-fg_active); + --text-color-quaternary: var(--color-text-quaternary); + --text-color-brand-primary: var(--color-text-brand-primary); + --text-color-primary_on-brand: var(--color-text-primary_on-brand); + --text-color-secondary_on-brand: var(--color-text-secondary_on-brand); + --text-color-tertiary_on-brand: var(--color-text-tertiary_on-brand); + --text-color-quaternary_on-brand: var(--color-text-quaternary_on-brand); + --text-color-brand-tertiary_alt: var(--color-text-brand-tertiary_alt); + --text-color-error-primary_hover: var(--color-text-error-primary_hover); + --text-color-brand-secondary_hover: var(--color-text-brand-secondary_hover); + --text-color-tooltip-supporting-text: var(--color-tooltip-supporting-text); + + /* BORDER PROPERTY COLORS */ + --border-color-primary: var(--color-border-primary); + --border-color-secondary: var(--color-border-secondary); + --border-color-secondary_alt: var(--color-border-secondary_alt); + --border-color-tertiary: var(--color-border-tertiary); + --border-color-error: var(--color-border-error); + --border-color-error_subtle: var(--color-border-error_subtle); + --border-color-brand: var(--color-border-brand); + --border-color-brand_alt: var(--color-border-brand_alt); + --border-color-brand-solid: var(--color-bg-brand-solid); + --border-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + + /* RING PROPERTY COLORS */ + --ring-color-primary: var(--color-border-primary); + --ring-color-secondary: var(--color-border-secondary); + --ring-color-secondary_alt: var(--color-border-secondary_alt); + --ring-color-tertiary: var(--color-border-tertiary); + --ring-color-error: var(--color-border-error); + --ring-color-error_subtle: var(--color-border-error_subtle); + --ring-color-brand: var(--color-border-brand); + --ring-color-brand_alt: var(--color-border-brand_alt); + --ring-color-brand-solid: var(--color-bg-brand-solid); + --ring-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + + /* OUTLINE PROPERTY COLORS */ + --outline-color-primary: var(--color-border-primary); + --outline-color-secondary: var(--color-border-secondary); + --outline-color-secondary_alt: var(--color-border-secondary_alt); + --outline-color-tertiary: var(--color-border-tertiary); + --outline-color-error: var(--color-border-error); + --outline-color-error_subtle: var(--color-border-error_subtle); + --outline-color-brand: var(--color-border-brand); + --outline-color-brand_alt: var(--color-border-brand_alt); + --outline-color-brand-solid: var(--color-bg-brand-solid); + --outline-color-brand-solid_hover: var(--color-bg-brand-solid_hover); +} + +@layer base { + /* DARK MODE VARIABLES */ + + .dark-mode { + --color-alpha-white: rgb(12 14 18); + --color-alpha-black: rgb(255 255 255); + + /* UTILITY COLORS */ + + --color-utility-blue-50: var(--color-blue-950); + --color-utility-blue-100: var(--color-blue-900); + --color-utility-blue-200: var(--color-blue-800); + --color-utility-blue-300: var(--color-blue-700); + --color-utility-blue-400: var(--color-blue-600); + --color-utility-blue-500: var(--color-blue-500); + --color-utility-blue-600: var(--color-blue-400); + --color-utility-blue-700: var(--color-blue-300); + + --color-utility-brand-50: var(--color-brand-950); + --color-utility-brand-50_alt: var(--color-utility-neutral-50); + --color-utility-brand-100: var(--color-brand-900); + --color-utility-brand-100_alt: var(--color-utility-neutral-100); + --color-utility-brand-200: var(--color-brand-800); + --color-utility-brand-200_alt: var(--color-utility-neutral-200); + --color-utility-brand-300: var(--color-brand-700); + --color-utility-brand-300_alt: var(--color-utility-neutral-300); + --color-utility-brand-400: var(--color-brand-600); + --color-utility-brand-400_alt: var(--color-utility-neutral-400); + --color-utility-brand-500: var(--color-brand-500); + --color-utility-brand-500_alt: var(--color-utility-neutral-500); + --color-utility-brand-600: var(--color-brand-400); + --color-utility-brand-600_alt: var(--color-utility-neutral-600); + --color-utility-brand-700: var(--color-brand-300); + --color-utility-brand-700_alt: var(--color-utility-neutral-700); + --color-utility-brand-800: var(--color-brand-200); + --color-utility-brand-800_alt: var(--color-utility-neutral-800); + --color-utility-brand-900: var(--color-brand-100); + --color-utility-brand-900_alt: var(--color-utility-neutral-900); + + --color-utility-neutral-50: var(--color-neutral-900); + --color-utility-neutral-100: var(--color-neutral-800); + --color-utility-neutral-200: var(--color-neutral-700); + --color-utility-neutral-300: var(--color-neutral-700); + --color-utility-neutral-400: var(--color-neutral-600); + --color-utility-neutral-500: var(--color-neutral-500); + --color-utility-neutral-600: var(--color-neutral-400); + --color-utility-neutral-700: var(--color-neutral-300); + --color-utility-neutral-800: var(--color-neutral-200); + --color-utility-neutral-900: var(--color-neutral-100); + + --color-utility-red-50: var(--color-red-950); + --color-utility-red-100: var(--color-red-900); + --color-utility-red-200: var(--color-red-800); + --color-utility-red-300: var(--color-red-700); + --color-utility-red-400: var(--color-red-600); + --color-utility-red-500: var(--color-red-500); + --color-utility-red-600: var(--color-red-400); + --color-utility-red-700: var(--color-red-300); + + --color-utility-yellow-50: var(--color-yellow-950); + --color-utility-yellow-100: var(--color-yellow-900); + --color-utility-yellow-200: var(--color-yellow-800); + --color-utility-yellow-300: var(--color-yellow-700); + --color-utility-yellow-400: var(--color-yellow-600); + --color-utility-yellow-500: var(--color-yellow-500); + --color-utility-yellow-600: var(--color-yellow-400); + --color-utility-yellow-700: var(--color-yellow-300); + + --color-utility-green-50: var(--color-green-950); + --color-utility-green-100: var(--color-green-900); + --color-utility-green-200: var(--color-green-800); + --color-utility-green-300: var(--color-green-700); + --color-utility-green-400: var(--color-green-600); + --color-utility-green-500: var(--color-green-500); + --color-utility-green-600: var(--color-green-400); + --color-utility-green-700: var(--color-green-300); + + --color-utility-orange-50: var(--color-orange-950); + --color-utility-orange-100: var(--color-orange-900); + --color-utility-orange-200: var(--color-orange-800); + --color-utility-orange-300: var(--color-orange-700); + --color-utility-orange-400: var(--color-orange-600); + --color-utility-orange-500: var(--color-orange-500); + --color-utility-orange-600: var(--color-orange-400); + --color-utility-orange-700: var(--color-orange-300); + + --color-utility-indigo-50: var(--color-indigo-950); + --color-utility-indigo-100: var(--color-indigo-900); + --color-utility-indigo-200: var(--color-indigo-800); + --color-utility-indigo-300: var(--color-indigo-700); + --color-utility-indigo-400: var(--color-indigo-600); + --color-utility-indigo-500: var(--color-indigo-500); + --color-utility-indigo-600: var(--color-indigo-400); + --color-utility-indigo-700: var(--color-indigo-300); + + --color-utility-fuchsia-50: var(--color-fuchsia-950); + --color-utility-fuchsia-100: var(--color-fuchsia-900); + --color-utility-fuchsia-200: var(--color-fuchsia-800); + --color-utility-fuchsia-300: var(--color-fuchsia-700); + --color-utility-fuchsia-400: var(--color-fuchsia-600); + --color-utility-fuchsia-500: var(--color-fuchsia-500); + --color-utility-fuchsia-600: var(--color-fuchsia-400); + --color-utility-fuchsia-700: var(--color-fuchsia-300); + + --color-utility-pink-50: var(--color-pink-950); + --color-utility-pink-100: var(--color-pink-900); + --color-utility-pink-200: var(--color-pink-800); + --color-utility-pink-300: var(--color-pink-700); + --color-utility-pink-400: var(--color-pink-600); + --color-utility-pink-500: var(--color-pink-500); + --color-utility-pink-600: var(--color-pink-400); + --color-utility-pink-700: var(--color-pink-300); + + --color-utility-purple-50: var(--color-purple-950); + --color-utility-purple-100: var(--color-purple-900); + --color-utility-purple-200: var(--color-purple-800); + --color-utility-purple-300: var(--color-purple-700); + --color-utility-purple-400: var(--color-purple-600); + --color-utility-purple-500: var(--color-purple-500); + --color-utility-purple-600: var(--color-purple-400); + --color-utility-purple-700: var(--color-purple-300); + + --color-utility-sky-50: var(--color-sky-950); + --color-utility-sky-100: var(--color-sky-900); + --color-utility-sky-200: var(--color-sky-800); + --color-utility-sky-300: var(--color-sky-700); + --color-utility-sky-400: var(--color-sky-600); + --color-utility-sky-500: var(--color-sky-500); + --color-utility-sky-600: var(--color-sky-400); + --color-utility-sky-700: var(--color-sky-300); + + --color-utility-slate-50: var(--color-slate-950); + --color-utility-slate-100: var(--color-slate-900); + --color-utility-slate-200: var(--color-slate-800); + --color-utility-slate-300: var(--color-slate-700); + --color-utility-slate-400: var(--color-slate-600); + --color-utility-slate-500: var(--color-slate-500); + --color-utility-slate-600: var(--color-slate-400); + --color-utility-slate-700: var(--color-slate-300); + + --color-utility-emerald-50: var(--color-emerald-950); + --color-utility-emerald-100: var(--color-emerald-900); + --color-utility-emerald-200: var(--color-emerald-800); + --color-utility-emerald-300: var(--color-emerald-700); + --color-utility-emerald-400: var(--color-emerald-600); + --color-utility-emerald-500: var(--color-emerald-500); + --color-utility-emerald-600: var(--color-emerald-400); + --color-utility-emerald-700: var(--color-emerald-300); + + --color-utility-amber-50: var(--color-amber-950); + --color-utility-amber-100: var(--color-amber-900); + --color-utility-amber-200: var(--color-amber-800); + --color-utility-amber-300: var(--color-amber-700); + --color-utility-amber-400: var(--color-amber-600); + --color-utility-amber-500: var(--color-amber-500); + --color-utility-amber-600: var(--color-amber-400); + --color-utility-amber-700: var(--color-amber-300); + + --color-text-primary: var(--color-neutral-50); + --color-text-tertiary: var(--color-neutral-400); + --color-text-error-primary: var(--color-red-400); + --color-text-warning-primary: var(--color-yellow-400); + --color-text-success-primary: var(--color-green-400); + --color-text-secondary: var(--color-neutral-300); + --color-text-secondary_hover: var(--color-neutral-200); + --color-text-tertiary_hover: var(--color-neutral-300); + --color-text-brand-secondary: var(--color-neutral-300); + --color-text-placeholder: var(--color-neutral-500); + --color-text-brand-tertiary: var(--color-neutral-400); + --color-text-editor-icon-fg: var(--color-neutral-400); + --color-text-editor-icon-fg_active: var(--color-white); + --color-text-quaternary: var(--color-neutral-400); + --color-text-brand-primary: var(--color-neutral-50); + --color-text-primary_on-brand: var(--color-neutral-50); + --color-text-secondary_on-brand: var(--color-neutral-300); + --color-text-tertiary_on-brand: var(--color-neutral-400); + --color-text-quaternary_on-brand: var(--color-neutral-400); + --color-text-brand-tertiary_alt: var(--color-neutral-50); + --color-text-error-primary_hover: var(--color-red-300); + --color-text-brand-secondary_hover: var(--color-neutral-200); + + --color-border-secondary: var(--color-neutral-800); + --color-border-error_subtle: var(--color-red-500); + --color-border-primary: var(--color-neutral-700); + --color-border-brand: var(--color-brand-400); + --color-border-error: var(--color-red-400); + --color-border-tertiary: var(--color-neutral-800); + --color-border-brand_alt: var(--color-neutral-700); + --color-border-secondary_alt: var(--color-neutral-800); + + --color-fg-secondary: var(--color-neutral-300); + --color-fg-warning-primary: var(--color-yellow-500); + --color-fg-success-primary: var(--color-green-500); + --color-fg-white: var(--color-white); + --color-fg-success-secondary: var(--color-green-400); + --color-fg-secondary_hover: var(--color-neutral-200); + --color-fg-primary: var(--color-white); + --color-fg-brand-secondary: var(--color-brand-500); + --color-fg-brand-primary: var(--color-brand-500); + --color-fg-quaternary: var(--color-neutral-600); + --color-fg-quaternary_hover: var(--color-neutral-500); + --color-fg-error-primary: var(--color-red-500); + --color-fg-warning-secondary: var(--color-yellow-400); + --color-fg-error-secondary: var(--color-red-400); + --color-fg-tertiary: var(--color-neutral-400); + --color-fg-tertiary_hover: var(--color-neutral-300); + --color-fg-brand-primary_alt: var(--color-neutral-300); + --color-fg-brand-secondary_alt: var(--color-neutral-600); + --color-fg-brand-secondary_hover: var(--color-neutral-500); + + --color-bg-primary: var(--color-neutral-950); + --color-bg-tertiary: var(--color-neutral-800); + --color-bg-brand-primary: var(--color-brand-500); + --color-bg-error-secondary: var(--color-red-600); + --color-bg-warning-primary: var(--color-yellow-950); + --color-bg-warning-secondary: var(--color-yellow-600); + --color-bg-success-primary: var(--color-green-950); + --color-bg-success-secondary: var(--color-green-600); + --color-bg-brand-solid: var(--color-brand-600); + --color-bg-secondary-solid: var(--color-neutral-600); + --color-bg-error-solid: var(--color-red-600); + --color-bg-warning-solid: var(--color-yellow-600); + --color-bg-success-solid: var(--color-green-600); + --color-bg-secondary_hover: var(--color-neutral-800); + --color-bg-primary_hover: var(--color-neutral-900); + --color-bg-brand-solid_hover: var(--color-brand-500); + --color-bg-error-primary: var(--color-red-950); + --color-bg-brand-secondary: var(--color-brand-600); + --color-bg-secondary: var(--color-neutral-900); + --color-bg-quaternary: var(--color-neutral-700); + --color-bg-primary_alt: var(--color-bg-secondary); + --color-bg-brand-primary_alt: var(--color-bg-secondary); + --color-bg-secondary_alt: var(--color-bg-primary); + --color-bg-overlay: var(--color-neutral-800); + --color-bg-brand-section: var(--color-bg-secondary); + --color-bg-brand-section_subtle: var(--color-bg-primary); + --color-bg-primary-solid: var(--color-bg-secondary); + --color-bg-error-solid_hover: var(--color-red-500); + + --color-app-store-badge-border: var(--color-white); + --color-avatar-styles-bg-neutral: 224 224 224 1; + --color-featured-icon-light-fg-brand: var(--color-brand-200); + --color-featured-icon-light-fg-error: var(--color-red-200); + --color-featured-icon-light-fg-gray: var(--color-neutral-200); + --color-featured-icon-light-fg-success: var(--color-green-200); + --color-featured-icon-light-fg-warning: var(--color-yellow-200); + --color-focus-ring-error: var(--color-red-500); + --color-focus-ring: var(--color-brand-500); + --color-footer-button-fg: var(--color-neutral-300); + --color-footer-button-fg_hover: var(--color-neutral-100); + --color-icon-fg-brand: var(--color-neutral-400); + --color-icon-fg-brand_on-brand: var(--color-neutral-400); + --color-screen-mockup-border: var(--color-neutral-700); + --color-slider-handle-bg: var(--color-fg-brand-primary); + --color-slider-handle-border: var(--color-bg-primary); + --color-toggle-border: var(--color-transparent); + --color-toggle-slim-border_pressed-hover: var(--color-transparent); + --color-toggle-slim-border_pressed: var(--color-transparent); + --color-tooltip-supporting-text: var(--color-neutral-300); + + /* BACKGROUND PROPERTY COLORS */ + --background-color-primary: var(--color-bg-primary); + --background-color-tertiary: var(--color-bg-tertiary); + --background-color-brand-primary: var(--color-bg-brand-primary); + --background-color-error-secondary: var(--color-bg-error-secondary); + --background-color-warning-primary: var(--color-bg-warning-primary); + --background-color-warning-secondary: var(--color-bg-warning-secondary); + --background-color-success-primary: var(--color-bg-success-primary); + --background-color-success-secondary: var(--color-bg-success-secondary); + --background-color-brand-solid: var(--color-bg-brand-solid); + --background-color-secondary-solid: var(--color-bg-secondary-solid); + --background-color-error-solid: var(--color-bg-error-solid); + --background-color-warning-solid: var(--color-bg-warning-solid); + --background-color-success-solid: var(--color-bg-success-solid); + --background-color-secondary_hover: var(--color-bg-secondary_hover); + --background-color-primary_hover: var(--color-bg-primary_hover); + --background-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + --background-color-error-primary: var(--color-bg-error-primary); + --background-color-brand-secondary: var(--color-bg-brand-secondary); + --background-color-secondary: var(--color-bg-secondary); + --background-color-quaternary: var(--color-bg-quaternary); + --background-color-primary_alt: var(--color-bg-primary_alt); + --background-color-brand-primary_alt: var(--color-bg-brand-primary_alt); + --background-color-secondary_alt: var(--color-bg-secondary_alt); + --background-color-overlay: var(--color-bg-overlay); + --background-color-brand-section: var(--color-bg-brand-section); + --background-color-brand-section_subtle: var(--color-bg-brand-section_subtle); + --background-color-primary-solid: var(--color-bg-primary-solid); + --background-color-error-solid_hover: var(--color-bg-error-solid_hover); + + --background-color-border-brand: var(--color-border-brand); + --background-color-border-tertiary: var(--color-border-tertiary); + --background-color-border-brand_alt: var(--color-border-brand_alt); + + /* TEXT PROPERTY COLORS */ + --text-color-primary: var(--color-text-primary); + --text-color-tertiary: var(--color-text-tertiary); + --text-color-error-primary: var(--color-text-error-primary); + --text-color-warning-primary: var(--color-text-warning-primary); + --text-color-success-primary: var(--color-text-success-primary); + --text-color-secondary: var(--color-text-secondary); + --text-color-secondary_hover: var(--color-text-secondary_hover); + --text-color-tertiary_hover: var(--color-text-tertiary_hover); + --text-color-brand-secondary: var(--color-text-brand-secondary); + --text-color-placeholder: var(--color-text-placeholder); + --text-color-brand-tertiary: var(--color-text-brand-tertiary); + --text-color-editor-icon-fg: var(--color-text-editor-icon-fg); + --text-color-editor-icon-fg_active: var(--color-text-editor-icon-fg_active); + --text-color-quaternary: var(--color-text-quaternary); + --text-color-brand-primary: var(--color-text-brand-primary); + --text-color-primary_on-brand: var(--color-text-primary_on-brand); + --text-color-secondary_on-brand: var(--color-text-secondary_on-brand); + --text-color-tertiary_on-brand: var(--color-text-tertiary_on-brand); + --text-color-quaternary_on-brand: var(--color-text-quaternary_on-brand); + --text-color-brand-tertiary_alt: var(--color-text-brand-tertiary_alt); + --text-color-error-primary_hover: var(--color-text-error-primary_hover); + --text-color-brand-secondary_hover: var(--color-text-brand-secondary_hover); + --text-color-tooltip-supporting-text: var(--color-tooltip-supporting-text); + + /* BORDER PROPERTY COLORS */ + --border-color-primary: var(--color-border-primary); + --border-color-secondary: var(--color-border-secondary); + --border-color-secondary_alt: var(--color-border-secondary_alt); + --border-color-tertiary: var(--color-border-tertiary); + --border-color-error: var(--color-border-error); + --border-color-error_subtle: var(--color-border-error_subtle); + --border-color-brand: var(--color-border-brand); + --border-color-brand_alt: var(--color-border-brand_alt); + --border-color-brand-solid: var(--color-bg-brand-solid); + --border-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + + /* RING PROPERTY COLORS */ + --ring-color-primary: var(--color-border-primary); + --ring-color-secondary: var(--color-border-secondary); + --ring-color-secondary_alt: var(--color-border-secondary_alt); + --ring-color-tertiary: var(--color-border-tertiary); + --ring-color-error: var(--color-border-error); + --ring-color-error_subtle: var(--color-border-error_subtle); + --ring-color-brand: var(--color-border-brand); + --ring-color-brand-solid: var(--color-bg-brand-solid); + --ring-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + --ring-color-brand_alt: var(--color-border-brand_alt); + + /* OUTLINE PROPERTY COLORS */ + --outline-color-primary: var(--color-border-primary); + --outline-color-secondary: var(--color-border-secondary); + --outline-color-secondary_alt: var(--color-border-secondary_alt); + --outline-color-tertiary: var(--color-border-tertiary); + --outline-color-error: var(--color-border-error); + --outline-color-error_subtle: var(--color-border-error_subtle); + --outline-color-brand: var(--color-border-brand); + --outline-color-brand-solid: var(--color-bg-brand-solid); + --outline-color-brand-solid_hover: var(--color-bg-brand-solid_hover); + --outline-color-brand_alt: var(--color-border-brand_alt); + } +} \ No newline at end of file diff --git a/apps/www/src/usage/Addaccount.tsx b/apps/www/src/usage/Addaccount.tsx index 2654a16..5d866e4 100644 --- a/apps/www/src/usage/Addaccount.tsx +++ b/apps/www/src/usage/Addaccount.tsx @@ -1,5 +1,5 @@ import React from "react"; -import AWSOnboardModal from "../components/docs/Addaccount"; +import AWSOnboardModal from "../components/docs/ts/Addaccount"; export default function AddaccountUsage() { return ( diff --git a/apps/www/src/usage/Button.tsx b/apps/www/src/usage/Button.tsx index 67454d0..ef39e87 100644 --- a/apps/www/src/usage/Button.tsx +++ b/apps/www/src/usage/Button.tsx @@ -1,5 +1,5 @@ import React from "react"; -import NoiseBackgroundDemo from "../components/docs/Button"; +import NoiseBackgroundDemo from "../components/docs/ts/Button"; export default function ButtonUsage() { return ( diff --git a/apps/www/src/usage/Cards.tsx b/apps/www/src/usage/Cards.tsx deleted file mode 100644 index 49c1471..0000000 --- a/apps/www/src/usage/Cards.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import Card from '../components/docs/Cards'; -import { Home } from 'lucide-react'; - -const CardsUsage = () => { - return ( - <> - } - number="$45,231.89" - sidenumber="+20.1%" - info="from last month" - textcolor="text-green-500" - /> - - ); -}; - -export default CardsUsage; diff --git a/apps/www/src/usage/Chatbot.tsx b/apps/www/src/usage/Chatbot.tsx index 0c6600c..febf710 100644 --- a/apps/www/src/usage/Chatbot.tsx +++ b/apps/www/src/usage/Chatbot.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Chatbot from '../components/docs/Chatbot'; +import Chatbot from '../components/docs/ts/Chatbot'; const ChatbotUsage = () => { return ( diff --git a/apps/www/src/usage/DataViewPanel.tsx b/apps/www/src/usage/DataViewPanel.tsx index 5d092df..5fac63d 100644 --- a/apps/www/src/usage/DataViewPanel.tsx +++ b/apps/www/src/usage/DataViewPanel.tsx @@ -1,5 +1,5 @@ import React from "react"; -import DataViewPanel from "../components/docs/DataViewPanel"; +import DataViewPanel from "../components/docs/ts/DataViewPanel"; export default function DataViewPanelUsage() { return ( diff --git a/apps/www/src/usage/Filterchip.tsx b/apps/www/src/usage/Filterchip.tsx index ce02dd5..798bde2 100644 --- a/apps/www/src/usage/Filterchip.tsx +++ b/apps/www/src/usage/Filterchip.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { FilterChip } from '../components/docs/Filterchip'; +import { FilterChip } from '../components/docs/ts/Filterchip'; interface Filters { status: string[]; diff --git a/apps/www/src/usage/Filters.tsx b/apps/www/src/usage/Filters.tsx index 9a8d035..fa917e3 100644 --- a/apps/www/src/usage/Filters.tsx +++ b/apps/www/src/usage/Filters.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { Upgradecluster } from '../components/docs/Filters'; +import { Upgradecluster } from '../components/docs/ts/Filters'; const FiltersUsage = () => { return ( diff --git a/apps/www/src/usage/Header.tsx b/apps/www/src/usage/Header.tsx index a6a6ac5..227c61d 100644 --- a/apps/www/src/usage/Header.tsx +++ b/apps/www/src/usage/Header.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import Header from '../components/docs/Header'; +import Header from '../components/docs/ts/Header'; import { Button } from '@workspace/ui/components/ui/button'; import { Plus } from 'lucide-react'; diff --git a/apps/www/src/usage/MultipleFilter.tsx b/apps/www/src/usage/MultipleFilter.tsx index 9a77160..f0d407c 100644 --- a/apps/www/src/usage/MultipleFilter.tsx +++ b/apps/www/src/usage/MultipleFilter.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import Upgradefilter from '../components/docs/MultipleFilter'; +import Upgradefilter from '../components/docs/ts/MultipleFilter'; const MultipleFilterUsage = () => { const [filters, setFilters] = useState({ diff --git a/apps/www/src/usage/Pagination.tsx b/apps/www/src/usage/Pagination.tsx index b991f0c..e44612b 100644 --- a/apps/www/src/usage/Pagination.tsx +++ b/apps/www/src/usage/Pagination.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import PaginationControls from '../components/docs/Pagination'; +import PaginationControls from '../components/docs/ts/Pagination'; const PaginationUsage = () => { const [currentPage, setCurrentPage] = useState(1); diff --git a/apps/www/src/usage/Sidebar.tsx b/apps/www/src/usage/Sidebar.tsx index 50c6f3f..27f7208 100644 --- a/apps/www/src/usage/Sidebar.tsx +++ b/apps/www/src/usage/Sidebar.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { Sidebar } from '../components/docs/Sidebar'; +import { Sidebar } from '../components/docs/ts/Sidebar'; const SidebarUsage = () => { const [isOpen, setIsOpen] = useState(true); diff --git a/apps/www/src/usage/Skeleton.tsx b/apps/www/src/usage/Skeleton.tsx index f76ac19..24a17c5 100644 --- a/apps/www/src/usage/Skeleton.tsx +++ b/apps/www/src/usage/Skeleton.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import Skeleton from '../components/docs/Skeleton' +import Skeleton from '../components/docs/ts/Skeleton' const SkeletonUsage = () => { const [isLoading, setIsLoading] = useState(true) diff --git a/apps/www/src/usage/Table.tsx b/apps/www/src/usage/Table.tsx index 44ff42a..5b4c159 100644 --- a/apps/www/src/usage/Table.tsx +++ b/apps/www/src/usage/Table.tsx @@ -1,7 +1,7 @@ import React from "react" import { FolderKanban, UserRoundPen } from 'lucide-react'; import { useEffect, useState } from 'react' -import Table from '../components/docs/Table'; +import Table from '../components/docs/ts/Table'; const DempTable = () => { const [accounts, setAccounts] = useState([]); @@ -118,6 +118,31 @@ const DempTable = () => { const columns = [ { key: "name", label: "Cluster Name" }, + { + key: "provider", + label: "Provider", + render: (val: string) => { + const providerVal = String(val || "").toLowerCase(); + if (providerVal.includes('eks') || providerVal.includes('aws')) { + return ( + + AWS + + ); + } else if (providerVal.includes('azure')) { + return ( + + Azure + + ); + } + return val ? ( + + {val} + + ) : "—"; + } + }, { key: "provider_cluster_id", label: "Cluster ID" }, { key: "current_version", label: "Current Version" }, { diff --git a/apps/www/src/usage/Toggle.tsx b/apps/www/src/usage/Toggle.tsx index 3528e31..1fe481c 100644 --- a/apps/www/src/usage/Toggle.tsx +++ b/apps/www/src/usage/Toggle.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Toggle } from '../components/docs/Toggle'; +import { Toggle } from '../components/docs/ts/Toggle'; const ToggleUsage = () => { return ( diff --git a/apps/www/template/page.tsx b/apps/www/template/page.tsx index 60afde1..974fc1a 100644 --- a/apps/www/template/page.tsx +++ b/apps/www/template/page.tsx @@ -6,7 +6,7 @@ import axios from "axios" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { TableScehma, TableSchema } from "../src/lib/Schema" -import Navbar from "@/components/navbar/Navbar" +// import Navbar from "@/src/components/navbar/Navbar" const CATEGORIES = [ { id: "ui", label: "UI Component", icon: Zap, color: "text-blue-400" }, @@ -53,7 +53,7 @@ const Template = () => { return (
- + {/* */}
void; - emptyState?: any; - onRowClick?: (row: any) => void; - className?: string; - expandable?: boolean; - renderExpandedContent?: (row: any) => React.ReactNode; - rowKey?: (row: any, index: number) => string | number; -} diff --git a/packages/cli/src/command/add.ts b/packages/cli/src/command/add.ts index f8e3f97..a6b585a 100644 --- a/packages/cli/src/command/add.ts +++ b/packages/cli/src/command/add.ts @@ -78,6 +78,11 @@ async function fetchManifest(name: string): Promise { if (!res.ok) { throw new Error(`Component "${name}" not found in registry (HTTP ${res.status}).`) } + const contentType = res.headers.get("content-type") + if (!contentType || !contentType.includes("application/json")) { + const text = await res.text() + throw new Error(`Expected JSON response for component manifest "${name}", but got content-type "${contentType}" with body: ${text.slice(0, 100)}`) + } return res.json() } diff --git a/packages/cli/src/utils/registry.ts b/packages/cli/src/utils/registry.ts index 9e34ba6..2369746 100644 --- a/packages/cli/src/utils/registry.ts +++ b/packages/cli/src/utils/registry.ts @@ -79,6 +79,12 @@ export async function fetchRegistryIndex(): Promise { const url = constructUrl("registry.json") const response = await fetch(url) if (!response.ok) throw new Error(`Failed to fetch registry index: ${response.statusText}`) + + const contentType = response.headers.get("content-type") + if (!contentType || !contentType.includes("application/json")) { + const text = await response.text() + throw new Error(`Expected JSON response from registry, but got content-type "${contentType}" with body: ${text.slice(0, 100)}`) + } return await response.json() as RegistryIndex } catch (error) { throw new Error(`Failed to fetch registry index: ${error}`) @@ -100,6 +106,12 @@ export async function fetchRegistryItem(name: string, style: string = "default") const url = constructUrl(`components/${name}.json`) const response = await fetch(url) if (!response.ok) throw new Error(`Failed to fetch component: ${name}`) + + const contentType = response.headers.get("content-type") + if (!contentType || !contentType.includes("application/json")) { + const text = await response.text() + throw new Error(`Expected JSON response for component "${name}", but got content-type "${contentType}" with body: ${text.slice(0, 100)}`) + } return await response.json() as RegistryItem } catch (error) { throw new Error(`Failed to fetch component ${name}: ${error}`) diff --git a/packages/ui/src/components/ui/tabs.tsx b/packages/ui/src/components/ui/tabs.tsx index 026d1a5..b2841c9 100644 --- a/packages/ui/src/components/ui/tabs.tsx +++ b/packages/ui/src/components/ui/tabs.tsx @@ -12,7 +12,7 @@ const TabsList = React.forwardRef<