npm package: @osise/api-client
Description: Official TypeScript SDK for the Osise API - connecting consumers with skilled artisans
Author: Osise Team
Latest version: 0.0.13

# @osise/api-client

Official TypeScript SDK for the Osise API - connecting consumers with skilled artisans in Nigeria.

## Installation

```bash
npm install @osise/api-client
# or
yarn add @osise/api-client
# or
pnpm add @osise/api-client
```

## Quick Start

### Basic Usage

```typescript
import { OsiseClient, BrowserTokenStorage } from '@osise/api-client';

// Create client instance
const client = new OsiseClient({
  baseUrl: 'https://api.osise.com',
  storage: new BrowserTokenStorage(), // Persists tokens in localStorage
  onAuthRequired: () => {
    // Handle auth required (e.g., redirect to login)
    window.location.href = '/login';
  },
});

// Request OTP
const otpResponse = await client.auth.requestOtp({
  phoneNumber: '+2348012345678',
  purpose: 'Login',
});

// Verify OTP
const authResponse = await client.auth.verifyOtp({
  phoneNumber: '+2348012345678',
  code: '123456',
  deviceId: 'unique-device-id',
  deviceName: 'iPhone 15',
  deviceType: 'iOS',
});

// Tokens are automatically stored and used for subsequent requests
console.log('Logged in as:', authResponse.data?.user.firstName);
```

### React Integration

```tsx
import { OsiseProvider, useOsiseClient, useConsumerProfile, useMutation } from '@osise/api-client/react';
import { BrowserTokenStorage } from '@osise/api-client';

// Wrap your app with the provider
function App() {
  return (
    <OsiseProvider
      config={{
        baseUrl: 'https://api.osise.com',
        storage: new BrowserTokenStorage(),
        onAuthRequired: () => navigate('/login'),
      }}
    >
      <MyApp />
    </OsiseProvider>
  );
}

// Use hooks in components
function Profile() {
  const { data: profile, isLoading, error } = useConsumerProfile();

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

  return (
    <div>
      <h1>Welcome, {profile?.firstName}!</h1>
      <p>Phone: {profile?.phoneNumber}</p>
    </div>
  );
}

// Use mutations for actions
function CreateJobButton() {
  const client = useOsiseClient();

  const { mutate, isLoading } = useMutation(
    (data) => client.jobs.create(data),
    {
      onSuccess: (job) => {
        console.log('Job created:', job.id);
      },
      onError: (error) => {
        alert(error.message);
      },
    }
  );

  return (
    <button
      onClick={() => mutate({
        serviceCategory: 'Plumbing',
        title: 'Fix leaky faucet',
        description: 'Kitchen faucet is leaking',
        addressId: 'addr-123',
      })}
      disabled={isLoading}
    >
      Create Job
    </button>
  );
}
```

### React Native Integration

```typescript
import { OsiseClient, SecureTokenStorage } from '@osise/api-client';
import AsyncStorage from '@react-native-async-storage/async-storage';

// Create secure storage for React Native
const storage = new SecureTokenStorage(AsyncStorage);
await storage.initialize(); // Load tokens from storage

const client = new OsiseClient({
  baseUrl: 'https://api.osise.com',
  storage,
});
```

## API Reference

### Authentication

```typescript
// Request OTP
await client.auth.requestOtp({
  phoneNumber: '+234...',
  purpose: 'Login', // or 'Registration'
  deviceId: 'optional-device-id',
});

// Verify OTP
await client.auth.verifyOtp({
  phoneNumber: '+234...',
  code: '123456',
  deviceId: 'device-id',
  deviceName: 'My Phone',
  deviceType: 'Android',
});

// Register new user
await client.auth.register({
  phoneNumber: '+234...',
  otpCode: '123456',
  firstName: 'John',
  lastName: 'Doe',
  userType: 'Consumer',
  deviceId: 'device-id',
});

// Get current user
await client.auth.getCurrentUser();

// Get active sessions
await client.auth.getSessions();

// Revoke a session
await client.auth.revokeSession('session-id');

// Logout
await client.auth.logout();

// Logout all sessions
await client.auth.logoutAll();
```

### Consumer Features

```typescript
// Profile
await client.consumers.getProfile();
await client.consumers.updateProfile({ firstName: 'Jane' });
await client.consumers.uploadProfilePhoto('https://uploadcare.com/...');

// Addresses
await client.consumerAddresses.getAll();
await client.consumerAddresses.create({
  label: 'Home',
  street: '123 Main St',
  area: 'Lekki',
  city: 'Lagos',
  state: 'Lagos',
  latitude: 6.4541,
  longitude: 3.3947,
});
await client.consumerAddresses.setDefault('address-id');

// Favorites
await client.consumers.getFavorites();
await client.consumers.addFavorite('artisan-id');
await client.consumers.removeFavorite('artisan-id');

// Notification Settings
await client.consumers.getNotificationSettings();
await client.consumers.updateNotificationSettings({
  pushEnabled: true,
  jobUpdates: true,
});

// NDPR Compliance
await client.consumers.exportData();
await client.consumers.requestAccountDeletion({ reason: 'Moving abroad' });
```

