Champagne

Member attributes

Club-defined typed person attributes, how onboarding answers save to them, and how staff edit and filter them.

Each club defines its own list of member attributes. An attribute has a stable key, a label, a fixed type and, for choices, a list of options. Values live on the person record under the key, as typed JSON values. Onboarding questions can save their answer to an attribute or to a built-in person field, so renaming a question or asking the same thing in several onboardings never splits the data.

List attribute definitions

query PersonAttributes($clubId: ID! = "51000000-0000-4000-8000-000000000051") {
  personAttributes(clubId: $clubId) {
    id
    key
    label
    type
    options
  }
}

personAttributes(clubId) returns the club's definitions in creation order, then by label. Any staff member with access to the club can read them; they are used by the settings page, the onboarding editor, the people filters and the person editor.

TypeStored JSON valueCanonical text in the API
TEXTString, up to 10,000 charsThe text
NUMBERNumberPlain decimal, e.g. 42.5
DATEYYYY-MM-DD stringYYYY-MM-DD
BOOLEANtrue or falsetrue or false
CHOICEOne of optionsThe option

Numbers accept up to 15 integer digits and 6 decimals; a decimal comma is accepted on input and stored as a number.

Manage definitions

mutation CreatePersonAttribute($input: CreatePersonAttributeInput!) {
  createPersonAttribute(input: $input) {
    attribute {
      id
      key
      label
      type
      options
    }
    error
  }
}
{
  "input": {
    "clubId": "51000000-0000-4000-8000-000000000051",
    "key": "preferred_language",
    "label": "Preferred language",
    "type": "CHOICE",
    "options": ["French", "English"]
  }
}

Keys start with a lowercase letter and contain up to 40 lowercase letters, digits or underscores; they are unique per club. Labels contain 1–100 trimmed characters. CHOICE attributes need 2–50 distinct options of 1–100 characters; other types take no options. A key already used in the club returns error.

updatePersonAttribute(input: { clubId, id, label, options }) changes the label and, for choices, the options. The key and type cannot change. Updated options are copied into every onboarding question of the club that saves to this attribute. Existing person values are kept even when their option is removed.

deletePersonAttribute(clubId, id) returns an error naming the onboardings that still save to the attribute. Otherwise it deletes the definition and removes the value from every person of the club.

These three mutations require the settings_manage permission and record club.attribute_created, club.attribute_updated or club.attribute_deleted activity.

Read and edit a person's values

PersonDetail.attributes lists every attribute defined by the person's club, in definition order, with key, label, type, options and the person's value as canonical text, or null when unset. People without a club have no attributes.

mutation SetPersonAttributeValues($input: SetPersonAttributeValuesInput!) {
  setPersonAttributeValues(input: $input) {
    person {
      attributes {
        key
        value
      }
    }
    error
  }
}
{
  "input": {
    "id": "00000000-0000-4000-8000-000000000084",
    "clubId": "51000000-0000-4000-8000-000000000051",
    "values": [
      { "key": "newsletter", "value": "false" },
      { "key": "profession", "value": null }
    ]
  }
}

Only the listed keys change; null or blank clears a value. Each value is validated against the attribute's type and the first invalid one returns error such as Shoe size: Enter a number. without saving anything. At most 100 values are accepted at once and each key may appear once. The mutation requires members_manage and records a person.updated activity whose changes are keyed by attribute label.

Save onboarding answers

Short answer, long answer and options questions accept either personField or attributeKey on OnboardingQuestionInput (never both). The target fixes the question type and the applicant's input format, returned as OnboardingQuestion.format:

TargetQuestion typeformatOptions
PHONE_NUMBERSHORT_ANSWERPHONENone
BIRTH_DATESHORT_ANSWERDATENone
GENDEROPTIONSGENDERfemale, male, nonbinary, other
TEXT attributeSHORT_ANSWER, LONG_ANSWERTEXTNone
NUMBER attributeSHORT_ANSWERNUMBERNone
DATE attributeSHORT_ANSWERDATENone
BOOLEAN attributeOPTIONSBOOLEANtrue, false
CHOICE attributeOPTIONSCHOICEThe attribute's options

For options targets the server replaces the submitted options with the target's. Two questions of one onboarding cannot save to the same target, and an unknown key or an incompatible question type returns error from saveOnboarding. Questions without a target keep format: TEXT (CHOICE for options) and their answers stay on the application only.

Applicants' answers are validated by format when each step is saved: numbers, real YYYY-MM-DD dates, phone numbers of 4–50 digits, spaces, +, (, ), . or -, and birth dates not in the future. Numbers are stored in canonical form.

On acceptance, each answer is written to its target: phoneNumber, birthDate and gender columns, or the attribute under its key as a typed value. Answers whose attribute was deleted since the application started are skipped.

Filter people

peoplePage(attributes: [PersonAttributeFilter!]) combines up to 20 filters with the other people filters. Each filter names a key and:

  • TEXT: value matches a case-insensitive substring;
  • BOOLEAN and CHOICE: value matches exactly (true/false for booleans);
  • NUMBER and DATE: inclusive from and to bounds, either optional;
  • without a value or bound, the filter keeps people who have any value.

An unknown key matches nobody. Values and bounds are limited to 200 characters.

{
  "clubId": "51000000-0000-4000-8000-000000000051",
  "attributes": [
    { "key": "preferred_language", "value": "French" },
    { "key": "newsletter", "value": "true" }
  ]
}

Sur cette page