npm package: @mhmdhammoud/meritt-dev-sdk
Description: SDK for Meritt Engine content management with Firebase integration
Author: Meritt Dev Team
Latest version: 0.1.0

# @meritt-dev/sdk

A powerful SDK for Meritt Engine content management with Firebase integration. This SDK provides a seamless interface for managing content, media, and more in your Meritt Engine projects.

## Features

- ✅ Direct Firebase integration
- ✅ Type-safe API with TypeScript
- ✅ React hooks for easy integration
- ✅ Zustand stores for state management
- ✅ Comprehensive content types (Products, Articles, Portfolio Projects)
- ✅ Media management with upload progress
- ✅ Built-in pagination and filtering
- ✅ Provider-based architecture for clean integration

## Installation

```bash
# Using npm
npm install @meritt-dev/sdk

# Using yarn
yarn add @meritt-dev/sdk

# Using pnpm
pnpm add @meritt-dev/sdk
```

## Prerequisites

The SDK requires Firebase as a peer dependency, and React for hooks functionality.

```bash
# For React integration
npm install react react-dom

# Firebase is included as a dependency
```

## Quick Start

### Setup with Provider

Wrap your application with the `MerittProvider` at the root level:

```tsx
import { MerittProvider } from '@meritt-dev/sdk';

function App() {
  return (
    <MerittProvider
      config={{
        apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
        authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
        projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
        storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET!,
        messagingSenderId:
          process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID!,
        appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID!,
      }}
    >
      <YourAppContent />
    </MerittProvider>
  );
}
```

### Using Content Hooks

#### Products

```tsx
import { useProducts } from '@meritt-dev/sdk';

function ProductsList() {
  const { products, loading, error, loadMore } = useProducts({
    featured: true,
    limit: 10,
  });

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

  return (
    <div>
      <h2>Featured Products</h2>
      <div className="grid">
        {products.map((product) => (
          <div key={product.id} className="product-card">
            <img src={product.images[0]} alt={product.name} />
            <h3>{product.name}</h3>
            <p>${product.price.toFixed(2)}</p>
          </div>
        ))}
      </div>
      <button onClick={loadMore}>Load More</button>
    </div>
  );
}
```

#### Media Management

Use the `useMedia` hook to fetch and manage media assets:

```tsx
import { useMedia } from '@meritt-dev/sdk';

function MediaGallery() {
  const { media, loading, uploadMedia, isUploading, progress } = useMedia({
    fileType: 'image',
    limit: 20,
  });

  const handleFileChange = async (event) => {
    const file = event.target.files[0];
    if (file) {
      try {
        const result = await uploadMedia(file, {
          folder: 'products',
          alt: 'Product image',
        });
        console.log('Uploaded successfully:', result.downloadUrl);
      } catch (error) {
        console.error('Upload failed:', error);
      }
    }
  };

  return (
    <div>
      <h2>Media Gallery</h2>

      <div className="upload-section">
        <input type="file" onChange={handleFileChange} />
        {isUploading && (
          <div className="progress-bar">
            <div className="progress" style={{ width: `${progress}%` }}></div>
            <span>{progress.toFixed(0)}%</span>
          </div>
        )}
      </div>

      <div className="media-grid">
        {media.map((item) => (
          <div key={item.id} className="media-item">
            <img src={item.thumbnailUrl} alt={item.alt || item.fileName} />
            <p>{item.fileName}</p>
          </div>
        ))}
      </div>
    </div>
  );
}
```

#### Direct File Upload

If you only need file upload functionality without creating media records:

```tsx
import { useUpload } from '@meritt-dev/sdk';

function FileUploader() {
  const { uploadFile, isUploading, progress, downloadUrl } = useUpload();

  const handleFileChange = async (event) => {
    const file = event.target.files[0];
    if (file) {
      try {
        const result = await uploadFile(file, { folder: 'documents' });
        console.log('Download URL:', result.downloadUrl);
      } catch (error) {
        console.error('Upload error:', error);
      }
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      {isUploading && <progress value={progress} max="100" />}
      {downloadUrl && <div>File uploaded: {downloadUrl}</div>}
    </div>
  );
}
```

## API Reference

### Provider

#### `MerittProvider`

The main provider component that initializes Firebase and provides context to all child components.

```tsx
<MerittProvider
  config={{
    apiKey: string,
    authDomain: string,
    projectId: string,
    storageBucket: string,
    messagingSenderId: string,
    appId: string,
  }}
  namespace?: string // Optional namespace for multiple instances
>
  <App />
</MerittProvider>
```

#### `useMerittContext()`

Hook to access the Meritt context within components.

```tsx
const {
  config, // Firebase configuration
  namespace, // Optional namespace
  isInitialized, // Boolean indicating if Firebase is ready
  firebase, // Firebase instances (db, auth, storage)
  error, // Initialization error if any
} = useMerittContext();
```

#### `useFirebaseInstances()`

Hook to directly access Firebase instances.

```tsx
const {
  db, // Firestore database
  auth, // Firebase Auth
  storage, // Firebase Storage
} = useFirebaseInstances();
```

### Content Hooks

#### `useProducts(options?)`

