Frontend Pages

Documentation for all frontend pages in the reseller platform.

Overview

The reseller platform adds the following pages to the AutoCom frontend:

/reseller-catalog
├── /                        # Catalog overview
├── /master-products         # Master product management (super_admin)
├── /my-catalog              # Available products (reseller)
└── /pricing                 # Pricing management

/reseller-orders
├── /                        # Chain orders dashboard
├── /incoming                # Orders from sub-resellers
├── /forwarded               # Orders sent to parent
└── /fulfillment             # Orders to fulfill

/reseller-finance
├── /                        # Finance dashboard
├── /wallet                  # Wallet details
├── /settlements             # Pending settlements
└── /transactions            # Transaction history

Navigation Visibility

Pages are filtered by tenant type:

Tenant Type Visible Pages
super_admin All pages including Master Products
reseller All pages except Master Products, includes My Catalog
standard None (reseller pages hidden)

ResellerCatalog Pages

Catalog Overview

Route: /reseller-catalog

File: frontend/app/reseller-catalog/page.tsx

Features:

  • Product count summary
  • Recent product activity
  • Quick links to sub-pages
  • Pricing health indicators

Master Products (Super Admin)

Route: /reseller-catalog/master-products

File: frontend/app/reseller-catalog/master-products/page.tsx

Features:

  • Product grid/list view
  • Create new product modal
  • Inline editing
  • Bulk actions (activate, deactivate, delete)
  • Stock management
  • Image upload
  • Filter by status, category
  • Search by name, SKU
  • Export to CSV

UI Components:

// Product card in grid view
<Card>
  <CardHeader>
    <img src={product.images[0]} />
    <Badge>{product.status}</Badge>
  </CardHeader>
  <CardContent>
    <h3>{product.name}</h3>
    <p>SKU: {product.sku}</p>
    <p>Base Cost: ₹{product.base_cost}</p>
    <p>Stock: {product.stock_quantity}</p>
  </CardContent>
  <CardFooter>
    <Button>Edit</Button>
    <Button>Set Pricing</Button>
  </CardFooter>
</Card>

My Catalog (Reseller)

Route: /reseller-catalog/my-catalog

File: frontend/app/reseller-catalog/my-catalog/page.tsx

Features:

  • Products available at your cost
  • Margin calculator
  • Suggested selling price
  • Stock availability
  • Quick price setting for sub-resellers

Key UI:

// Product with pricing info
<Card>
  <CardContent>
    <h3>{product.name}</h3>
    <div className="pricing">
      <span className="cost">Your Cost: ₹{product.your_cost}</span>
      <span className="suggested">Suggested: ₹{product.suggested_price}</span>
      <Badge>Min Margin: {product.min_margin}%</Badge>
    </div>
    <div className="stock">
      {product.available_stock > 0
        ? <Badge variant="success">In Stock ({product.available_stock})</Badge>
        : <Badge variant="destructive">Out of Stock</Badge>
      }
    </div>
  </CardContent>
</Card>

Pricing Management

Route: /reseller-catalog/pricing

File: frontend/app/reseller-catalog/pricing/page.tsx

Features:

  • Set prices for sub-resellers
  • Bulk pricing updates
  • Margin analysis
  • Price breakdown view

ResellerOrders Pages

Chain Orders Dashboard

Route: /reseller-orders

File: frontend/app/reseller-orders/page.tsx

Features:

  • Order status overview cards
  • Status breakdown chart
  • Recent orders list
  • Quick filters
  • Action buttons

UI Layout:

<div className="grid gap-4 md:grid-cols-4">
  <StatsCard title="Pending" value={stats.pending} icon={Clock} />
  <StatsCard title="Processing" value={stats.processing} icon={Package} />
  <StatsCard title="Shipped" value={stats.shipped} icon={Truck} />
  <StatsCard title="Delivered" value={stats.delivered} icon={CheckCircle} />
</div>

<Card>
  <CardHeader>
    <CardTitle>Recent Chain Orders</CardTitle>
  </CardHeader>
  <CardContent>
    <OrdersTable orders={recentOrders} />
  </CardContent>
