Skip to main content

Overview

The dashboard is the central hub for delivery riders, displaying wallet balance, earnings summary, quick action buttons, and performance statistics. It provides riders with a comprehensive view of their delivery operations and financial status.

Dashboard Layout

Main Dashboard Component

The dashboard combines multiple features into a cohesive interface.
src/modules/dashboard/parcels/riders/home/index.tsx
import { getUserWalletQueryOptions } from "@/lib/tanstack-query/query-options/users";
import { formatCurrency } from "@/utils/currency";
import { Storage, StorageKeys } from "@/utils/storage";
import { useQuery } from "@tanstack/react-query";
import { useRouter } from "expo-router";
import { Avatar, Button, Skeleton } from "heroui-native";
import { ImageBackground, Text, View } from "react-native";
import { RiderAccountStats } from "./account-stats";
import { AvailableDeliveries } from "./available-deliveries";

export function RidersHomePage() {
  const user = Storage.getObject(StorageKeys.USER) as User;
  const router = useRouter();

  const { data: walletResponse, isLoading } = useQuery(
    getUserWalletQueryOptions(),
  );

  const wallet = walletResponse?.data;
  const accountBalance = wallet?.balance ? +wallet.balance : 0;
  const totalEarnings = wallet?.totalEarned ? +wallet.totalEarned : 0;

  return (
    <View className="pt-4">
      {/* Welcome Header */}
      <View className="flex-row items-center justify-between">
        <Text className="text-lg text-secondary font-semibold">
          Hello <Text className="text-accent">{user.fullName}</Text> 👋
        </Text>
        <Avatar size="sm" alt={user.fullName}>
          <Avatar.Fallback>{getInitials(user.fullName)}</Avatar.Fallback>
        </Avatar>
      </View>

      {/* Wallet Section */}
      <View className="rounded-2xl mt-4 relative">
        <ImageBackground source={require("@/assets/images/level-curves.png")}>
          <View className="p-4">
            <View className="flex-row items-center gap-2 mb-1">
              <Ionicons name="wallet-outline" size={20} color="white" />
              <Text className="text-lg text-white font-semibold">Balance</Text>
            </View>
            <Text className="text-xs text-white/70 mb-3">
              Your total available earnings
            </Text>
            <View className="flex-row items-baseline gap-2 mb-4">
              {isLoading ? (
                <Skeleton className="w-24 h-6 rounded-lg" />
              ) : (
                <Text className="text-3xl text-white font-bold">
                  {AppConfig.currency.symbol} {formatCurrency(accountBalance)}
                </Text>
              )}
            </View>
            <View className="flex-row items-center">
              <Button
                size="sm"
                onPress={() => router.push("/(parcel)/(stack)/request-payment")}
              >
                <Ionicons name="cash-outline" size={16} color="white" />
                <Text className="text-white font-medium ml-1">
                  Request Payment
                </Text>
              </Button>
              <Button
                size="sm"
                variant="ghost"
                onPress={() => router.push("/(parcel)/(tabs)/transactions")}
              >
                <Ionicons name="list-outline" size={16} color="white" />
                <Text className="text-white font-medium ml-1">Transactions</Text>
              </Button>
            </View>
          </View>
        </ImageBackground>
      </View>

      {/* Account Stats */}
      <RiderAccountStats totalEarnings={totalEarnings} />

      {/* Available Deliveries */}
      <AvailableDeliveries />
    </View>
  );
}

Wallet Balance Display

The wallet balance card shows the rider’s current available balance with a visually appealing design.
Wallet Balance Card

Key Features

Real-time Balance

Displays the rider’s current wallet balance fetched from the API.

Quick Actions

Provides instant access to request payment and view transactions.

Visual Design

Features an attractive background with gradient overlay.

Loading States

Shows skeleton loaders while fetching wallet data.

Account Statistics

The statistics section displays key performance metrics for the rider.
src/modules/dashboard/parcels/riders/home/account-stats.tsx
import { getRiderAccountStatQueryOptions } from "@/lib/tanstack-query/query-options/users";
import { formatCurrency } from "@/utils/currency";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "heroui-native";
import { FlatList, Text, View } from "react-native";

