0.1.0 • Published 5 months ago

@mhmdhammoud/meritt-dev-sdk v0.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

@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

# 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.

# 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:

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

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:

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:

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.

<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.

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.

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

Content Hooks

useProducts(options?)

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?)

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()

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:

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:

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:

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:

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:

    cp node_modules/@meritt-dev/sdk/firestore.indexes.json .
  2. Deploy the indexes:

    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
0.1.0

5 months ago