</Card>

Incoming Orders

Route: /reseller-orders/incoming

File: frontend/app/reseller-orders/incoming/page.tsx

Features:

  • Orders from sub-resellers
  • Accept/Forward actions
  • Bulk operations
  • Filter by status, sub-reseller, date
  • Order details modal

Key Actions:

// Action buttons per order
<div className="actions">
  <Button onClick={() => acceptOrder(order.id)}>
    Accept
  </Button>
  <Button variant="outline" onClick={() => forwardOrder(order.id)}>
    Forward to Parent
  </Button>
  <Button variant="ghost" onClick={() => viewDetails(order.id)}>
    View Details
  </Button>
</div>

Forwarded Orders

Route: /reseller-orders/forwarded

File: frontend/app/reseller-orders/forwarded/page.tsx

Features:

  • Orders sent up the chain
  • Track fulfillment progress
  • Settlement status
  • Filter by status, date

UI Elements:

// Order with fulfillment status
<Card>
  <CardContent>
    <div className="order-header">
      <span>{order.origin_order_number}</span>
      <Badge>{order.chain_status}</Badge>
    </div>
    <div className="fulfiller">
      Fulfilling: {order.fulfiller_name || 'Pending'}
    </div>
    {order.tracking_number && (
      <div className="tracking">
        <Truck className="h-4 w-4" />
        {order.carrier}: {order.tracking_number}
      </div>
    )}
    <div className="settlement">
      Your Margin: ₹{order.your_margin}
      <Badge variant={order.settled ? 'success' : 'warning'}>
        {order.settled ? 'Settled' : 'Pending'}
      </Badge>
    </div>
  </CardContent>
</Card>

Fulfillment Queue

Route: /reseller-orders/fulfillment

File: frontend/app/reseller-orders/fulfillment/page.tsx

Features:

  • Orders you need to ship
  • Generate shipping labels
  • Bulk ship functionality
  • Print packing slips
  • Carrier selection

Shipping Flow:

// Ship order modal
<Dialog>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Ship Order #{order.origin_order_number}</DialogTitle>
    </DialogHeader>
    <div className="space-y-4">
      <Select value={carrier} onValueChange={setCarrier}>
        <SelectItem value="delhivery">Delhivery</SelectItem>
        <SelectItem value="shiprocket">Shiprocket</SelectItem>
        <SelectItem value="manual">Manual Entry</SelectItem>
      </Select>
      <Input
        placeholder="Tracking Number"
        value={trackingNumber}
        onChange={(e) => setTrackingNumber(e.target.value)}
      />
    </div>
    <DialogFooter>
      <Button onClick={handleShip}>Mark as Shipped</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

ResellerFinance Pages

Finance Dashboard

Route: /reseller-finance

File: frontend/app/reseller-finance/page.tsx

Features:

  • Wallet balance overview
  • Available vs total balance
  • Pending settlements summary
  • Net position indicator
  • Quick links

Layout:

<div className="grid gap-4 md:grid-cols-4">
  <Card>
    <CardHeader>
      <CardTitle>Available Balance</CardTitle>
      <Wallet className="h-4 w-4" />
    </CardHeader>
    <CardContent>
      <div className="text-2xl font-bold text-green-600">
        ₹{wallet.available_balance.toLocaleString()}
      </div>
      <p className="text-sm text-muted">
        Total: ₹{wallet.balance.toLocaleString()}
      </p>
    </CardContent>
  </Card>

  <Card>
    <CardHeader>
      <CardTitle>Pending Credits</CardTitle>
    </CardHeader>
    <CardContent>
      <div className="text-2xl font-bold">
        ₹{wallet.pending_credits.toLocaleString()}
      </div>
    </CardContent>
  </Card>

  <Card>
    <CardHeader>
      <CardTitle>On Hold</CardTitle>
    </CardHeader>
    <CardContent>
      <div className="text-2xl font-bold text-orange-600">
        ₹{wallet.pending_debits.toLocaleString()}
      </div>
    </CardContent>
  </Card>

  <Card>
    <CardHeader>
      <CardTitle>Net Position</CardTitle>
    </CardHeader>
    <CardContent>
      <div className={`text-2xl font-bold ${
        netPosition >= 0 ? 'text-green-600' : 'text-red-600'
      }`}>
        {netPosition >= 0 ? '+' : ''}₹{netPosition.toLocaleString()}
      </div>
    </CardContent>
  </Card>
