> ## 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.

# Locations

> Manage UAE cities and areas for delivery and store operations

## Overview

The Locations API provides comprehensive location management for the UAE market, including cities and areas with Arabic translations. This system supports delivery zone configuration, store location setup, and customer address management.

**Location:** `convex/shared/locations.ts`

## Get Cities

Retrieves all available cities in the UAE.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const cities = await convex.query(api.shared.locations.getCities);
  ```

  ```javascript JavaScript theme={null}
  const cities = await convex.query("shared.locations.getCities");
  ```

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

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

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

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "ct123456789",
      "name": "Dubai",
      "nameArabic": "دبي",
      "isActive": true,
      "sortOrder": 1,
      "_creationTime": 1640995200000
    },
    {
      "_id": "ct987654321",
      "name": "Abu Dhabi",
      "nameArabic": "أبو ظبي",
      "isActive": true,
      "sortOrder": 2,
      "_creationTime": 1640995200000
    },
    {
      "_id": "ct555666777",
      "name": "Sharjah",
      "nameArabic": "الشارقة",
      "isActive": true,
      "sortOrder": 3,
      "_creationTime": 1640995200000
    }
  ]
  ```
</ResponseExample>

## Get Areas by City

Retrieves all areas within a specific city.

<ParamField path="cityId" type="Id<'cities'>" required>
  City ID to get areas for
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const areas = await convex.query(api.shared.locations.getAreasByCity, { 
    cityId: "ct123456789" 
  });
  ```

  ```javascript JavaScript theme={null}
  const areas = await convex.query("shared.locations.getAreasByCity", { 
    cityId: "ct123456789" 
  });
  ```

  ```python Python theme={null}
  response = requests.post(
      f"{CONVEX_URL}/api/query",
      json={
          "path": "shared.locations.getAreasByCity",
          "args": {
              "cityId": "ct123456789"
          }
      },
      headers={"Authorization": f"Bearer {token}"}
  )

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

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "ar123456789",
      "name": "Marina",
      "nameArabic": "مارينا",
      "city": "ct123456789",
      "type": "district",
      "isActive": true,
      "_creationTime": 1640995200000
    },
    {
      "_id": "ar987654321",
      "name": "Downtown Dubai",
      "nameArabic": "وسط مدينة دبي",
      "city": "ct123456789",
      "type": "district",
      "isActive": true,
      "_creationTime": 1640995200000
    },
    {
      "_id": "ar555666777",
      "name": "Jumeirah",
      "nameArabic": "جميرا",
      "city": "ct123456789",
      "type": "neighborhood",
      "isActive": true,
      "_creationTime": 1640995200000
    }
  ]
  ```
</ResponseExample>

## Get Location Hierarchy

Retrieves the complete location hierarchy for the entire UAE.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const hierarchy = await convex.query(api.shared.locations.getLocationHierarchy);
  ```

  ```javascript JavaScript theme={null}
  const hierarchy = await convex.query("shared.locations.getLocationHierarchy");
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "cities": [
      {
        "_id": "ct123456789",
        "name": "Dubai",
        "nameArabic": "دبي",
        "areas": [
          {
            "_id": "ar123456789",
            "name": "Marina",
            "nameArabic": "مارينا",
            "type": "district"
          },
          {
            "_id": "ar987654321",
            "name": "Downtown Dubai",
            "nameArabic": "وسط مدينة دبي",
            "type": "district"
          }
        ]
      },
      {
        "_id": "ct987654321",
        "name": "Abu Dhabi",
        "nameArabic": "أبو ظبي",
        "areas": [
          {
            "_id": "ar111222333",
            "name": "Corniche",
            "nameArabic": "الكورنيش",
            "type": "district"
          }
        ]
      }
    ]
  }
  ```
</ResponseExample>

## Get Location Details

Retrieves detailed location information for a specific city or area.

<ParamField path="cityId" type="Id<'cities'>">
  City ID for city details
