Docs/Frontend Development
Module Development

Frontend Development

Building React/TypeScript frontend code for AutoCom modules.

Frontend Structure

modules/ShippingDelhivery/frontend/
├── index.ts              # Main exports
├── types/
│   └── shipment.ts       # TypeScript interfaces
├── hooks/
│   └── use-delhivery.ts  # React hooks
└── components/
    ├── ShipmentTracker.tsx
    └── LabelDownload.tsx

TypeScript Types

// modules/ShippingDelhivery/frontend/types/shipment.ts

export interface DelhiveryShipment {
  id: string;
  order_id: string;
  awb: string;
  status: ShipmentStatus;
  weight_kg: number;
  dimensions?: {
    length: number;
    width: number;
    height: number;
  };
  tracking_url: string;
  label_url: string | null;
  pickup_scheduled_at: string | null;
  shipped_at: string | null;
  delivered_at: string | null;
  created_at: string;
  updated_at: string;
}

export type ShipmentStatus =
  | 'manifested'
  | 'picked_up'
  | 'in_transit'
  | 'out_for_delivery'
  | 'delivered'
  | 'rto_initiated'
  | 'rto_delivered'
  | 'cancelled';

export interface TrackingEvent {
  status: string;
  location: string;
  description: string;
  timestamp: string;
}

export interface CreateShipmentPayload {
  order_id: string;
  weight_kg: number;
  dimensions?: {
    length: number;
    width: number;
    height: number;
  };
  pickup_date?: string;
  is_cod?: boolean;
  cod_amount?: number;
}

React Hooks

// modules/ShippingDelhivery/frontend/hooks/use-delhivery.ts

import { useState, useCallback } from 'react';
import { apiClient } from '@/lib/api-client';
import type {
  DelhiveryShipment,
  CreateShipmentPayload,
  TrackingEvent
} from '../types/shipment';

export function useDelhiveryShipments() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const createShipment = useCallback(async (
    payload: CreateShipmentPayload
  ): Promise<DelhiveryShipment> => {
    setLoading(true);
    setError(null);

    try {
      const response = await apiClient.post(
        '/api/v1/shipping/delhivery/shipments',
        payload
      );
      return response.data.data;
    } catch (err: any) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  }, []);

  const trackShipment = useCallback(async (
    awb: string
  ): Promise<TrackingEvent[]> => {
    const response = await apiClient.get(
      `/api/v1/shipping/delhivery/shipments/${awb}/track`
    );
    return response.data.data;
  }, []);

  return {
    loading,
    error,
    createShipment,
    trackShipment,
  };
}

React Components

// modules/ShippingDelhivery/frontend/components/ShipmentTracker.tsx

import { useState, useEffect } from 'react';
import { useDelhiveryShipments } from '../hooks/use-delhivery';
import type { TrackingEvent } from '../types/shipment';

interface ShipmentTrackerProps {
  awb: string;
  onStatusChange?: (status: string) => void;
}

export function ShipmentTracker({ awb, onStatusChange }: ShipmentTrackerProps) {
  const { trackShipment } = useDelhiveryShipments();
  const [events, setEvents] = useState<TrackingEvent[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchTracking() {
      try {
        const data = await trackShipment(awb);
        setEvents(data);
        if (data.length > 0 && onStatusChange) {
          onStatusChange(data[0].status);
        }
      } finally {
        setLoading(false);
      }
    }
    fetchTracking();
  }, [awb, trackShipment, onStatusChange]);

  if (loading) {
    return <div className="animate-pulse">Loading tracking...</div>;
  }

  return (
    <div className="space-y-4">
      <h3 className="font-medium">Tracking: {awb}</h3>
      <div className="space-y-2">
        {events.map((event, index) => (
          <div
            key={index}
            className="flex items-start gap-3 p-3 rounded-lg border"
          >
            <div className="w-2 h-2 mt-2 rounded-full bg-blue-500" />
            <div>
              <p className="font-medium">{event.status}</p>
              <p className="text-sm text-gray-500">{event.location}</p>
              <p className="text-xs text-gray-400">{event.timestamp}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Main Exports (index.ts)

// modules/ShippingDelhivery/frontend/index.ts

// Types
export * from './types/shipment';

// Hooks
export { useDelhiveryShipments } from './hooks/use-delhivery';

// Components
export { ShipmentTracker } from './components/ShipmentTracker';
export { LabelDownload } from './components/LabelDownload';

Using Module Exports

Import module exports in your main application:

// In your Next.js app
import {
  useDelhiveryShipments,
  ShipmentTracker,
  type DelhiveryShipment,
} from '@modules/ShippingDelhivery';

function OrderShippingSection({ orderId }: { orderId: string }) {
  const { createShipment, loading } = useDelhiveryShipments();
  const [shipment, setShipment] = useState<DelhiveryShipment | null>(null);

  const handleShip = async () => {
    const newShipment = await createShipment({
      order_id: orderId,
      weight_kg: 0.5,
    });
    setShipment(newShipment);
  };

  return (
    <div>
      {!shipment ? (
        <button onClick={handleShip} disabled={loading}>
          Create Shipment
        </button>
      ) : (
        <ShipmentTracker awb={shipment.awb} />
      )}
    </div>
  );
}

Best Practices

Type Everything

Use TypeScript interfaces for all API responses and payloads.

Hooks for Logic

Keep API calls and state management in custom hooks, not components.

Error Handling

Always handle loading and error states in your hooks.

Export Everything

Re-export all public APIs from index.ts for clean imports.