> ## Documentation Index
> Fetch the complete documentation index at: https://help.cartble.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cartble plugin architecture patterns and design system

> Understand the modularity rules, Firestore conventions, UI guidelines, and React Query patterns that keep Cartble plugins consistent and maintainable.

Cartble's plugin system is built around a small set of architectural rules that keep each plugin self-contained, visually consistent with the admin, and safe from cross-platform data collisions. Following these patterns ensures your plugin feels like a native part of the platform from day one, and that it remains maintainable as the codebase evolves.

<Note>
  The plugin system is designed exclusively for **admin-side extensions**. Plugins add pages, sidebar navigation, and UI slots inside the Cartble admin shell. They do not modify the customer-facing storefront or the public checkout experience.
</Note>

## Modularity principle

Every plugin is a fully self-contained directory under `src/plugins/[plugin-name]/`. All code that belongs to a plugin — pages, hooks, services, types, utilities, and local components — lives inside that directory. Plugins must not import from each other's directories, and they must not modify files outside their own scope.

This rule enforces clear ownership: if something is inside `src/plugins/your-plugin/`, it belongs to that plugin. If it is outside, the plugin should consume it as a shared resource (UI components, hooks) rather than duplicating it.

## UI consistency

Your plugin's UI should be indistinguishable from the rest of the Cartble admin. Follow these rules to achieve that consistency.

### Use shared UI components

Do not create custom buttons, inputs, or modal dialogs. If an equivalent component already exists in `src/components/ui/`, use it.

| Need                 | Use                                  |
| -------------------- | ------------------------------------ |
| Buttons              | `src/components/ui/Button.tsx`       |
| Text inputs          | `src/components/ui/Input.tsx`        |
| Confirmation dialogs | `src/components/ui/ConfirmModal.tsx` |

### Match the admin design system

Apply the same Tailwind CSS color palette, spacing scale, border radius conventions, and typography that the main admin uses. Do not introduce custom color variables or override global styles. Reference existing admin pages to understand which Tailwind classes are standard.

### Format all prices with `formatPrice`

Every monetary value your plugin displays must be formatted using `formatPrice` from the `useTranslation` hook. This function applies the correct currency symbol and regional number formatting for the platform's locale. Raw number output or custom formatting functions will produce inconsistent results across locales.

```ts theme={null}
import { useTranslation } from '@/src/i18n';

const { formatPrice } = useTranslation();

// Correct
<span>{formatPrice(product.price)}</span>

// Incorrect — bypasses locale-aware formatting
<span>$ {product.price.toFixed(2)}</span>
```

## Firestore data model

Cartble's Firestore database is organized around a platform-scoped hierarchy. Every document your plugin reads or writes must live under the `platforms/{platformId}/` path. Access outside this scope is not permitted.

### Collection conventions

| Data type                      | Firestore path                               | When to use                                               |
| ------------------------------ | -------------------------------------------- | --------------------------------------------------------- |
| Products and primary assets    | `platforms/{platformId}/resources/`          | When your plugin reads or updates product catalog entries |
| Orders, transactions, and logs | `platforms/{platformId}/records/`            | When your plugin records financial or operational events  |
| Plugin-private data            | `platforms/{platformId}/[plugin_name]_data/` | For data that only your plugin reads and writes           |

When writing directly to the `resources` collection, your documents must conform to the platform's existing resource schema. Do not add fields that conflict with or shadow existing resource properties.

## Platform awareness

All data access must be scoped to the platform the current admin session belongs to. Use the `usePlatform()` hook to retrieve the `platformId` and any platform-level settings at runtime. Never construct a Firestore path using a hardcoded string.

```ts theme={null}
import { usePlatform } from '@/src/hooks/usePlatform';

const { platformId } = usePlatform();
const path = `platforms/${platformId}/your_plugin_data`;
```

Hardcoded platform IDs will work in development but silently corrupt data in production when multiple platforms share the same deployment.

## React Query patterns

Use React Query as the standard for managing server-side state in your plugin. Your plugin's `withBridge` HOC must provide its own `QueryClient` instance so that the plugin's cache is entirely isolated from the main application.

Always include `platformId` as the first element of every query key. This prevents stale data from one platform appearing in another platform's admin when a user switches accounts within the same session.

```ts theme={null}
// Correct — platformId scopes the cache entry
queryKey: [platformId, 'your-plugin', 'settings']

// Incorrect — cache is not scoped, data can leak between platforms
queryKey: ['your-plugin', 'settings']
```

## Plugin type system

Plugins are defined using the `Plugin` interface from `src/plugins/core/types.ts`. The key fields you will work with are:

```ts theme={null}
interface Plugin {
  id: string;                        // Unique identifier, e.g. 'your-plugin'
  name: string;                      // Display name shown in the Plugins Manager
  description?: string;              // Short description shown in the plugin card
  version: string;                   // Semantic version string
  category: PluginCategory;          // 'payment' | 'shipping' | 'marketing' | 'analytics' | 'other'
  isNative?: boolean;                // True for first-party plugins bundled with the platform
  icon?: React.ComponentType<any>;   // Lucide icon shown in the Plugins Manager card
  settingsComponent?: React.ComponentType<any>; // Inline settings panel shown in the Plugins Manager
  adminApps?: AdminApp[];            // Sidebar navigation entries and their page components
  slots?: Partial<Record<SlotID, React.ComponentType<any>>>; // UI slot injections
  hooks?: Partial<Record<HookID, (data: any) => void>>;      // Platform event listeners
  translations?: Record<string, any>;                        // i18n strings
}
```

Define `adminApps` for every page your plugin exposes. Each entry requires a unique `id`, a display `label`, a Lucide `icon`, and a `component` wrapped with your `withBridge` HOC.
