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

# Places Directory

> Comprehensive directory system for establishments and points of interest with multi-language support and dynamic attributes

# Places Directory

The Places Directory is Concordia's core feature for managing physical establishments and points of interest. It supports multi-language translations, dynamic attributes, and a complete ownership model where proprietors can manage their own listings.

## Overview

The directory system enables:

* Submission and management of places by proprietors (owners)
* Multi-language translations for international reach
* Dynamic attribute system for flexible place characteristics
* Moderation workflow with status management
* Integration with reviews, bookings, and services

## Place Entity

Based on the specifications in `concordia-specs.md`, places represent physical establishments or points of interest.

### Core Fields

<ParamField path="id" type="uuid" required>
  Unique identifier for the place
</ParamField>

<ParamField path="owner_id" type="uuid" required>
  Reference to the user who owns/manages this place. User must have the `owner` role.
</ParamField>

<ParamField path="category_id" type="uuid" required>
  Reference to a leaf category of type `place`. Categories can be hierarchical (max 3 levels).
</ParamField>

<ParamField path="slug" type="text" required>
  Unique URL-friendly identifier (2-220 chars, alphanumeric + hyphens)

  Example: `"hotel-du-parc-paris"`
</ParamField>

<ParamField path="type" type="enum" required>
  Place type from: `restaurant`, `hotel`, `camping`, `commerce`, `admin`, `activity`, `poi`, `trail`, `balade`, `randonnee`, `velo`
</ParamField>

<ParamField path="address_id" type="uuid">
  Optional reference to normalized address with geolocation
</ParamField>

<ParamField path="latitude" type="decimal(10,7)">
  Latitude coordinate (-90 to 90)

  Example: `48.8566969`
</ParamField>

<ParamField path="longitude" type="decimal(10,7)">
  Longitude coordinate (-180 to 180)

  Example: `2.3514616`
</ParamField>

### Contact Information

<ParamField path="email" type="text">
  Contact email for the establishment
</ParamField>

<ParamField path="phone" type="text">
  Phone number in E.164 format
</ParamField>

<ParamField path="website" type="text">
  Valid URL for the establishment's website
</ParamField>

### Business Details

<ParamField path="open_hours" type="jsonb">
  Structured JSON object with opening hours by day of week

  ```json theme={null}
  {
    "monday": [{"open": "07:00", "close": "23:00"}],
    "tuesday": [{"open": "07:00", "close": "23:00"}]
  }
  ```
</ParamField>

<ParamField path="accessibility" type="text[]">
  Accessibility features

  Example: `["wheelchair", "elevator"]`
</ParamField>

<ParamField path="audience" type="text[]">
  Target audience labels

  Example: `["families", "couples", "business"]`
</ParamField>

<ParamField path="price_range" type="enum">
  Price level: `low`, `medium`, `high`, `luxury`
</ParamField>

### Ratings

<ParamField path="rating_avg" type="decimal(2,1)">
  Average rating (0.0 to 5.0), calculated automatically from reviews

  Example: `4.2`
</ParamField>

<ParamField path="rating_count" type="integer">
  Total number of published reviews

  Example: `42`
</ParamField>

## Status Workflow

Places follow a state machine for moderation:

<ParamField path="status" type="enum" required default="pending_review">
  Current status: `pending_review`, `published`, `archived`, `rejected`
</ParamField>

### State Transitions

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending_review
    pending_review --> published : Admin approves
    pending_review --> rejected : Admin rejects
    published --> archived : Owner/Admin archives
    rejected --> pending_review : Owner resubmits
    archived --> pending_review : Owner reactivates
