Skip to main content

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.
const cities = await convex.query(api.shared.locations.getCities);
[
  {
    "_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
  }
]

Get Areas by City

Retrieves all areas within a specific city.
cityId
Id<'cities'>
required
City ID to get areas for
const areas = await convex.query(api.shared.locations.getAreasByCity, { 
  cityId: "ct123456789" 
});
[
  {
    "_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
  }
]

Get Location Hierarchy

Retrieves the complete location hierarchy for the entire UAE.
const hierarchy = await convex.query(api.shared.locations.getLocationHierarchy);
{
  "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"
        }
      ]
    }
  ]
}

Get Location Details

Retrieves detailed location information for a specific city or area.
cityId
Id<'cities'>
City ID for city details
areaId
Id<'areas'>
Area ID for area details
// 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"
});
{
  "type": "city",
  "city": {
    "_id": "ct123456789",
    "name": "Dubai",
    "nameArabic": "دبي",
    "isActive": true,
    "sortOrder": 1,
    "areaCount": 45,
    "storeCount": 125
  }
}

Search Areas

Searches for areas by name (supports both English and Arabic).
searchTerm
string
required
Search term (supports partial matching)
// 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: "مارينا"
});
[
  {
    "_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
  }
]

Add Area

Adds a new area to a city (admin function).
cityId
Id<'cities'>
required
City ID to add area to
name
string
required
Area name in English
nameArabic
string
required
Area name in Arabic
type
string
required
Area type: district, neighborhood, community, sector, or zone
const newArea = await convex.mutation(api.shared.locations.addArea, {
  cityId: "ct123456789",
  name: "Business Bay",
  nameArabic: "خليج الأعمال",
  type: "district"
});
{
  "_id": "ar789012345",
  "name": "Business Bay",
  "nameArabic": "خليج الأعمال",
  "city": "ct123456789",
  "type": "district",
  "isActive": true,
  "_creationTime": 1640995200000
}

Clear Location Data

Clears all location data from the system (admin function - use with caution).
await convex.mutation(api.shared.locations.clearLocationData);
null

Location Picker Component

Here’s a complete location picker component for your frontend:
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;

Area Types

The system supports different area types for better organization:

District

Large administrative divisions within cities (e.g., Downtown Dubai, Marina)

Neighborhood

Residential areas within districts (e.g., Jumeirah 1, Al Barsha)

Community

Planned communities or compounds (e.g., Arabian Ranches, The Springs)

Sector

Industrial or commercial sectors (e.g., DIFC, DMCC)

Error Handling

Status Code: 404
{
  "error": "City not found",
  "cityId": "ct123456789"
}
Status Code: 404
{
  "error": "Area not found",
  "areaId": "ar123456789"
}
Status Code: 400
{
  "error": "Area with this name already exists in the city"
}
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.
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.