</div>

Wallet Details

Route: /reseller-finance/wallet

File: frontend/app/reseller-finance/wallet/page.tsx

Features:

  • Detailed balance breakdown
  • Period summary (week/month/quarter/year)
  • Transaction breakdown by type
  • Lifetime statistics
  • Credit limit info

Period Selector:

<Select value={period} onValueChange={setPeriod}>
  <SelectItem value="week">This Week</SelectItem>
  <SelectItem value="month">This Month</SelectItem>
  <SelectItem value="quarter">This Quarter</SelectItem>
  <SelectItem value="year">This Year</SelectItem>
</Select>

Settlements

Route: /reseller-finance/settlements

File: frontend/app/reseller-finance/settlements/page.tsx

Features:

  • To Pay tab - Remittances you owe
  • To Receive tab - Remittances owed to you
  • History tab - Completed settlements
  • Process individual remittances
  • Bulk payment
  • Overdue indicators

Tabs Layout:

<Tabs defaultValue="to-pay">
  <TabsList>
    <TabsTrigger value="to-pay">
      To Pay
      {toPay.length > 0 && (
        <Badge variant="destructive" className="ml-2">
          {toPay.length}
        </Badge>
      )}
    </TabsTrigger>
    <TabsTrigger value="to-receive">
      To Receive
      {toReceive.length > 0 && (
        <Badge className="ml-2 bg-green-600">
          {toReceive.length}
        </Badge>
      )}
    </TabsTrigger>
    <TabsTrigger value="history">History</TabsTrigger>
  </TabsList>

  <TabsContent value="to-pay">
    <RemittanceTable
      remittances={toPay}
      direction="outgoing"
      onProcess={handleProcess}
    />
  </TabsContent>

  <TabsContent value="to-receive">
    <RemittanceTable
      remittances={toReceive}
      direction="incoming"
    />
  </TabsContent>

  <TabsContent value="history">
    <RemittanceTable
      remittances={history}
      showStatus
    />
  </TabsContent>
</Tabs>

Process Remittance Dialog:

<AlertDialog>
  <AlertDialogTrigger asChild>
    <Button>Mark Paid</Button>
  </AlertDialogTrigger>
  <AlertDialogContent>
    <AlertDialogHeader>
      <AlertDialogTitle>Confirm Payment</AlertDialogTitle>
      <AlertDialogDescription>
        This will debit ₹{remittance.amount.toLocaleString()} from
        your wallet and credit it to {remittance.to_tenant.name}.
      </AlertDialogDescription>
    </AlertDialogHeader>
    <AlertDialogFooter>
      <AlertDialogCancel>Cancel</AlertDialogCancel>
      <AlertDialogAction onClick={confirmPayment}>
        Confirm Payment
      </AlertDialogAction>
    </AlertDialogFooter>
  </AlertDialogContent>
</AlertDialog>

Transaction History

Route: /reseller-finance/transactions

File: frontend/app/reseller-finance/transactions/page.tsx

Features:

  • Full transaction log
  • Filter by type (credit/debit/hold/release)
  • Filter by reference type
  • Date range filter
  • Search
  • Export to CSV
  • Pagination

Filters:

