{
 "$schema": "https://json-schema.org/draft/2020-12/schema",
 "$id": "https://elsegundo.xyz/directory/schema.json",
 "title": "El Segundo local directory (elsegundo-directory/1)",
 "description": "Two kinds of record. A business is a checked yellow pages listing read from the business's own page. A person is a white pages listing that the person wrote and confirmed themself.",
 "$defs": {
  "business": {
   "type": "object",
   "additionalProperties": false,
   "required": [
    "id",
    "name",
    "kind",
    "category",
    "subcategory",
    "address",
    "phone",
    "website",
    "hoursUrl",
    "note",
    "sourceQuote",
    "sourceUrls",
    "label",
    "status",
    "lastChecked",
    "checkedBy",
    "latitude",
    "longitude",
    "coordSource",
    "distanceMiles"
   ],
   "properties": {
    "id": {
     "type": "string",
     "pattern": "^[a-z]{2}-[a-z0-9-]+$",
     "description": "Town prefix (es, mb, hb, rb...) plus a slug."
    },
    "name": {
     "type": "string",
     "description": "As the business writes it."
    },
    "kind": {
     "const": "business"
    },
    "category": {
     "enum": [
      "Food & drink",
      "Groceries & markets",
      "Shopping",
      "Health",
      "Pets",
      "Money",
      "Beauty & wellness",
      "Local services",
      "Arts & entertainment",
      "Automotive",
      "Home & garden"
     ]
    },
    "subcategory": {
     "type": "string"
    },
    "address": {
     "type": "object",
     "additionalProperties": false,
     "required": [
      "street",
      "city",
      "state",
      "postcode"
     ],
     "properties": {
      "street": {
       "type": "string"
      },
      "city": {
       "type": "string"
      },
      "state": {
       "const": "CA"
      },
      "postcode": {
       "type": "string",
       "pattern": "^9\\d{4}$"
      }
     }
    },
    "latitude": {
     "type": [
      "number",
      "null"
     ]
    },
    "longitude": {
     "type": [
      "number",
      "null"
     ]
    },
    "coordSource": {
     "type": [
      "string",
      "null"
     ]
    },
    "distanceMiles": {
     "type": [
      "number",
      "null"
     ],
     "maximum": 25,
     "description": "Straight line from central El Segundo."
    },
    "phone": {
     "type": [
      "string",
      "null"
     ],
     "description": "The public business phone as its own page shows it. Null when the page shows none."
    },
    "website": {
     "type": "string",
     "format": "uri"
    },
    "hoursUrl": {
     "type": [
      "string",
      "null"
     ],
     "format": "uri",
     "description": "Where the business publishes its hours. We don't copy hours, because they change."
    },
    "note": {
     "type": "string",
     "maxLength": 120,
     "description": "Our own one-line summary."
    },
    "sourceQuote": {
     "type": [
      "string",
      "null"
     ],
     "description": "Word for word from the source page."
    },
    "sourceUrls": {
     "type": "array",
     "items": {
      "type": "string",
      "format": "uri"
     },
     "minItems": 1
    },
    "caveat": {
     "type": "string"
    },
    "osmUrl": {
     "type": "string",
     "format": "uri"
    },
    "publicRoles": {
     "type": "array",
     "description": "People named in a public-facing role on the business's own page, quoted.",
     "items": {
      "type": "object",
      "additionalProperties": false,
      "required": [
       "name",
       "role",
       "quote",
       "sourceUrl"
      ],
      "properties": {
       "name": {
        "type": "string"
       },
       "role": {
        "type": "string"
       },
       "quote": {
        "type": "string"
       },
       "sourceUrl": {
        "type": "string",
        "format": "uri"
       }
      }
     }
    },
    "label": {
     "const": "listing"
    },
    "status": {
     "enum": [
      "checked"
     ]
    },
    "lastChecked": {
     "type": "string",
     "format": "date"
    },
    "checkedBy": {
     "type": "string"
    },
    "confirmation": {
     "description": "Added by the API, never in businesses.json. Null until the business confirms its own listing by putting a one-time code on the website in its listing.",
     "oneOf": [
      {
       "type": "null"
      },
      {
       "$ref": "#/$defs/confirmation"
      }
     ]
    }
   }
  },
  "person": {
   "type": "object",
   "additionalProperties": false,
   "required": [
    "id",
    "kind",
    "displayName",
    "area",
    "whatTheyDo",
    "reach",
    "listedAt",
    "updatedAt",
    "consent",
    "label",
    "verified"
   ],
   "properties": {
    "id": {
     "type": "string",
     "pattern": "^pp_[a-f0-9]{16}$"
    },
    "kind": {
     "const": "person"
    },
    "displayName": {
     "type": "string",
     "minLength": 2,
     "maxLength": 40,
     "description": "However they want to be known: a full name, a first name or a nickname."
    },
    "area": {
     "enum": [
      "El Segundo",
      "Manhattan Beach",
      "Hermosa Beach",
      "Redondo Beach",
      "Torrance",
      "Hawthorne",
      "Lawndale",
      "Inglewood",
      "Gardena",
      "Westchester",
      "Playa del Rey",
      "Playa Vista",
      "Marina del Rey",
      "Venice",
      "Culver City",
      "Santa Monica",
      "Mar Vista",
      "Palos Verdes Peninsula",
      "Lomita",
      "Carson",
      "San Pedro",
      "Long Beach",
      "Downtown Los Angeles",
      "Elsewhere within 25 miles"
     ],
     "description": "A town, never a street address."
    },
    "whatTheyDo": {
     "type": "string",
     "minLength": 10,
     "maxLength": 140
    },
    "reach": {
     "type": "object",
     "additionalProperties": false,
     "description": "Only what they chose to share.",
     "properties": {
      "website": {
       "type": "string",
       "format": "uri",
       "pattern": "^https://"
      },
      "x": {
       "type": "string",
       "pattern": "^@[A-Za-z0-9_]{1,15}$"
      },
      "publicEmail": {
       "type": "string",
       "format": "email"
      },
      "pickleball": {
       "type": "string",
       "description": "Slug of their player page at /pickleball/players/<slug>/"
      },
      "ping": {
       "type": "string",
       "description": "Their name on the community ping board at /ping/"
      }
     }
    },
    "listedAt": {
     "type": "string",
     "format": "date-time"
    },
    "updatedAt": {
     "type": "string",
     "format": "date-time"
    },
    "consent": {
     "const": "self-listed"
    },
    "label": {
     "const": "self-listed"
    },
    "verified": {
     "const": false
    }
   }
  },
  "listingInput": {
   "type": "object",
   "additionalProperties": false,
   "required": [
    "displayName",
    "area",
    "whatTheyDo",
    "thisIsMe"
   ],
   "description": "What POST /api/v1/directory/people/draft takes inside {\"listing\": ...}. No phone numbers, street addresses, links or emails in the text fields.",
   "properties": {
    "displayName": {
     "type": "string"
    },
    "area": {
     "enum": [
      "El Segundo",
      "Manhattan Beach",
      "Hermosa Beach",
      "Redondo Beach",
      "Torrance",
      "Hawthorne",
      "Lawndale",
      "Inglewood",
      "Gardena",
      "Westchester",
      "Playa del Rey",
      "Playa Vista",
      "Marina del Rey",
      "Venice",
      "Culver City",
      "Santa Monica",
      "Mar Vista",
      "Palos Verdes Peninsula",
      "Lomita",
      "Carson",
      "San Pedro",
      "Long Beach",
      "Downtown Los Angeles",
      "Elsewhere within 25 miles"
     ]
    },
    "whatTheyDo": {
     "type": "string"
    },
    "reach": {
     "$ref": "#/$defs/person/properties/reach"
    },
    "thisIsMe": {
     "const": true
    }
   }
  },
  "confirmation": {
   "type": "object",
   "additionalProperties": false,
   "required": [
    "status",
    "label",
    "confirmedAt",
    "says",
    "fixes",
    "method",
    "pageChecked",
    "note"
   ],
   "properties": {
    "status": {
     "const": "confirmed-by-business"
    },
    "label": {
     "const": "Confirmed by the business"
    },
    "confirmedAt": {
     "type": "string",
     "format": "date-time"
    },
    "says": {
     "enum": [
      "correct",
      "sent-fixes"
     ]
    },
    "fixes": {
     "type": "object",
     "additionalProperties": false,
     "properties": {
      "phone": {
       "type": "string"
      },
      "website": {
       "type": "string",
       "format": "uri"
      },
      "hoursUrl": {
       "type": "string",
       "format": "uri"
      },
      "note": {
       "type": "string",
       "maxLength": 200
      }
     },
     "description": "The business's own words. The checked fields are unchanged until the next check."
    },
    "method": {
     "const": "code-on-own-website"
    },
    "pageChecked": {
     "type": "string",
     "format": "uri"
    },
    "note": {
     "type": "string"
    }
   }
  },
  "confirmStart": {
   "type": "object",
   "additionalProperties": false,
   "required": [
    "id",
    "report"
   ],
   "description": "POST /api/v1/directory/businesses/confirm/start, from https://elsegundo.xyz/directory/confirm/ only.",
   "properties": {
    "id": {
     "type": "string",
     "pattern": "^[a-z]{2}-[a-z0-9-]+$"
    },
    "report": {
     "type": "object",
     "additionalProperties": false,
     "required": [
      "correct",
      "thisIsMyBusiness"
     ],
     "properties": {
      "correct": {
       "type": "boolean"
      },
      "thisIsMyBusiness": {
       "const": true
      },
      "fixes": {
       "type": "object",
       "additionalProperties": false,
       "properties": {
        "phone": {
         "type": "string"
        },
        "website": {
         "type": "string"
        },
        "hoursUrl": {
         "type": "string"
        },
        "note": {
         "type": "string",
         "minLength": 10,
         "maxLength": 200
        }
       }
      }
     }
    }
   }
  }
 }
}
