npm package: @kodeme-io/next-core-storage-react
Description: React hooks for @kodeme-io/next-core-storage
Author: ABC Food Development Team
Latest version: 0.8.4

# @next-core-storage-react

React hooks for the enhanced Next.js storage package with progress tracking, smart compression, and batch upload capabilities.

## Installation

```bash
npm install @kodeme-io/next-core-storage-react @kodeme-io/next-core-storage react
```

## Quick Start

### Basic Upload with Progress

```tsx
import { useUpload } from '@kodeme-io/next-core-storage-react'
import { createS3Client } from '@kodeme-io/next-core-storage'

function UploadComponent() {
  const client = createS3Client({
    endpoint: process.env.S3_ENDPOINT_URL!,
    accessKeyId: process.env.S3_ACCESS_KEY!,
    secretAccessKey: process.env.S3_SECRET_KEY!,
    bucket: process.env.S3_BUCKET_NAME!,
  })

  const {
    upload,
    progress,
    isUploading,
    error,
    result
  } = useUpload(client, process.env.S3_BUCKET_NAME!, {
    onSuccess: (result) => console.log('Upload complete:', result.url),
    onError: (error) => console.error('Upload failed:', error)
  })

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]
    if (!file) return

    try {
      const base64 = await fileToBase64(file)
      await upload(base64, file.name, file.type, 'uploads')
    } catch (error) {
      console.error('Error:', error)
    }
  }

  return (
    <div>
      <input type="file" onChange={handleFileSelect} disabled={isUploading} />

      {isUploading && (
        <div>
          <div>Uploading... {progress?.percentage.toFixed(1)}%</div>
          <div>Speed: {progress?.speed ? (progress.speed / 1024).toFixed(1) : 0} KB/s</div>
          <div>ETA: {progress?.eta ? Math.round(progress.eta) : 0}s</div>
        </div>
      )}

      {error && <div style={{ color: 'red' }}>Error: {error.message}</div>}

      {result && (
        <div>
          <div>Upload complete!</div>
          <a href={result.url} target="_blank" rel="noopener noreferrer">
            View file
          </a>
        </div>
      )}
    </div>
  )
}
```

### Smart Photo Compression

```tsx
import { useSmartCompression } from '@kodeme-io/next-core-storage-react'

function PhotoUpload() {
  const {
    compress,
    isCompressing,
    result,
    originalSize,
    compressedSize,
    compressionRatio,
    format
  } = useSmartCompression({
    onSuccess: (compressedFile) => {
      console.log('Photo compressed:', {
        original: originalSize,
        compressed: compressedSize,
        ratio: compressionRatio,
        format
      })
    }
  })

  const handlePhoto = async (file: File) => {
    try {
      const compressed = await compress(file, {
        smartFormat: true,
        maxWidth: 1920,
        maxHeight: 1080,
        maxSize: 400 * 1024 // 400KB
      })

      // Upload compressed photo
      // await uploadPhoto(compressed)
    } catch (error) {
      console.error('Compression failed:', error)
    }
  }

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={(e) => e.target.files?.[0] && handlePhoto(e.target.files[0])}
        disabled={isCompressing}
      />

      {isCompressing && <div>Compressing photo...</div>}

      {result && (
        <div>
          <div>✅ Compression complete</div>
          <div>Original: {(originalSize / 1024).toFixed(1)} KB</div>
          <div>Compressed: {(compressedSize / 1024).toFixed(1)} KB</div>
          <div>Saved: {compressionRatio.toFixed(1)}%</div>
          <div>Format: {format}</div>
        </div>
      )}
    </div>
  )
}
```

### Batch Upload Queue

```tsx
import { useUploadQueue } from '@kodeme-io/next-core-storage-react'

function BatchUploader() {
  const {
    addJob,
    addBatch,
    pause,
    resume,
    clear,
    status,
    jobs,
    isProcessing
  } = useUploadQueue(client, bucket, {
    maxConcurrency: 3,
    onQueueComplete: (result) => {
      console.log(`Batch complete: ${result.successful.length}/${result.total} successful`)
    }
  })

  const handleMultipleFiles = async (files: FileList) => {
    const uploadJobs = Array.from(files).map(file => ({
      file: await fileToBase64(file),
      fileName: file.name,
      contentType: file.type,
      folder: 'batch-uploads',
      priority: 'normal' as const
    }))

    addBatch(uploadJobs)
  }

  return (
    <div>
      <input
        type="file"
        multiple
        onChange={(e) => e.target.files && handleMultipleFiles(e.target.files)}
      />

      <div>
        <button onClick={pause} disabled={!isProcessing}>Pause</button>
        <button onClick={resume} disabled={isProcessing}>Resume</button>
        <button onClick={clear}>Clear Completed</button>
      </div>

      <div>
        <div>Pending: {status.pending}</div>
        <div>Active: {status.active}</div>
        <div>Completed: {status.completed}</div>
        <div>Failed: {status.failed}</div>
      </div>

      <div>
        {jobs.map(job => (
          <div key={job.id}>
            {job.fileName} - {job.status}
            {job.progress && (
              <span> ({job.progress.percentage.toFixed(1)}%)</span>
            )}
          </div>
        ))}
      </div>
    </div>
  )
}
```

