> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Store Queries

> Retrieve store information and data for authenticated store owners

## Overview

The Store Queries API provides functions for retrieving store information, primarily for authenticated store owners to access their own store data. This is the primary way stores get their profile information for dashboard and management interfaces.

**Location:** `convex/stores/queries.ts`

## Get Store by Authenticated Owner

Retrieves complete store information for the authenticated store owner.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const store = await convex.query(api.stores.queries.getStoreByAuthenticatedOwner);
  ```

  ```javascript JavaScript theme={null}
  const store = await convex.query("stores.queries.getStoreByAuthenticatedOwner");
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      f"{CONVEX_URL}/api/query",
      json={
          "path": "stores.queries.getStoreByAuthenticatedOwner",
          "args": {}
      },
      headers={"Authorization": f"Bearer {token}"}
  )

  store = response.json()
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "_id": "j123456789",
    "name": "Mario's Pizza Palace",
    "primaryCategory": "k123456789",
    "status": "approved",
    "statusReason": "All documents verified and approved",
    "logoUrl": "https://storage.convex.dev/logo123.jpg",
    "coverImageUrl": "https://storage.convex.dev/cover456.jpg",
    "tradeLicenseUrl": "https://storage.convex.dev/license789.pdf",
    "ownerUserId": "user_123456789",
    "ownerName": "Mario Rossi",
    "ownerEmail": "mario@mariospizza.com",
    "settings": {
      "deliveryEnabled": true,
      "pickupEnabled": true,
      "storeLive": true
    },
    "bankAccount": {
      "IBAN": "AE123456789012345678901",
      "bankName": "Emirates NBD",
      "bankId": "b123456789",
      "accountHolderName": "Mario Rossi"
    },
    "category": {
      "id": "k123456789",
      "name": "Food & Restaurants"
    },
    "address": {
      "fullAddress": "123 Main Street, Business Bay, Dubai, UAE",
      "city": {
        "_id": "c123456789",
        "name": "Dubai",
        "nameArabic": "دبي"
      },
      "area": {
        "_id": "a987654321",
        "name": "Business Bay",
        "city": "c123456789"
      },
      "flatVilaNumber": "Villa 123",
      "buildingNameNumber": "Marina Plaza",
      "landmark": "Near Dubai Mall",
      "latitude": "25.1972",
      "longitude": "55.2744"
    },
    "wallet": {
      "availableBalance": 1250.50,
      "pendingBalance": 340.25,
      "totalBalance": 1590.75
    },
    "stats": {
      "totalProducts": 25,
      "activeProducts": 23,
      "totalOrders": 150,
      "averageRating": 4.8
    },
    "_creationTime": 1640995200000,
    "lastUpdated": 1640995800000
  }
  ```
</ResponseExample>

## Store Dashboard Hook

Complete React hook for store dashboard data:

<CodeGroup>
  ```typescript React Hook theme={null}
  import { useQuery } from 'convex/react';
  import { api } from './convex/_generated/api';

  interface StoreData {
    store: any;
    isLoading: boolean;
    error: string | null;
    isApproved: boolean;
    isPending: boolean;
    isRejected: boolean;
  }

  function useStoreData(): StoreData {
    const store = useQuery(api.stores.queries.getStoreByAuthenticatedOwner);
    
    const isLoading = store === undefined;
    const error = store === null ? "No store found for this user" : null;
    
    const isApproved = store?.status === "approved";
    const isPending = store?.status === "pending";
    const isRejected = store?.status === "rejected";

    return {
      store,
      isLoading,
      error,
      isApproved,
      isPending,
      isRejected
    };
  }

  // Usage in component
  function StoreDashboard() {
    const { store, isLoading, error, isApproved, isPending, isRejected } = useStoreData();

    if (isLoading) {
      return <div className="flex items-center justify-center p-8">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
        <span className="ml-2">Loading store...</span>
      </div>;
    }

    if (error) {
      return <div className="bg-red-50 p-4 rounded-lg">
        <p className="text-red-700">{error}</p>
        <button className="mt-2 bg-blue-600 text-white px-4 py-2 rounded">
          Register Store
        </button>
      </div>;
    }

    if (isPending) {
      return <div className="bg-yellow-50 p-6 rounded-lg">
        <h2 className="text-xl font-bold text-yellow-900 mb-2">Application Under Review</h2>
        <p className="text-yellow-700 mb-4">
          Your store application is being reviewed by our team. This typically takes 24-48 hours.
        </p>
        <div className="bg-white p-4 rounded border">
          <h3 className="font-medium mb-2">Submitted Information:</h3>
          <ul className="text-sm text-gray-600 space-y-1">
            <li>• Store Name: {store.name}</li>
            <li>• Category: {store.category?.name}</li>
            <li>• Trade License: Uploaded</li>
            <li>• Submitted: {new Date(store._creationTime).toLocaleDateString()}</li>
          </ul>
        </div>
      </div>;
    }

    if (isRejected) {
      return <div className="bg-red-50 p-6 rounded-lg">
        <h2 className="text-xl font-bold text-red-900 mb-2">Application Rejected</h2>
        <p className="text-red-700 mb-4">{store.statusReason}</p>
        <button className="bg-blue-600 text-white px-4 py-2 rounded">
          Update Application
        </button>
      </div>;
    }

    // Approved store dashboard
    return (
      <div className="space-y-6">
        {/* Store Header */}
        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center space-x-4">
            {store.logoUrl && (
              <img 
                src={store.logoUrl} 
                alt={store.name}
                className="w-16 h-16 rounded-lg object-cover"
              />
            )}
            <div>
              <h1 className="text-2xl font-bold text-gray-900">{store.name}</h1>
              <p className="text-gray-600">{store.category?.name}</p>
              <div className="flex items-center space-x-4 mt-2">
                <span className={`px-2 py-1 rounded-full text-xs font-medium ${
                  store.settings?.storeLive 
                    ? 'bg-green-100 text-green-800' 
                    : 'bg-gray-100 text-gray-800'
                }`}>
                  {store.settings?.storeLive ? 'Live' : 'Offline'}
                </span>
                <span className="text-sm text-gray-500">
                  ⭐ {store.stats?.averageRating || 0} rating
                </span>
              </div>
            </div>
          </div>
        </div>

        {/* Quick Stats */}
        <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
          <div className="bg-white p-4 rounded-lg shadow">
            <p className="text-sm text-gray-600">Total Products</p>
            <p className="text-2xl font-bold text-gray-900">{store.stats?.totalProducts || 0}</p>
          </div>
          <div className="bg-white p-4 rounded-lg shadow">
            <p className="text-sm text-gray-600">Active Products</p>
            <p className="text-2xl font-bold text-green-600">{store.stats?.activeProducts || 0}</p>
          </div>
          <div className="bg-white p-4 rounded-lg shadow">
            <p className="text-sm text-gray-600">Total Orders</p>
            <p className="text-2xl font-bold text-blue-600">{store.stats?.totalOrders || 0}</p>
          </div>
          <div className="bg-white p-4 rounded-lg shadow">
            <p className="text-sm text-gray-600">Average Rating</p>
            <p className="text-2xl font-bold text-yellow-600">
              {store.stats?.averageRating ? store.stats.averageRating.toFixed(1) : '0.0'}
            </p>
          </div>
        </div>

        {/* Store Settings */}
        <div className="bg-white rounded-lg shadow p-6">
          <h2 className="text-lg font-semibold mb-4">Store Settings</h2>
          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
            <div className="flex items-center justify-between p-3 bg-gray-50 rounded">
              <span>Delivery Service</span>
              <span className={`px-2 py-1 rounded text-xs font-medium ${
                store.settings?.deliveryEnabled 
                  ? 'bg-green-100 text-green-800' 
                  : 'bg-red-100 text-red-800'
              }`}>
                {store.settings?.deliveryEnabled ? 'Enabled' : 'Disabled'}
              </span>
            </div>
            <div className="flex items-center justify-between p-3 bg-gray-50 rounded">
              <span>Pickup Service</span>
              <span className={`px-2 py-1 rounded text-xs font-medium ${
                store.settings?.pickupEnabled 
                  ? 'bg-green-100 text-green-800' 
                  : 'bg-red-100 text-red-800'
              }`}>
                {store.settings?.pickupEnabled ? 'Enabled' : 'Disabled'}
              </span>
            </div>
            <div className="flex items-center justify-between p-3 bg-gray-50 rounded">
              <span>Store Status</span>
              <span className={`px-2 py-1 rounded text-xs font-medium ${
                store.settings?.storeLive 
                  ? 'bg-green-100 text-green-800' 
                  : 'bg-gray-100 text-gray-800'
              }`}>
                {store.settings?.storeLive ? 'Live' : 'Offline'}
              </span>
            </div>
          </div>
        </div>

        {/* Bank Account Info */}
        {store.bankAccount && (
          <div className="bg-white rounded-lg shadow p-6">
            <h2 className="text-lg font-semibold mb-4">Bank Account</h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              <div>
                <p className="text-sm text-gray-600">Bank Name</p>
                <p className="font-medium">{store.bankAccount.bankName}</p>
              </div>
              <div>
                <p className="text-sm text-gray-600">Account Holder</p>
                <p className="font-medium">{store.bankAccount.accountHolderName}</p>
              </div>
              <div className="md:col-span-2">
                <p className="text-sm text-gray-600">IBAN</p>
                <p className="font-mono text-sm bg-gray-50 p-2 rounded">
                  {store.bankAccount.IBAN}
                </p>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }

  export { useStoreData, StoreDashboard };
  ```

  ```javascript Vue Composition API theme={null}
  import { computed } from 'vue';
  import { useConvexQuery } from '@convex-vue/core';

  export function useStoreData() {
    const store = useConvexQuery('stores.queries.getStoreByAuthenticatedOwner');
    
    const isLoading = computed(() => store.value === undefined);
    const error = computed(() => store.value === null ? "No store found for this user" : null);
    
    const isApproved = computed(() => store.value?.status === "approved");
    const isPending = computed(() => store.value?.status === "pending");
    const isRejected = computed(() => store.value?.status === "rejected");

    return {
      store,
      isLoading,
      error,
      isApproved,
      isPending,
      isRejected
    };
  }
  ```
</CodeGroup>

## Store Status Indicators

Visual indicators for different store states:

<CardGroup cols={2}>
  <Card title="Pending Review" icon="clock" color="#f59e0b">
    Store application submitted and waiting for admin review
  </Card>

  <Card title="Approved" icon="check-circle" color="#10b981">
    Store is approved and can start selling products
  </Card>

  <Card title="Rejected" icon="x-circle" color="#ef4444">
    Store application rejected with reason provided
  </Card>

  <Card title="Live" icon="broadcast-tower" color="#3b82f6">
    Store is live and accepting orders from customers
  </Card>
</CardGroup>

## Store Information Structure

The store query returns comprehensive information:

```typescript theme={null}
interface StoreData {
  // Basic Information
  _id: string;
  name: string;
  primaryCategory: string;
  status: 'pending' | 'approved' | 'rejected';
  statusReason?: string;
  
  // Media Assets
  logoUrl?: string;
  coverImageUrl?: string;
  tradeLicenseUrl: string;
  
  // Owner Information
  ownerUserId: string;
  ownerName: string;
  ownerEmail: string;
  
  // Operational Settings
  settings: {
    deliveryEnabled: boolean;
    pickupEnabled: boolean;
    storeLive: boolean;
  };
  
  // Financial Information
  bankAccount?: {
    IBAN: string;
    bankName: string;
    bankId: string;
    accountHolderName: string;
  };

  // Address Information (enriched with city/area details)
  address?: {
    fullAddress?: string;
    city?: {
      _id: string;
      name: string;
      nameArabic: string;
    };
    area?: {
      _id: string;
      name: string;
      city: string;
    };
    flatVilaNumber?: string;
    buildingNameNumber?: string;
    landmark?: string;
    latitude?: string;
    longitude?: string;
  };

  // Wallet Information
  wallet: {
    availableBalance: number;
    pendingBalance: number;
    totalBalance: number;
  };

  // Category Information
  category: {
    id: string;
    name: string;
  };
  
  // Statistics
  stats: {
    totalProducts: number;
    activeProducts: number;
    totalOrders: number;
    averageRating: number;
  };
  
  // Timestamps
  _creationTime: number;
  lastUpdated: number;
}
```

## Get Store Address

Retrieve only the address information for a store with enriched city and area details.

<ParamField path="storeId" type="Id<'stores'>">
  Optional store ID. If not provided, returns address for authenticated owner's store.
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get address for authenticated store owner
  const storeAddress = await convex.query(api.stores.queries.getStoreAddress);

  // Get address for specific store (must be owned by authenticated user)
  const specificStoreAddress = await convex.query(api.stores.queries.getStoreAddress, {
    storeId: "j123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  // Get address for authenticated store owner
  const storeAddress = await convex.query("stores.queries.getStoreAddress");

  // Get address for specific store
  const specificStoreAddress = await convex.query("stores.queries.getStoreAddress", {
    storeId: "j123456789"
  });
  ```

  ```python Python theme={null}
  import requests

  # Get address for authenticated store owner
  response = requests.post(
      f"{CONVEX_URL}/api/query",
      json={
          "path": "stores.queries.getStoreAddress",
          "args": {}
      },
      headers={"Authorization": f"Bearer {token}"}
  )

  store_address = response.json()
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "_id": "j123456789",
    "address": {
      "fullAddress": "123 Main Street, Business Bay, Dubai, UAE",
      "city": {
        "_id": "c123456789",
        "name": "Dubai",
        "nameArabic": "دبي"
      },
      "area": {
        "_id": "a987654321",
        "name": "Business Bay",
        "city": "c123456789"
      },
      "flatVilaNumber": "Villa 123",
      "buildingNameNumber": "Marina Plaza",
      "landmark": "Near Dubai Mall",
      "latitude": "25.1972",
      "longitude": "55.2744"
    }
  }
  ```
</ResponseExample>

<Note>
  Returns `null` if the authenticated user doesn't have a registered store and no `storeId` is provided.
</Note>

## Get Store Orders

Retrieve orders for the authenticated store with filtering and pagination support.

<ParamField path="status" type="OrderStatus">
  Filter orders by status: "pending", "confirmed", "preparing", "ready", "out\_for\_delivery", "delivered", "cancelled"
</ParamField>

<ParamField path="limit" type="number">
  Maximum number of orders to return (default: 50)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get all orders
  const allOrders = await convex.query(api.stores.queries.getStoreOrders);

  // Get only pending orders
  const pendingOrders = await convex.query(api.stores.queries.getStoreOrders, {
    status: "pending"
  });

  // Get confirmed orders with limit
  const confirmedOrders = await convex.query(api.stores.queries.getStoreOrders, {
    status: "confirmed",
    limit: 20
  });
  ```

  ```javascript JavaScript theme={null}
  const orders = await convex.query("stores.queries.getStoreOrders", {
    status: "pending",
    limit: 25
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "o123456789",
      "_creationTime": 1640995200000,
      "customerId": "cu123456789",
      "customerName": "Ahmed Al-Mansouri",
      "customerPhone": "+971501234567",
      "deliveryAddress": {
        "fullAddress": "Building 23, Street 45, Al Karama, Dubai",
        "city": "Dubai",
        "area": "Al Karama",
        "flatVilaNumber": "Apt 304",
        "buildingNameNumber": "Golden Tower",
        "phoneNumber": "+971501234567",
        "landmark": "Near Karama Park"
      },
      "subtotal": 125.50,
      "deliveryFee": 15.00,
      "totalAmount": 140.50,
      "storeEarnings": 112.40,
      "status": "pending",
      "paymentStatus": "paid",
      "orderDate": 1640995200000,
      "itemsCount": 3,
      "deliveryType": "delivery",
      "orderItems": [
        {
          "productId": "p123456789",
          "productName": "Margherita Pizza",
          "productImageUrl": "https://storage.convex.dev/pizza123.jpg",
          "quantity": 2,
          "unitPrice": 45.00,
          "totalPrice": 90.00
        },
        {
          "productId": "p987654321",
          "productName": "Caesar Salad",
          "productImageUrl": "https://storage.convex.dev/salad456.jpg",
          "quantity": 1,
          "unitPrice": 35.50,
          "totalPrice": 35.50
        }
      ]
    }
  ]
  ```
</ResponseExample>

## Real-time Updates

The store query automatically updates when store information changes:

```typescript theme={null}
// Store data updates automatically
function StoreProfile() {
  const store = useQuery(api.stores.queries.getStoreByAuthenticatedOwner);
  
  // This will re-render when:
  // - Store status changes (approved/rejected)
  // - Store settings are updated
  // - Bank account information is added/updated
  // - Store statistics change
  
  return (
    <div>
      {store && (
        <div>
          <h1>{store.name}</h1>
          <p>Status: {store.status}</p>
          <p>Live: {store.settings.storeLive ? 'Yes' : 'No'}</p>
        </div>
      )}
    </div>
  );
}
```

## Error Handling

<AccordionGroup>
  <Accordion title="No store found">
    **Response**: `null`

    This indicates the authenticated user doesn't have a registered store yet.

    ```typescript theme={null}
    if (store === null) {
      // User needs to register a store
      return <RegisterStoreComponent />;
    }
    ```
  </Accordion>

  <Accordion title="Authentication required">
    **Status Code**: `401`

    ```json theme={null}
    {
      "error": "Authentication required to access store data"
    }
    ```
  </Accordion>

  <Accordion title="Store access denied">
    **Status Code**: `403`

    ```json theme={null}
    {
      "error": "Access denied. User is not the owner of this store."
    }
    ```
  </Accordion>
</AccordionGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Real-time Updates" icon="arrows-rotate">
    Use the React hook pattern to automatically receive updates when store data changes
  </Card>

  <Card title="Status Handling" icon="traffic-light">
    Always check store status before showing management features
  </Card>

  <Card title="Loading States" icon="spinner">
    Handle loading states gracefully while store data is being fetched
  </Card>

  <Card title="Error Boundaries" icon="shield-exclamation">
    Implement proper error handling for missing or inaccessible stores
  </Card>
</CardGroup>

<Note>
  This query only returns data for the authenticated user's own store. Store owners cannot access other stores' private information.
</Note>

<Tip>
  Use the store status to conditionally show different UI components - registration forms for new users, pending status for under review, and full dashboard for approved stores.
</Tip>
