npm package: @prodeegi/scribe
Description: Frontend SDK for Prodeegi CMS - Type-safe content client for React and Next.js
Author: Prodeegi Team
Latest version: 1.1.4

# @prodeegi/scribe

Frontend SDK for Prodeegi CMS - Type-safe content client for React and Next.js.

## Installation

```bash
npm install @prodeegi/scribe
```

## Quick Start

### 1. Wrap your app with ProdegiProvider

```tsx
import { ProdegiProvider } from '@prodeegi/scribe';

function App() {
  return (
    <ProdegiProvider
      config={{
        siteId: 'site_xxx',
        publicApiKey: 'pk_xxx',
        environment: 'production'
      }}
    >
      <YourApp />
    </ProdegiProvider>
  );
}
```

### 2. Use hooks to fetch content

```tsx
import { usePage } from '@prodeegi/scribe';

function HomePage() {
  const { data: page, loading, error } = usePage('home');

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>{page.sections.hero.title}</h1>
      <p>{page.sections.hero.subtitle}</p>
    </div>
  );
}
```

## API Reference

### Configuration

```typescript
interface ProdegiConfig {
  siteId: string;           // Your site ID from Prodeegi CMS
  publicApiKey: string;     // Your public API key (starts with pk_)
  environment: 'production' | 'preview';
  baseUrl?: string;         // Optional: Custom API base URL
}
```

### React Hooks

#### `usePage(pageKey: string)`

Fetch an entire page's content.

```tsx
const { data, loading, error, refetch } = usePage('home');
```

**Returns:**
- `data`: Page content object with sections
- `loading`: Boolean loading state
- `error`: Error object if request failed
- `refetch`: Function to manually refetch data

#### `useSection(pageKey: string, sectionKey: string, options?)`

Fetch a specific section from a page.

```tsx
const { data, loading, error } = useSection('home', 'hero', {
  initialValues: {
    title: 'Welcome',
    subtitle: 'Loading...'
  },
  fallback: {
    title: 'Default Title'
  }
});
```

**Options:**
- `initialValues`: Values to use while loading
- `fallback`: Values to use if section not found

#### `useField(path: string)`

Fetch a specific field value using dot notation.

```tsx
const { value, loading, error } = useField('home.hero.title');
```

**Path format:** `pageKey.sectionKey.fieldKey`

#### `useCollection(collectionKey: string, options?)`

Fetch a list of items from a repeatable collection (e.g. products, blog posts).

```tsx
const { items, loading, error } = useCollection('products', {
  search: 'shoes',
  filter: { category: 'running' }
});
```

**Options:**
- `search`: String to search against searchable fields
- `filter`: Object for exact match filtering (JSONB)

#### `useItem(collectionKey: string, slug: string)`

Fetch a single item from a collection by its slug.

```tsx
const { item, loading, error } = useItem('products', 'nike-air-max');
```

#### `usePreview()`

Manage preview mode for viewing draft content.

```tsx
const { enabled, enablePreview, disablePreview } = usePreview();

// Enable preview mode
enablePreview('preview_token_xxx');

// Disable preview mode
disablePreview();
```

### Core Client (Advanced)

For non-React usage or advanced scenarios:

```typescript
import { createClient, createConfig } from '@prodeegi/scribe';

const config = createConfig({
  siteId: 'site_xxx',
  publicApiKey: 'pk_xxx',
  environment: 'production'
});

const client = createClient(config);

// Fetch page
const page = await client.getPage('home');

// Fetch section
const section = await client.getSection('home', 'hero');

// Fetch field
const value = await client.getField('home.hero.title');

// Enable preview
client.enablePreview('preview_token_xxx');
```

## TypeScript Support

Scribe is written in TypeScript and provides full type definitions.

```typescript
import type {
  ProdegiConfig,
  PageContent,
  SectionData,
  FieldValue,
  ContentSchema
} from '@prodeegi/scribe';
```

## Preview Mode

Preview mode allows you to view draft content before publishing.

### In React

```tsx
import { usePreview } from '@prodeegi/scribe';

function PreviewBanner() {
  const { enabled, disablePreview } = usePreview();

  if (!enabled) return null;

  return (
    <div className="preview-banner">
      🔍 Preview Mode Active
      <button onClick={disablePreview}>Exit Preview</button>
    </div>
  );
}
```

### Enabling Preview

