@fluid-app/rep-sdk
v0.1.2
Published
SDK for building custom Fluid rep portals
Keywords
Readme
@fluid-app/rep-sdk
SDK for building custom Fluid rep portals. Provides React hooks, providers, and an API client for integrating with the Fluid Commerce platform.
Installation
npm install @fluid-app/rep-sdk
# or
pnpm add @fluid-app/rep-sdk
# or
bun add @fluid-app/rep-sdkPeer Dependencies
This package requires the following peer dependencies:
{
"react": ">=18.0.0",
"@tanstack/react-query": ">=5.0.0"
}Quick Start
Wrap your application with FluidProvider:
import { FluidProvider } from "@fluid-app/rep-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
useFluidApi
Access the API client for making requests:
import { useFluidApi } from "@fluid-app/rep-sdk";
import { useQuery } from "@tanstack/react-query";
function ProductList() {
const api = useFluidApi();
const { data: products, isLoading } = useQuery({
queryKey: ["products"],
queryFn: () => api.products.list({ active: true }),
});
if (isLoading) return <Spinner />;
return (
<ul>
{products?.map((product) => <li key={product.id}>{product.name}</li>)}
</ul>
);
}useCurrentRep
Fetch the currently authenticated rep's profile:
import { useCurrentRep } from "@fluid-app/rep-sdk";
function RepHeader() {
const { data: rep, isLoading } = useCurrentRep();
if (isLoading) return <Skeleton />;
return (
<div>
<Avatar src={rep?.avatar_url} />
<span>
{rep?.first_name} {rep?.last_name}
</span>
</div>
);
}useFluidProfile
Fetch the rep portal profile (themes, navigation, screens):
import { useFluidProfile } from "@fluid-app/rep-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>
);
}useFluidPermissions
Check user permissions with a convenient can() helper:
import { useFluidPermissions } from "@fluid-app/rep-sdk";
function TeamSettings() {
const { can, isSuperAdmin } = useFluidPermissions();
// Check if user can manage team
if (!can("team", "manage")) {
return <AccessDenied />;
}
return (
<div>
<h1>Team Settings</h1>
{/* Only show delete button if user has delete permission */}
{can("team", "delete") && <DeleteTeamButton />}
</div>
);
}Available permission actions: "view", "create", "update", "delete", "settings", "add", "manage", "send"
useFluidTheme
Control theme settings:
import { useFluidTheme } from "@fluid-app/rep-sdk";
import type { Theme } from "@fluid-app/rep-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>
);
}API Client
The SDK provides a typed API client with methods for common operations:
Products
const api = useFluidApi();
// List products with optional filters
const products = await api.products.list({
active: true,
page: 1,
per_page: 20,
});
// Get a single product
const product = await api.products.get("product-id");
// Search products
const results = await api.products.search("query");Orders
const api = useFluidApi();
// List orders
const orders = await api.orders.list({
status: "pending",
date_from: "2024-01-01",
});
// Get a single order
const order = await api.orders.get("order-id");
// Create an order
const newOrder = await api.orders.create({
customer_id: "customer-id",
line_items: [{ product_id: "product-id", quantity: 2 }],
});Reps
const api = useFluidApi();
// Get current rep profile
const rep = await api.reps.current();
// Update profile
const updated = await api.reps.updateProfile({
first_name: "John",
last_name: "Doe",
});Analytics
const api = useFluidApi();
// Get dashboard data
const dashboard = await api.analytics.dashboard();
// Get sales data
const sales = await api.analytics.sales({
date_from: "2024-01-01",
date_to: "2024-12-31",
group_by: "month",
});Custom Requests
For endpoints not covered by the typed methods:
const api = useFluidApi();
// Generic GET request
const data = await api.get<MyType>("/custom/endpoint", { param: "value" });
// Generic POST request
const result = await api.post<MyType>("/custom/endpoint", { body: "data" });Providers
FluidProvider
The main provider that sets up the SDK. It wraps:
QueryClientProvider(TanStack Query)FluidThemeProvider(theme management)
import { FluidProvider } from "@fluid-app/rep-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:
import { FluidThemeProvider, useFluidTheme } from "@fluid-app/rep-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:
import type {
// Core types
Profile,
Theme,
ThemeConfig,
Navigation,
NavigationItem,
ScreenDefinition,
// Permissions
UserPermissions,
Permissions,
PermissionAction,
ResourcePermissions,
// Rep
Rep,
UpdateRepData,
// API types
Product,
ProductListParams,
Order,
OrderListParams,
CreateOrderData,
DashboardData,
SalesData,
SalesParams,
// Client types
FluidSDKConfig,
FluidClient,
RequestOptions,
} from "@fluid-app/rep-sdk";Query Keys
Query keys are exported for cache invalidation and prefetching:
import {
PROFILE_QUERY_KEY,
PERMISSIONS_QUERY_KEY,
CURRENT_REP_QUERY_KEY,
} from "@fluid-app/rep-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:
import { ApiError, isApiError } from "@fluid-app/rep-sdk";
try {
await api.orders.create(orderData);
} 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