### Artisan Features

```typescript
// Profile
await client.artisans.getProfile();
await client.artisans.updateProfile({ bio: 'Experienced plumber' });

// Documents
await client.artisans.getDocuments();
await client.artisans.submitDocument({
  documentType: 'Nin',
  fileUrl: 'https://...',
  fileName: 'nin.pdf',
  fileSize: 1024,
  mimeType: 'application/pdf',
});

// Guarantors
await client.artisans.getGuarantors();
await client.artisans.addGuarantor({
  fullName: 'Jane Smith',
  phoneNumber: '+234...',
  relationship: 'Former Employer',
  occupation: 'Business Owner',
  address: '456 Oak St, Lagos',
});

// Bank Account
await client.artisans.getBankAccount();
await client.artisans.setBankAccount({
  bankCode: '044',
  accountNumber: '0123456789',
  accountName: 'John Doe',
});

// Application Status
await client.artisans.getApplicationStatus();
await client.artisans.getSkillTestSlots();
await client.artisans.bookSkillTest({ slotId: 'slot-id' });

// Availability
await client.artisanAvailability.getStatus();
await client.artisanAvailability.updateStatus({ status: 'Online' });
await client.artisanAvailability.updateWorkingHours({
  workingHours: [
    { dayOfWeek: 'Monday', isWorking: true, startTime: '08:00', endTime: '17:00' },
    // ...
  ],
});
await client.artisanAvailability.updateLocation({ latitude: 6.45, longitude: 3.39 });

// Earnings
await client.artisanEarnings.getSummary();
await client.artisanEarnings.getJobEarnings({ page: 1, pageSize: 20 });
await client.artisanEarnings.getPendingEarnings();
await client.artisanEarnings.getPayouts();

// Performance
await client.artisanPerformance.getRating();
await client.artisanPerformance.getReviews();
await client.artisanPerformance.getMetrics({ period: 'month' });
await client.artisanPerformance.getAlerts();
```

### Jobs

```typescript
// Consumer - Create and manage jobs
await client.jobs.create({
  serviceCategory: 'Electrical',
  title: 'Fix electrical outlet',
  description: 'Outlet in bedroom not working',
  addressId: 'address-id',
  isUrgent: true,
});

await client.jobs.list({ status: 'Pending', page: 1 });
await client.jobs.get('job-id');
await client.jobs.cancel('job-id', { reason: 'Found another solution' });
await client.jobs.getTimeline('job-id');

// Quotes
await client.jobs.getQuote('job-id');
await client.jobs.respondToQuote('job-id', { decision: 'approve' });

// Payments
await client.jobs.initiatePayment('job-id', { method: 'Card' });
await client.jobs.verifyPayment('job-id', 'payment-reference');

// Ratings
await client.jobs.submitRating('job-id', {
  overall: 5,
  quality: 5,
  punctuality: 4,
  professionalism: 5,
  value: 4,
  reviewText: 'Great work!',
});

// Artisan - Handle assigned jobs
await client.artisanJobs.list({ status: 'Assigned' });
await client.artisanJobs.accept('job-id');
await client.artisanJobs.decline('job-id', 'Too far away');
await client.artisanJobs.startEnRoute('job-id');
await client.artisanJobs.markArrived('job-id', { latitude: 6.45, longitude: 3.39 });
await client.artisanJobs.submitQuote('job-id', {
  laborCost: 5000,
  materials: [
    { item: 'Wire', quantity: 2, unitPrice: 500 },
  ],
  estimatedDuration: '2 hours',
});
await client.artisanJobs.startWork('job-id');
await client.artisanJobs.complete('job-id', 'All fixed');
```

### Admin Features

```typescript
// Dashboard
await client.adminDashboard.getStats();

// Artisan Management
await client.adminArtisans.list({ search: 'John', status: 'Active' });
await client.adminArtisans.get('artisan-id');
await client.adminArtisans.suspend('artisan-id', 'Policy violation');
await client.adminArtisans.issueStrike('artisan-id', {
  type: 'QualityIssue',
  reason: 'Poor workmanship reported',
  severity: 'warning',
});

// Application Review
await client.adminApplications.list({ verificationStatus: 'DocumentsReview' });
await client.adminApplications.reviewDocument('artisan-id', 'doc-id', {
  status: 'Approved',
});
await client.adminApplications.process('artisan-id', { action: 'approve' });

// Consumer Management
await client.adminConsumers.list({ accountType: 'Business' });
await client.adminConsumers.block('consumer-id', { reason: 'Fraud' });
await client.adminConsumers.unblock('consumer-id');

// Job Management
await client.adminJobs.list();
await client.adminJobs.assignArtisan('job-id', 'artisan-id');
await client.adminJobs.resolveDispute('job-id', 'Refund issued', 5000);

// Service Categories Management
await client.adminServiceCategories.list();
await client.adminServiceCategories.get('category-id');
await client.adminServiceCategories.create({
  name: 'Landscaping',
  description: 'Garden and outdoor maintenance services',
  displayOrder: 10,
});
await client.adminServiceCategories.update('category-id', {
  isActive: true,
  displayOrder: 5,
});
await client.adminServiceCategories.delete('category-id');
```