```

<Note>
  When status transitions to `published`, the `published_at` timestamp is automatically set.
</Note>

## Multi-Language Translations

From `concordia-specs.md` (lines 484-502), places support translations via the `place_translation` table:

### Translation Fields

<ParamField path="place_id" type="uuid" required>
  Reference to the parent place
</ParamField>

<ParamField path="language" type="text" required>
  ISO 639-1 language code: `fr`, `en`, `ar`, `es`
</ParamField>

<ParamField path="name" type="text" required>
  Translated name (2-200 chars)

  Example: `"Hôtel du Parc"`
</ParamField>

<ParamField path="description" type="text">
  Translated description (max 2000 chars)

  Example: `"Un hôtel charmant au cœur de la ville..."`
</ParamField>

<Info>
  A place must have at least one translation in the owner's preferred language. The composite key `(place_id, language)` ensures uniqueness.
</Info>

## Dynamic Attributes

The attribute system allows flexible, admin-defined characteristics for places.

### Attribute Definition

From `concordia-specs.md` (lines 337-359), administrators create attribute definitions:

<ParamField path="name" type="text" required>
  Attribute name (2-100 chars, globally unique)

  Example: `"Wifi"`
</ParamField>

<ParamField path="value_type" type="enum" required>
  Data type: `boolean`, `string`, `integer`, `decimal`, `enum`
</ParamField>

<ParamField path="possible_values" type="text[]">
  Required if `value_type` is `enum`

  Example: `["gratuit", "payant"]`
</ParamField>

<ParamField path="applicable_category_ids" type="uuid[]">
  Categories this attribute applies to (null = all categories)
</ParamField>

### Attribute Values

From `concordia-specs.md` (lines 504-521), owners set values via `place_attribute_value`:

<ParamField path="place_id" type="uuid" required>
  Reference to the place
</ParamField>

<ParamField path="attribute_id" type="uuid" required>
  Reference to the attribute definition
</ParamField>

<ParamField path="value_boolean" type="boolean">
  Populated if attribute type is `boolean`
</ParamField>

<ParamField path="value_string" type="text">
  Populated if attribute type is `string` or `enum` (max 255 chars)
</ParamField>

<ParamField path="value_integer" type="integer">
  Populated if attribute type is `integer`
</ParamField>

<ParamField path="value_decimal" type="decimal">
  Populated if attribute type is `decimal`
</ParamField>

<Warning>
  Exactly one `value_*` field must be non-null, matching the attribute definition's `value_type`.
</Warning>

## Ownership Model

Places follow the "Propriétaire" (owner) model from the specifications:

1. **User Role**: User must have the `owner` role in the `user_role` table
2. **Submission**: Owner creates a place with status `pending_review`
3. **Moderation**: Admin reviews and approves/rejects
4. **Management**: Owner can update, archive, and reactivate their places
5. **Restrictions**: Delete only allowed if no bookings or reviews exist

## Specialized Place Types

Based on place type, additional detail tables provide type-specific fields:

### Accommodation Details

From `concordia-specs.md` (lines 523-542):

```typescript theme={null}
{
  place_id: uuid,        // Primary key and foreign key
  stars: integer,        // 1-5 star rating
  accommodation_type: text,
  capacity: integer,
  pets_allowed: boolean,
  pool: boolean,
  spa: boolean,
  family_rooms: boolean,
  booking_url: text,
  availability: jsonb
}
```

### Gastronomy Details

From `concordia-specs.md` (lines 544-561):

```typescript theme={null}
{
  place_id: uuid,
  cuisine: text,
  price_range: enum,
  takeaway: boolean,
  delivery: boolean,
  vegan: boolean,
  brunch: boolean,
  seating_capacity: integer
}
```

### Activity Details

From `concordia-specs.md` (lines 563-579):

```typescript theme={null}
{
  place_id: uuid,
  duration_min: integer,
  min_age: integer,
  max_age: integer,
  price_min: decimal,
  price_max: decimal,
  seasons: text[]  // ["spring", "summer", "autumn", "winter"]
}
```

## Address Structure

From `concordia-specs.md` (lines 581-599), addresses are normalized:

```typescript theme={null}
{
  id: uuid,
  street: text,
  city: text,           // Required
  postcode: text,
  region: text,
  country: text,        // ISO 3166-1 alpha-2
  latitude: decimal(10,7),
  longitude: decimal(10,7)
}
```

## Integration Points

### With Reviews

Places can receive reviews that automatically update `rating_avg` and `rating_count` fields.

### With Services

Places can have associated services via the `local_service` table's `place_id` foreign key.

### With Bookings

Places can define availability schedules via `service_availability` for reservation systems.

### With Blog Articles

Articles can reference places via `article_place_link` for editorial content.

## Example JSON

From `concordia-specs.md` (lines 450-480):

```json theme={null}
{
  "id": "place-uuid-1",
  "owner_id": "user-uuid-1",
  "category_id": "cat-hotel-uuid",
  "slug": "hotel-du-parc-paris",
  "type": "hotel",
  "address_id": "addr-uuid-1",
  "latitude": 48.8566969,
  "longitude": 2.3514616,
  "email": "contact@hotelduparc.fr",
  "phone": "+33140000000",
  "website": "https://hotelduparc.fr",
  "open_hours": {
    "monday": [{"open": "07:00", "close": "23:00"}],
    "tuesday": [{"open": "07:00", "close": "23:00"}]
  },
  "accessibility": ["wheelchair", "elevator"],
  "audience": ["families", "couples", "business"],
  "price_range": "high",
  "rating_avg": 4.2,
  "rating_count": 42,
  "status": "published",
  "submitted_at": "2026-01-15T09:00:00Z",
  "published_at": "2026-01-16T14:30:00Z",
  "created_at": "2026-01-15T09:00:00Z",
  "updated_at": "2026-02-01T11:00:00Z"
}
```