```tsx
// Get preview token from URL or CMS
const searchParams = new URLSearchParams(window.location.search);
const previewToken = searchParams.get('preview');

if (previewToken) {
  enablePreview(previewToken);
}
```

## Next.js Integration

### App Router

```tsx
// app/layout.tsx
import { ProdegiProvider } from '@prodeegi/scribe';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ProdegiProvider
          config={{
            siteId: process.env.NEXT_PUBLIC_PRODEEGI_SITE_ID!,
            publicApiKey: process.env.NEXT_PUBLIC_PRODEEGI_API_KEY!,
            environment: process.env.NODE_ENV === 'production' ? 'production' : 'preview'
          }}
        >
          {children}
        </ProdegiProvider>
      </body>
    </html>
  );
}
```

### Pages Router

```tsx
// pages/_app.tsx
import { ProdegiProvider } from '@prodeegi/scribe';

export default function App({ Component, pageProps }) {
  return (
    <ProdegiProvider
      config={{
        siteId: process.env.NEXT_PUBLIC_PRODEEGI_SITE_ID!,
        publicApiKey: process.env.NEXT_PUBLIC_PRODEEGI_API_KEY!,
        environment: 'production'
      }}
    >
      <Component {...pageProps} />
    </ProdegiProvider>
  );
}
```

## Environment Variables

Create a `.env.local` file:

```env
NEXT_PUBLIC_PRODEEGI_SITE_ID=site_xxx
NEXT_PUBLIC_PRODEEGI_API_KEY=pk_xxx
```

## Error Handling

Scribe follows a fail-safe philosophy:

- **Development**: Errors logged to console with detailed messages
- **Production**: Silent failures with console warnings, no crashes
- **Missing content**: Returns `null` instead of throwing
- **Invalid config**: Throws clear error messages

## Best Practices

1. **Use ProdegiProvider at the root** of your app
2. **Use environment variables** for sensitive config
3. **Handle loading states** in your UI
4. **Provide fallback content** for better UX
5. **Use TypeScript** for type safety

## Examples

### Feature List

```tsx
function FeaturesSection() {
  const { data: features } = useSection('home', 'features');

  return (
    <div className="features-grid">
      {features?.items?.map((item, index) => (
        <div key={index} className="feature">
          {item.icon && <img src={item.icon.url} alt={item.icon.alt} />}
          <h3>{item.heading}</h3>
          <p>{item.body}</p>
        </div>
      ))}
    </div>
  );
}
```

### Testimonials

```tsx
function TestimonialsSection() {
  const { data: testimonials } = useSection('home', 'testimonials');

  return (
    <div className="testimonials">
      {testimonials?.items?.map((item, index) => (
        <blockquote key={index}>
          <p>"{item.quote}"</p>
          <footer>
            {item.avatar && <img src={item.avatar.url} alt={item.author} />}
            <cite>{item.author}</cite>
            {item.role && <span>{item.role}</span>}
          </footer>
        </blockquote>
      ))}
    </div>
  );
}
```

### Collections (Products)

```tsx
function ProductGrid() {
  const { items, loading } = useCollection('products', { search: 'premium' });

  if (loading) return <div>Loading...</div>;

  return (
    <div className="product-grid">
      {items.map(product => (
        <div key={product.itemSlug} className="product-card">
          <img src={product.data.image?.url} alt={product.data.name} />
          <h3>{product.data.name}</h3>
          <p>{product.data.price}</p>
        </div>
      ))}
    </div>
  );
}
```

## License

MIT

## Icon Component

The SDK includes a powerful `Icon` component that supports **Tabler Icons** (`react-icons/tb`) and **Social Icons** (FontAwesome 6 - `react-icons/fa6`).

It uses **Dynamic Imports** to ensure you only load the icon libraries you actually use, keeping your bundle size small.

```tsx
import { Icon } from '@prodeegi/scribe/react';

function MyComponent() {
  return (
    <div>
      {/* Tabler Icon (Default) */}
      <Icon name="TbActivity" className="w-6 h-6" />
      
      {/* Social Icon (FontAwesome) - Prefix with 'fa/' */}
      <Icon name="fa/FaGithub" className="w-6 h-6" />
    </div>
  );
}
```

> **Note**: The exact icon name string ("TbActivity", "fa/FaGithub") comes directly from the CMS API response when using `icon` or `social-icon` fields.

## License

MIT

## Support

For issues and questions, visit: https://github.com/prodeegi/vault/issues