# @fluid-app/portal-sdk

SDK for building custom Fluid portals. Provides React hooks, providers, and an API client for integrating with the Fluid Commerce platform.

## Installation

```bash
npm install @fluid-app/portal-sdk
# or
pnpm add @fluid-app/portal-sdk
# or
bun add @fluid-app/portal-sdk
```

### Peer Dependencies

This package requires the following peer dependencies:

```json
{
  "react": ">=18.0.0",
  "@tanstack/react-query": ">=5.0.0"
}
```

## Quick Start

Wrap your application with `FluidProvider`:

```tsx
import { FluidProvider } from "@fluid-app/portal-sdk";

function App() {
  return (
    <FluidProvider
      config={{
        baseUrl: "https://api.fluid.app/api",
        getAuthToken: () => localStorage.getItem("fluid_token"),
        onAuthError: () => {
          // Handle 401 errors (e.g., redirect to login)
          window.location.href = "/login";
        },
      }}
    >
      <YourApp />
    </FluidProvider>
  );
}
```

## Hooks

### useFluidProfile

Fetch the portal profile (themes, navigation, screens):

```tsx
import { useFluidProfile } from "@fluid-app/portal-sdk";

function Navigation() {
  const { data: profile, isLoading } = useFluidProfile();

  if (isLoading) return <Spinner />;

  return (
    <nav>
      {profile?.navigation.navigation_items.map((item) => (
        <NavItem key={item.id} item={item} />
      ))}
    </nav>
  );
}
```

### useFluidTheme

Control theme settings:

```tsx
import { useFluidTheme } from "@fluid-app/portal-sdk";
import type { Theme } from "@fluid-app/portal-sdk";

function ThemeSwitcher({ themes }: { themes: Theme[] }) {
  const { currentTheme, setTheme, setThemeMode, mode } = useFluidTheme();

  return (
    <div>
      <select
        value={currentTheme?.name}
        onChange={(e) => {
          const theme = themes.find((t) => t.name === e.target.value);
          if (theme) setTheme(theme);
        }}
      >
        {themes.map((theme) => (
          <option key={theme.name} value={theme.name}>
            {theme.name}
          </option>
        ))}
      </select>

      <button onClick={() => setThemeMode(mode === "dark" ? "light" : "dark")}>
        Toggle {mode === "dark" ? "Light" : "Dark"} Mode
      </button>
    </div>
  );
}
```

## Providers

### FluidProvider

The main provider that sets up the SDK. It wraps:

- `QueryClientProvider` (TanStack Query)
- `FluidThemeProvider` (theme management)

```tsx
import { FluidProvider } from "@fluid-app/portal-sdk";
import { QueryClient } from "@tanstack/react-query";

// Optional: provide your own QueryClient
const queryClient = new QueryClient({
  defaultOptions: {
    queries: { staleTime: 5 * 60 * 1000 },
  },
});

function App() {
  return (
    <FluidProvider
      config={{
        baseUrl: "https://api.fluid.app/api",
        getAuthToken: () => getToken(),
      }}
      queryClient={queryClient}
      initialTheme={defaultTheme}
    >
      <YourApp />
    </FluidProvider>
  );
}
```

### FluidThemeProvider

Can be used standalone if you need theme management without the full SDK:

```tsx
import { FluidThemeProvider, useFluidTheme } from "@fluid-app/portal-sdk";

function App() {
  return (
    <FluidThemeProvider initialTheme={myTheme}>
      <ThemedContent />
    </FluidThemeProvider>
  );
}
```

Theme CSS variables are applied to the document root (or a custom container) with the `--fluid-` prefix.

## Types

All types are exported for use in your application:

```tsx
import type {
  // Core types
  Profile,
  Navigation,
  NavigationItem,
  ScreenDefinition,

  // Client types
  FluidSDKConfig,
  RequestOptions,
} from "@fluid-app/portal-sdk";
```

## Query Keys

Query keys are exported for cache invalidation and prefetching:

```tsx
import { PROFILE_QUERY_KEY } from "@fluid-app/portal-sdk";
import { useQueryClient } from "@tanstack/react-query";

function RefreshButton() {
  const queryClient = useQueryClient();

  const handleRefresh = () => {
    queryClient.invalidateQueries({ queryKey: PROFILE_QUERY_KEY });
  };

  return <button onClick={handleRefresh}>Refresh Profile</button>;
}
```

## Error Handling

The SDK provides an `ApiError` class for structured error handling:

```tsx
import { ApiError, isApiError } from "@fluid-app/portal-sdk";

try {
  await someApiCall();
} catch (error) {
  if (isApiError(error)) {
    console.log("Status:", error.status);
    console.log("Message:", error.message);
    console.log("Data:", error.data); // Server error details
  }
}
```

## License

Private - Fluid Commerce