```tsx
const {
  products, // Array of products
  loading, // Boolean loading state
  error, // Error object if any
  fetchProduct, // Function to fetch a single product
  loadMore, // Function to load more products
  createProduct, // Function to create a new product
  updateProduct, // Function to update a product
  deleteProduct, // Function to delete a product
} = useProducts({
  featured: boolean, // Filter by featured status
  inStock: boolean, // Filter by stock status
  categoryId: string, // Filter by category ID
  limit: number, // Number of items to fetch
  offset: number, // Offset for pagination
});
```

#### `useMedia(options?)`

```tsx
const {
  media,             // Array of media items
  loading,           // Boolean loading state
  error,             // Error object if any

  // Upload state
  isUploading,       // Boolean upload state
  progress,          // Upload progress (0-100)
  downloadUrl,       // URL of the last uploaded file
  uploadError,       // Upload error if any

  // Operations
  uploadMedia,       // Function to upload a file and create a media record
  getMediaById,      // Function to fetch a single media item
  loadMore,          // Function to load more media items
  updateMedia,       // Function to update a media item
  deleteMedia,       // Function to delete a media item
  resetUploadState,  // Function to reset upload state
} = useMedia({
  fileType?: string | string[],  // Filter by file type
  folder?: string,               // Filter by folder
  tags?: string[],               // Filter by tags
  limit?: number,                // Number of items to fetch
  offset?: number,               // Offset for pagination
});
```

#### `useUpload()`

```tsx
const {
  isUploading, // Boolean upload state
  progress, // Upload progress (0-100)
  downloadUrl, // URL of the last uploaded file
  error, // Upload error if any
  uploadFile, // Function to upload a file
  resetUploadState, // Function to reset upload state
} = useUpload();
```

### Type Definitions

The SDK exports TypeScript interfaces for all content types:

- `Product` - E-commerce product
- `Article` - Blog article
- `PortfolioProject` - Portfolio project
- `Media` - Media asset
- `Category` - Content category
- `Order` - E-commerce order

Refer to the [Types Documentation](#) for detailed type definitions.

## Advanced Usage

### Multiple Instances

You can use multiple provider instances for multi-tenant applications:

```tsx
import { MerittProvider } from '@meritt-dev/sdk';

function MultiTenantApp() {
  return (
    <div>
      <MerittProvider config={tenant1Config} namespace="tenant1">
        <TenantDashboard tenantId="tenant1" />
      </MerittProvider>

      <MerittProvider config={tenant2Config} namespace="tenant2">
        <TenantDashboard tenantId="tenant2" />
      </MerittProvider>
    </div>
  );
}
```

### Using Stores Directly

You can use the Zustand stores directly for more advanced use cases:

```tsx
import {
  useProductStore,
  useMediaStore,
  useFirebaseInstances,
} from '@meritt-dev/sdk';

function AdvancedComponent() {
  const firebase = useFirebaseInstances();
  const products = useProductStore((state) => state.products);
  const fetchProducts = useProductStore((state) => state.fetchProducts);

  // Use store methods with Firebase instances
  const loadProducts = async () => {
    await fetchProducts({ limit: 20 }, firebase);
  };

  return <div>Advanced component content</div>;
}
```

### Custom Firebase Queries

For advanced use cases, you can access the Firebase instances directly:

```tsx
import { useFirebaseInstances } from '@meritt-dev/sdk';
import { collection, query, where, getDocs } from 'firebase/firestore';

function CustomQueryComponent() {
  const { db } = useFirebaseInstances();

  const customQuery = async () => {
    const productsRef = collection(db, 'products');
    const q = query(productsRef, where('price', '<', 50));
    const snapshot = await getDocs(q);
    // Process results...
  };

  return <div>Custom query component</div>;
}
```

### Testing

For testing, you can provide mock Firebase configurations:

```tsx
import { MerittProvider } from '@meritt-dev/sdk';

function TestWrapper({ children }) {
  const mockConfig = {
    apiKey: 'test-api-key',
    authDomain: 'test.firebaseapp.com',
    projectId: 'test-project',
    storageBucket: 'test-project.appspot.com',
    messagingSenderId: '123456789',
    appId: 'test-app-id',
  };

  return <MerittProvider config={mockConfig}>{children}</MerittProvider>;
}
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Support

If you have any questions or need help integrating the SDK, please open an issue on the GitHub repository or contact us at support@meritt.dev.

## Firestore Indexes

The SDK requires specific Firestore indexes for optimal query performance. A `firestore.indexes.json` file is included with the SDK.

### Setup

1. **Copy the indexes file** to your Firebase project root:

   ```bash
   cp node_modules/@meritt-dev/sdk/firestore.indexes.json .
   ```

2. **Deploy the indexes**:

   ```bash
   firebase deploy --only firestore:indexes
   ```

3. **Wait for indexes to build** (usually 2-5 minutes)

### Prerequisites

- Firebase CLI installed: `npm install -g firebase-tools`
- Firebase project initialized: `firebase init`
- Logged in to Firebase: `firebase login`

### Troubleshooting

If you get index-related errors:

1. Ensure the `firestore.indexes.json` file is in your project root
2. Check the Firebase Console for index build status
3. Wait for indexes to finish building before testing queries