</ParamField>

<ParamField path="areaId" type="Id<'areas'>">
  Area ID for area details
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Get city details
  const cityDetails = await convex.query(api.shared.locations.getLocationDetails, {
    cityId: "ct123456789"
  });

  // Get area details
  const areaDetails = await convex.query(api.shared.locations.getLocationDetails, {
    areaId: "ar123456789"
  });
  ```

  ```javascript JavaScript theme={null}
  // Get city details
  const cityDetails = await convex.query("shared.locations.getLocationDetails", {
    cityId: "ct123456789"
  });

  // Get area details  
  const areaDetails = await convex.query("shared.locations.getLocationDetails", {
    areaId: "ar123456789"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json City Details Response theme={null}
  {
    "type": "city",
    "city": {
      "_id": "ct123456789",
      "name": "Dubai",
      "nameArabic": "دبي",
      "isActive": true,
      "sortOrder": 1,
      "areaCount": 45,
      "storeCount": 125
    }
  }
  ```

  ```json Area Details Response theme={null}
  {
    "type": "area",
    "area": {
      "_id": "ar123456789",
      "name": "Marina",
      "nameArabic": "مارينا",
      "city": "ct123456789",
      "cityName": "Dubai",
      "type": "district",
      "isActive": true,
      "storeCount": 15
    }
  }
  ```
</ResponseExample>

## Search Areas

Searches for areas by name (supports both English and Arabic).

<ParamField path="searchTerm" type="string" required>
  Search term (supports partial matching)
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Search in English
  const areas = await convex.query(api.shared.locations.searchAreas, {
    searchTerm: "marina"
  });

  // Search in Arabic
  const areasArabic = await convex.query(api.shared.locations.searchAreas, {
    searchTerm: "مارينا"
  });
  ```

  ```javascript JavaScript theme={null}
  const areas = await convex.query("shared.locations.searchAreas", {
    searchTerm: "marina"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_id": "ar123456789",
      "name": "Marina",
      "nameArabic": "مارينا",
      "city": "ct123456789",
      "cityName": "Dubai",
      "type": "district",
      "isActive": true
    },
    {
      "_id": "ar444555666",
      "name": "Marina Village",
      "nameArabic": "قرية مارينا",
      "city": "ct987654321",
      "cityName": "Abu Dhabi",
      "type": "community",
      "isActive": true
    }
  ]
  ```
</ResponseExample>

## Add Area

Adds a new area to a city (admin function).

<ParamField path="cityId" type="Id<'cities'>" required>
  City ID to add area to
</ParamField>

<ParamField path="name" type="string" required>
  Area name in English
</ParamField>

<ParamField path="nameArabic" type="string" required>
  Area name in Arabic
</ParamField>

<ParamField path="type" type="string" required>
  Area type: `district`, `neighborhood`, `community`, `sector`, or `zone`
</ParamField>

<CodeGroup>
  ```typescript TypeScript theme={null}
  const newArea = await convex.mutation(api.shared.locations.addArea, {
    cityId: "ct123456789",
    name: "Business Bay",
    nameArabic: "خليج الأعمال",
    type: "district"
  });
  ```

  ```javascript JavaScript theme={null}
  const newArea = await convex.mutation("shared.locations.addArea", {
    cityId: "ct123456789",
    name: "Business Bay",
    nameArabic: "خليج الأعمال",
    type: "district"
  });
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  {
    "_id": "ar789012345",
    "name": "Business Bay",
    "nameArabic": "خليج الأعمال",
    "city": "ct123456789",
    "type": "district",
    "isActive": true,
    "_creationTime": 1640995200000
  }
  ```
</ResponseExample>

## Clear Location Data

Clears all location data from the system (admin function - use with caution).

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.shared.locations.clearLocationData);
  ```

  ```javascript JavaScript theme={null}
  await convex.mutation("shared.locations.clearLocationData");
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  null
  ```
</ResponseExample>

## Location Picker Component

Here's a complete location picker component for your frontend:

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

  interface LocationPickerProps {
    onLocationSelect: (cityId: string, areaId: string) => void;
    selectedCityId?: string;
    selectedAreaId?: string;
  }

  function LocationPicker({ onLocationSelect, selectedCityId, selectedAreaId }: LocationPickerProps) {
    const [searchTerm, setSearchTerm] = useState('');
    
    const cities = useQuery(api.shared.locations.getCities);
    const areas = useQuery(
      api.shared.locations.getAreasByCity,
      selectedCityId ? { cityId: selectedCityId } : "skip"
    );
    const searchResults = useQuery(
      api.shared.locations.searchAreas,
      searchTerm.length > 2 ? { searchTerm } : "skip"
    );

    const handleCitySelect = (cityId: string) => {
      onLocationSelect(cityId, '');
    };

    const handleAreaSelect = (areaId: string) => {
      if (selectedCityId) {
        onLocationSelect(selectedCityId, areaId);
      }
    };

    return (
      <div className="location-picker">
        {/* Search */}
        <div className="mb-4">
          <input
            type="text"
            placeholder="Search areas..."
            value={searchTerm}
            onChange={(e) => setSearchTerm(e.target.value)}
            className="w-full p-2 border rounded"
          />
          
          {searchResults && searchResults.length > 0 && (
            <div className="mt-2 border rounded max-h-32 overflow-y-auto">
              {searchResults.map((area) => (
                <div
                  key={area._id}
                  onClick={() => {
                    handleCitySelect(area.city);
                    handleAreaSelect(area._id);
                    setSearchTerm('');
                  }}
                  className="p-2 hover:bg-gray-100 cursor-pointer"
                >
                  <div className="font-medium">{area.name}</div>
                  <div className="text-sm text-gray-600">{area.nameArabic} • {area.cityName}</div>
                </div>
              ))}
            </div>
          )}
        </div>

        {/* City Selection */}
        <div className="mb-4">
          <label className="block text-sm font-medium mb-2">City</label>
          <select
            value={selectedCityId || ''}
            onChange={(e) => handleCitySelect(e.target.value)}
            className="w-full p-2 border rounded"
          >
            <option value="">Select a city</option>
            {cities?.map((city) => (
              <option key={city._id} value={city._id}>
                {city.name} - {city.nameArabic}
              </option>
            ))}
          </select>
        </div>

        {/* Area Selection */}
        {selectedCityId && (
          <div className="mb-4">
            <label className="block text-sm font-medium mb-2">Area</label>
            <select
              value={selectedAreaId || ''}
              onChange={(e) => handleAreaSelect(e.target.value)}
              className="w-full p-2 border rounded"
            >
              <option value="">Select an area</option>
              {areas?.map((area) => (
                <option key={area._id} value={area._id}>
                  {area.name} - {area.nameArabic} ({area.type})
                </option>
              ))}
            </select>
          </div>
        )}
      </div>
    );
  }

  export default LocationPicker;
  ```

  ```javascript Vue Component theme={null}
  <template>
    <div class="location-picker">
      <!-- Search -->
      <div class="mb-4">
        <input
          v-model="searchTerm"
          type="text"
          placeholder="Search areas..."
          class="w-full p-2 border rounded"
        />
        
        <div v-if="searchResults && searchResults.length > 0" class="search-results">
          <div
            v-for="area in searchResults"
            :key="area._id"
            @click="selectFromSearch(area)"
            class="search-result-item"
          >
            <div class="font-medium">{{ area.name }}</div>
            <div class="text-sm text-gray-600">{{ area.nameArabic }} • {{ area.cityName }}</div>
          </div>
        </div>
      </div>

      <!-- City Selection -->
      <div class="mb-4">
        <label class="block text-sm font-medium mb-2">City</label>
        <select v-model="selectedCityId" @change="onCityChange" class="w-full p-2 border rounded">
          <option value="">Select a city</option>
          <option v-for="city in cities" :key="city._id" :value="city._id">
            {{ city.name }} - {{ city.nameArabic }}
          </option>
        </select>
      </div>

      <!-- Area Selection -->
      <div v-if="selectedCityId" class="mb-4">
        <label class="block text-sm font-medium mb-2">Area</label>
        <select v-model="selectedAreaId" @change="onAreaChange" class="w-full p-2 border rounded">
          <option value="">Select an area</option>
          <option v-for="area in areas" :key="area._id" :value="area._id">
            {{ area.name }} - {{ area.nameArabic }} ({{ area.type }})
          </option>
        </select>
      </div>
    </div>
  </template>

  <script>
  export default {
    props: {
      initialCityId: String,
      initialAreaId: String
    },
    
    emits: ['location-select'],
    
    data() {
      return {
        searchTerm: '',
        selectedCityId: this.initialCityId || '',
        selectedAreaId: this.initialAreaId || '',
        cities: [],
        areas: [],
        searchResults: []
      };
    },
    
    watch: {
      searchTerm: {
        handler: 'searchAreas',
        immediate: false
      },
      selectedCityId: 'loadAreas'
    },
    
    async mounted() {
      await this.loadCities();
      if (this.selectedCityId) {
        await this.loadAreas();
      }
    },
    
    methods: {
      async loadCities() {
        this.cities = await convex.query("shared.locations.getCities");
      },
      
      async loadAreas() {
        if (this.selectedCityId) {
          this.areas = await convex.query("shared.locations.getAreasByCity", {
            cityId: this.selectedCityId
          });
        }
      },
      
      async searchAreas() {
        if (this.searchTerm.length > 2) {
          this.searchResults = await convex.query("shared.locations.searchAreas", {
            searchTerm: this.searchTerm
          });
        } else {
          this.searchResults = [];
        }
      },
      
      selectFromSearch(area) {
        this.selectedCityId = area.city;
        this.selectedAreaId = area._id;
        this.searchTerm = '';
        this.searchResults = [];
        this.loadAreas();
        this.emitSelection();
      },
      
      onCityChange() {
        this.selectedAreaId = '';
        this.emitSelection();
      },
      
      onAreaChange() {
        this.emitSelection();
      },
      
      emitSelection() {
        this.$emit('location-select', {
          cityId: this.selectedCityId,
          areaId: this.selectedAreaId
        });
      }
    }
  };
  </script>
  ```
</CodeGroup>

## Area Types

The system supports different area types for better organization:

<CardGroup cols={2}>
  <Card title="District" icon="city">
    Large administrative divisions within cities (e.g., Downtown Dubai, Marina)
  </Card>

  <Card title="Neighborhood" icon="home">
    Residential areas within districts (e.g., Jumeirah 1, Al Barsha)
  </Card>

  <Card title="Community" icon="users">
    Planned communities or compounds (e.g., Arabian Ranches, The Springs)
  </Card>

  <Card title="Sector" icon="map">
    Industrial or commercial sectors (e.g., DIFC, DMCC)
  </Card>
</CardGroup>

## Error Handling

<AccordionGroup>
  <Accordion title="City not found">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "City not found",
      "cityId": "ct123456789"
    }
    ```
  </Accordion>

  <Accordion title="Area not found">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "Area not found",
      "areaId": "ar123456789"
    }
    ```
  </Accordion>

  <Accordion title="Duplicate area name">
    **Status Code**: `400`

    ```json theme={null}
    {
      "error": "Area with this name already exists in the city"
    }
    ```
  </Accordion>
</AccordionGroup>

<Note>
  The location system is specifically designed for the UAE market with comprehensive coverage of major cities and areas, including Arabic translations for better user experience.
</Note>

<Tip>
  Use the search functionality to provide users with a fast way to find their location, especially useful for areas with similar names across different cities.
</Tip>