export function RiderAccountStats({ totalEarnings }: RiderAccountStatsProps) {
  const user = Storage.getObject(StorageKeys.USER);

  const { data: accountStatsResponse, isLoading } = useQuery(
    getRiderAccountStatQueryOptions(user.id),
  );

  const STATS = [
    {
      label: "Total revenue",
      value: `${AppConfig.currency.symbol} ${formatCurrency(totalEarnings)}`,
    },
    {
      label: "Total deliveries today",
      value: accountStatsResponse?.data?.total_deliveries_today,
    },
    {
      label: "Total deliveries",
      value: accountStatsResponse?.data?.total_orders_delivered,
    },
    {
      label: "Cancelled Orders",
      value: accountStatsResponse?.data?.total_orders_cancelled,
    },
  ];

  return (
    <View className="mt-4">
      <Text className="text-base text-secondary font-semibold mb-2">
        Statistics
      </Text>
      <FlatList
        data={STATS}
        horizontal
        showsHorizontalScrollIndicator={false}
        contentContainerStyle={{ gap: 16 }}
        renderItem={({ item }) => (
          <View className="bg-muted/10 rounded-lg p-4 min-w-[230px]">
            <Text className="text-gray-500 text-sm h-14">{item.label}</Text>
            {isLoading ? (
              <Skeleton className="w-24 h-6 rounded-lg" />
            ) : (
              <Text className="font-semibold text-lg">{item.value}</Text>
            )}
          </View>
        )}
      />
    </View>
  );
}

Statistics Cards

Displays the total earnings accumulated by the rider across all completed deliveries.

Quick Actions

The dashboard provides two primary action buttons:
Navigates to the payout request screen where riders can withdraw their earnings to mobile money or bank accounts.
Opens the transaction history screen to review all wallet activities and earnings.

Available Deliveries Preview

The bottom section of the dashboard shows a preview of available delivery requests.
src/modules/dashboard/parcels/riders/home/available-deliveries.tsx
import { ShipmentCard } from "@/components/shipment-card";
import { getShipmentsQueryOptions } from "@/lib/tanstack-query/query-options/shipment";
import { FlashList } from "@shopify/flash-list";
import { useQuery } from "@tanstack/react-query";
import { Link } from "expo-router";

export function AvailableDeliveries() {
  const user = Storage.getObject(StorageKeys.USER) as User;

  const { data: shipmentsResponse, isLoading, refetch } = useQuery(
    getShipmentsQueryOptions(user.id)
  );

  const shipments = shipmentsResponse?.data.items || [];

  return (
    <View className="mt-4">
      <Text className="text-base text-secondary font-semibold mb-2">
        Available deliveries
      </Text>
      {shipments.length === 0 ? (
        <Empty />
      ) : (
        <FlashList
          data={shipments}
          renderItem={({ item }) => (
            <Link asChild href={`/shipments/${item.reference}`}>
              <ShipmentCard shipment={item} />
            </Link>
          )}
          keyExtractor={(item) => item.id}
        />
      )}
    </View>
  );
}

Features

  • Real-time Updates: Automatically refreshes when the screen comes into focus
  • Empty State: Shows a helpful message when no deliveries are available
  • Quick Navigation: Tap any delivery card to view full details
  • FlashList Performance: Uses optimized list rendering for smooth scrolling

Data Fetching

The dashboard uses TanStack Query for efficient data fetching and caching:
// Wallet data
const { data: walletResponse, isLoading } = useQuery(
  getUserWalletQueryOptions(),
);

// Account statistics
const { data: accountStatsResponse } = useQuery(
  getRiderAccountStatQueryOptions(user.id),
);

// Available shipments
const { data: shipmentsResponse } = useQuery(
  getShipmentsQueryOptions(user.id)
);
All data is cached and automatically revalidated to ensure riders see the most up-to-date information.

Loading States

The dashboard implements skeleton loaders for a smooth user experience:
{isLoading ? (
  <Skeleton className="w-24 h-6 rounded-lg" />
) : (
  <Text className="text-3xl text-white font-bold">
    {AppConfig.currency.symbol} {formatCurrency(accountBalance)}
  </Text>
)}

Responsive Design

The dashboard adapts to different screen sizes:
  • Mobile: Vertical scrolling layout with full-width cards
  • Tablet: Optimized spacing with flexible card widths
  • Web: Centered content with maximum width constraints

Deliveries

Browse and accept delivery requests.

Transactions

View detailed transaction history.

Payouts

Request withdrawals to your account.

Build docs developers (and LLMs) love