<div className="grid gap-4 md:grid-cols-4">
  <div className="relative">
    <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4" />
    <Input placeholder="Search..." className="pl-9" />
  </div>

  <Select value={typeFilter} onValueChange={setTypeFilter}>
    <SelectItem value="all">All Types</SelectItem>
    <SelectItem value="credit">Credits</SelectItem>
    <SelectItem value="debit">Debits</SelectItem>
    <SelectItem value="hold">Holds</SelectItem>
    <SelectItem value="release">Releases</SelectItem>
  </Select>

  <Select value={refFilter} onValueChange={setRefFilter}>
    <SelectItem value="all">All References</SelectItem>
    <SelectItem value="chain_order">Chain Orders</SelectItem>
    <SelectItem value="remittance">Remittances</SelectItem>
  </Select>

  <Button variant="outline" onClick={exportCSV}>
    <Download className="h-4 w-4 mr-2" />
    Export
  </Button>
</div>

Transaction Row:

<TableRow>
  <TableCell>{formatDate(txn.created_at)}</TableCell>
  <TableCell>
    <div className="flex items-center gap-2">
      {getTypeIcon(txn.type)}
      <Badge variant={getTypeVariant(txn.type)}>
        {txn.type}
      </Badge>
    </div>
  </TableCell>
  <TableCell className="max-w-xs truncate">
    {txn.description}
  </TableCell>
  <TableCell>
    <Badge variant="outline">
      {txn.reference_type?.replace('_', ' ')}
    </Badge>
  </TableCell>
  <TableCell className={`text-right font-medium ${
    txn.type === 'credit' ? 'text-green-600' :
    txn.type === 'debit' ? 'text-red-600' : ''
  }`}>
    {txn.type === 'credit' ? '+' : txn.type === 'debit' ? '-' : ''}
    ₹{txn.amount.toLocaleString()}
  </TableCell>
  <TableCell className="text-right">
    ₹{txn.balance_after.toLocaleString()}
  </TableCell>
</TableRow>

Shared Components

Currency Formatter

const formatCurrency = (amount: number, currency = 'INR') => {
  return new Intl.NumberFormat('en-IN', {
    style: 'currency',
    currency,
  }).format(amount);
};

Status Badge

const StatusBadge = ({ status }: { status: string }) => {
  const variants: Record<string, string> = {
    pending_forward: 'bg-yellow-100 text-yellow-800',
    forwarded: 'bg-blue-100 text-blue-800',
    accepted: 'bg-purple-100 text-purple-800',
    processing: 'bg-indigo-100 text-indigo-800',
    shipped: 'bg-cyan-100 text-cyan-800',
    delivered: 'bg-green-100 text-green-800',
    cancelled: 'bg-red-100 text-red-800',
  };

  return (
    <Badge className={variants[status] || 'bg-gray-100 text-gray-800'}>
      {status.replace('_', ' ')}
    </Badge>
  );
};

Empty State

const EmptyState = ({
  icon: Icon,
  title,
  description
}: {
  icon: LucideIcon;
  title: string;
  description: string;
}) => (
  <div className="text-center py-12">
    <Icon className="h-12 w-12 mx-auto mb-4 text-muted-foreground" />
    <h3 className="text-lg font-medium">{title}</h3>
    <p className="text-muted-foreground">{description}</p>
  </div>
);

State Management

Pages use React hooks for local state:

// Example: Settlements page state
const [toPay, setToPay] = useState<Remittance[]>([]);
const [toReceive, setToReceive] = useState<Remittance[]>([]);
const [loading, setLoading] = useState(true);
const [processing, setProcessing] = useState<string | null>(null);

useEffect(() => {
  fetchSettlements();
}, []);

const fetchSettlements = async () => {
  setLoading(true);
  try {
    const response = await api.get('/finance/settlements/pending');
    setToPay(response.data.to_pay);
    setToReceive(response.data.to_receive);
  } catch (error) {
    toast.error('Failed to load settlements');
  } finally {
    setLoading(false);
  }
};

API Integration

All pages connect to the API using the configured client:

// lib/api.ts
import axios from 'axios';

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
});

// Add tenant headers
api.interceptors.request.use((config) => {
  const tenant = getTenantFromContext();
  config.headers['X-Tenant'] = tenant.id;

  // For context switching
  const effectiveTenant = getEffectiveTenant();
  if (effectiveTenant) {
    config.headers['X-Effective-Tenant'] = effectiveTenant.id;
  }

  return config;
});

export default api;