### Location Services

```typescript
// Geocode an address to coordinates
const geocodeResult = await client.location.geocode({
  address: '123 Main Street',
  city: 'Lagos',
  state: 'Lagos',
  country: 'Nigeria',
});
console.log('Coordinates:', geocodeResult.data?.latitude, geocodeResult.data?.longitude);

// Reverse geocode coordinates to address
const reverseResult = await client.location.reverseGeocode({
  latitude: 6.5244,
  longitude: 3.3792,
});
console.log('Address:', reverseResult.data?.formattedAddress);

// Calculate distance between two points
const distance = await client.location.calculateDistance({
  originLatitude: 6.5244,
  originLongitude: 3.3792,
  destinationLatitude: 6.4281,
  destinationLongitude: 3.4219,
});
console.log(`Distance: ${distance.data?.distanceKm} km`);
console.log(`Estimated travel time: ${distance.data?.estimatedTravelTimeMinutes} minutes`);

// Get all service areas
const areas = await client.location.getServiceAreas({
  state: 'Lagos',
  city: 'Lekki',
});

// Find nearby service areas
const nearby = await client.location.getNearbyAreas({
  latitude: 6.5244,
  longitude: 3.3792,
  radiusKm: 10,
  limit: 5,
});

// Check if a location is serviced
const serviceCheck = await client.location.checkServiceArea(6.5244, 3.3792);
if (serviceCheck.data?.isServiced) {
  console.log('Location is serviced!');
  console.log('Service Area:', serviceCheck.data.serviceArea);
} else {
  console.log('Location not serviced. Nearby areas:', serviceCheck.data?.nearbyAreas);
}
```

## Error Handling

```typescript
import { OsiseApiError } from '@osise/api-client';

try {
  await client.auth.verifyOtp({ ... });
} catch (error) {
  if (error instanceof OsiseApiError) {
    if (error.isUnauthorized) {
      // Token expired or invalid
    } else if (error.isValidationError) {
      // Validation failed - check error.errors
      console.log(error.errors);
    } else if (error.isRateLimited) {
      // Too many requests
    } else if (error.isNetworkError) {
      // Network issue
    } else if (error.isTimeoutError) {
      // Request timed out
    }

    console.error(error.message, error.statusCode);
  }
}
```

## TypeScript Support

All types are fully exported:

```typescript
import type {
  // Auth
  AuthResponseDto,
  UserProfileDto,
  SessionDto,

  // Consumer
  ConsumerProfileDto,
  AddressDto,

  // Artisan
  ArtisanProfileDto,
  ApplicationStatusDto,
  VerificationStatus,

  // Jobs
  JobRequestDto,
  JobStatus,
  Quote,

  // Location
  LocationServiceAreaDto,
  GeocodeRequestDto,
  GeocodeResponseDto,
  ReverseGeocodeRequestDto,
  ReverseGeocodeResponseDto,
  CalculateDistanceRequestDto,
  CalculateDistanceResponseDto,
  ServiceAreaCheckResultDto,

  // Admin
  AdminServiceCategoryDto,
  CreateServiceCategoryDto,
  UpdateServiceCategoryDto,

  // Common
  StandardResponse,
  PagedResult,

  // Enums
  ServiceCategory,
  PaymentMethod,
  AccountType,
} from '@osise/api-client';
```

## Configuration Options

```typescript
const client = new OsiseClient({
  // Required
  baseUrl: 'https://api.osise.com',

  // Optional
  timeout: 30000, // Request timeout in ms
  storage: new BrowserTokenStorage(), // Token storage
  headers: { 'X-Custom-Header': 'value' }, // Custom headers
  autoRefresh: true, // Auto refresh tokens (default: true)

  // Callbacks
  onError: (error) => console.error(error),
  onAuthRequired: () => redirectToLogin(),
  onTokenRefresh: (accessToken, refreshToken) => {
    console.log('Tokens refreshed');
  },
});
```

## Storage Options

```typescript
// Browser (localStorage)
import { BrowserTokenStorage } from '@osise/api-client';
const storage = new BrowserTokenStorage('osise'); // optional key prefix

// React Native (AsyncStorage)
import { SecureTokenStorage } from '@osise/api-client';
const storage = new SecureTokenStorage(AsyncStorage);
await storage.initialize();

// In-memory (SSR, testing)
import { MemoryTokenStorage } from '@osise/api-client';
const storage = new MemoryTokenStorage();
```

## License

MIT