## Hooks Reference

### `useUpload(client, bucket, options?)`

Hook for single file uploads with progress tracking.

**Parameters:**
- `client`: S3Client instance
- `bucket`: S3 bucket name
- `options`: Upload options and callbacks

**Returns:**
```tsx
{
  upload: (file, fileName, contentType, folder?, endpoint?) => Promise<UploadResult>
  cancel: () => void
  reset: () => void
  progress: UploadProgress | null
  isUploading: boolean
  error: Error | null
  result: UploadResult | null
}
```

### `useVisitPhotoUpload(client, bucket, options?)`

Specialized hook for visit photo uploads with organized folder structure.

**Returns:**
```tsx
{
  upload: (base64Data, visitId, photoType, endpoint?) => Promise<VisitPhotoUploadResult>
  cancel: () => void
  reset: () => void
  progress: UploadProgress | null
  isUploading: boolean
  error: Error | null
  result: VisitPhotoUploadResult | null
}
```

### `useSmartCompression(options?)`

Hook for intelligent photo compression with automatic format selection.

**Returns:**
```tsx
{
  compress: (file, options?) => Promise<File>
  reset: () => void
  isCompressing: boolean
  error: Error | null
  result: File | null
  originalSize: number
  compressedSize: number
  compressionRatio: number
  format: string
}
```

### `useUploadQueue(client, bucket, options?)`

Hook for managing multiple concurrent uploads with queue control.

**Returns:**
```tsx
{
  addJob: (job, priority?) => string
  addBatch: (jobs) => string[]
  removeJob: (jobId) => boolean
  cancelJob: (jobId) => boolean
  pause: () => void
  resume: () => void
  clear: () => void
  getJob: (jobId) => UploadJob | undefined
  retryFailedJobs: () => void
  status: QueueStatus
  jobs: UploadJob[]
  isProcessing: boolean
  error: Error | null
}
```

### `useCompression(options?)`

Basic photo compression hook.

**Returns:**
```tsx
{
  compress: (file, options?) => Promise<File>
  reset: () => void
  isCompressing: boolean
  error: Error | null
  result: File | null
  originalSize: number
  compressedSize: number
  compressionRatio: number
}
```

### `useBatchCompression(options?)`

Hook for batch compression operations.

**Returns:**
```tsx
{
  compressBatch: (files, options?) => Promise<File[]>
  compressSmartBatch: (files, options?) => Promise<File[]>
  reset: () => void
  isCompressing: boolean
  error: Error | null
  results: File[]
  progress: number
  total: number
}
```

### `useStorageQuota()`

Hook for browser storage quota information.

**Returns:**
```tsx
{
  quota: StorageQuota | null
  loading: boolean
  error: Error | null
  refresh: () => Promise<StorageQuota | null>
}
```

## Examples

### Photo Upload with Compression and Progress

```tsx
function PhotoUploader() {
  const [file, setFile] = useState<File | null>(null)

  const { compress, isCompressing, result: compressedFile } = useSmartCompression()
  const { upload, progress, isUploading } = useUpload(client, bucket)

  const handleUpload = async () => {
    if (!file) return

    try {
      // Step 1: Compress photo
      const compressed = await compress(file, {
        smartFormat: true,
        maxSize: 500 * 1024 // 500KB
      })

      // Step 2: Upload compressed photo
      const base64 = await fileToBase64(compressed)
      await upload(base64, file.name, compressed.type, 'photos')

    } catch (error) {
      console.error('Upload failed:', error)
    }
  }

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        onChange={(e) => setFile(e.target.files?.[0] || null)}
      />

      <button
        onClick={handleUpload}
        disabled={!file || isCompressing || isUploading}
      >
        {isCompressing ? 'Compressing...' : isUploading ? 'Uploading...' : 'Upload'}
      </button>

      {progress && (
        <div>
          <div>Progress: {progress.percentage.toFixed(1)}%</div>
          <div>Speed: {(progress.speed / 1024).toFixed(1)} KB/s</div>
          <div>ETA: {Math.round(progress.eta)}s</div>
        </div>
      )}
    </div>
  )
}
```

## TypeScript Support

All hooks are fully typed with TypeScript. Import types:

```tsx
import type {
  UseUploadState,
  UseCompressionState,
  UploadProgress,
  CompressionOptions
} from '@kodeme-io/next-core-storage-react'
```

## License

MIT

---

**Built with ❤️ by ABC Food Development Team**