Skip to content

Commit 3a38e8a

Browse files
committed
Add neurotype and accessibility fields to profiles
- Added `accessibility_notes` (free text) to profiles to capture members' practical access needs while avoiding filterable attributes. - Introduced `neurotype` (selectable values with details option) and GIN indexing to enable efficient keyword searches. - Updated UI components, filters, and forms to support these new profile attributes. - Modified backend and tests to handle the changes and ensure consistency across filtering, seeding, and display logic. - Bumped API version to `1.49.0`.
1 parent cfa39ec commit 3a38e8a

23 files changed

Lines changed: 434 additions & 4 deletions

backend/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@compass/api",
3-
"version": "1.48.0",
3+
"version": "1.49.0",
44
"private": true,
55
"description": "Backend API endpoints",
66
"main": "src/serve.ts",

backend/api/src/get-profiles.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
GENDERS,
77
LANGUAGE_CHOICES,
88
MBTI_CHOICES,
9+
NEUROTYPE_CHOICES,
910
ORIENTATION_CHOICES,
1011
POLITICAL_CHOICES,
1112
RACE_CHOICES,
@@ -81,6 +82,7 @@ export type profileQueryType = {
8182
languages?: string[] | undefined
8283
religion?: string[] | undefined
8384
orientation?: string[] | undefined
85+
neurotype?: string[] | undefined
8486
wants_kids_strength?: number | undefined
8587
has_kids?: number | undefined
8688
is_smoker?: boolean | undefined
@@ -131,6 +133,9 @@ const textFields = [
131133
'religious_beliefs',
132134
'orientation_details',
133135
'gender_details',
136+
'neurotype_details',
137+
// Keyword-searchable but deliberately has no filter of its own — see the migration for why.
138+
'accessibility_notes',
134139
]
135140

136141
// Define choice fields to search
@@ -147,6 +152,7 @@ const arrayChoiceFields = [
147152
{field: 'relationship_status', choices: RELATIONSHIP_STATUS_CHOICES},
148153
{field: 'religion', choices: RELIGION_CHOICES},
149154
{field: 'orientation', choices: ORIENTATION_CHOICES},
155+
{field: 'neurotype', choices: NEUROTYPE_CHOICES},
150156
{field: 'pref_relation_styles', choices: RELATIONSHIP_CHOICES},
151157
{field: 'pref_romantic_styles', choices: ROMANTIC_CHOICES},
152158
{field: 'languages', choices: LANGUAGE_CHOICES},
@@ -211,6 +217,7 @@ export const loadProfiles = async (props: profileQueryType, db?: SupabaseDirectC
211217
languages,
212218
religion,
213219
orientation,
220+
neurotype,
214221
wants_kids_strength,
215222
has_kids,
216223
interests,
@@ -508,6 +515,17 @@ export const loadProfiles = async (props: profileQueryType, db?: SupabaseDirectC
508515
},
509516
),
510517

518+
neurotype?.length &&
519+
where(
520+
// most are neurotypical, so we don't include the people who didn't answer if we're looking for
521+
// anything else — a rare value is only meaningful when it was actually stated
522+
(!neurotype.includes('neurotypical') ? '' : "neurotype IS NULL OR neurotype = '{}' OR ") +
523+
`neurotype && $(neurotype)`,
524+
{
525+
neurotype,
526+
},
527+
),
528+
511529
interests?.length && where(getManyToManyClause('interests'), {values: interests.map(Number)}),
512530

513531
causes?.length && where(getManyToManyClause('causes'), {values: causes.map(Number)}),

backend/supabase/migration.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,6 @@ BEGIN;
5858
\i backend/supabase/email_unsubscribe_tokens.sql
5959
\i backend/supabase/migrations/20260524_add_orientation_to_profiles.sql
6060
\i backend/supabase/migrations/20260709_add_last_checked_at_to_bookmarked_searches.sql
61+
\i backend/supabase/migrations/20260724_add_neurotype_to_profiles.sql
62+
\i backend/supabase/migrations/20260724_add_accessibility_notes_to_profiles.sql
6163
COMMIT;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Add accessibility notes to profiles table
2+
-- Created: 2026-07-24
3+
--
4+
-- Deliberately free text rather than a checkbox taxonomy of conditions: a taxonomy exists mainly to be
5+
-- filtered on, and filtering people out by disability is exactly what this field must not enable. Free
6+
-- text keeps framing and depth of disclosure with the member. It is keyword-searchable (see `textFields`
7+
-- in backend/api/src/get-profiles.ts) but has no filter of its own.
8+
9+
ALTER TABLE profiles
10+
ADD COLUMN IF NOT EXISTS accessibility_notes TEXT;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Add neurotype (neurodivergence identity) fields to profiles table
2+
-- Created: 2026-07-24
3+
4+
ALTER TABLE profiles
5+
ADD COLUMN IF NOT EXISTS neurotype TEXT[],
6+
ADD COLUMN IF NOT EXISTS neurotype_details TEXT;
7+
8+
-- Create GIN index for array field
9+
CREATE INDEX IF NOT EXISTS profiles_neurotype_gin ON profiles USING GIN (neurotype);

common/messages/de.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@
387387
"filter.any_interests": "Jedes Interesse",
388388
"filter.any_language": "Alle Sprachen",
389389
"filter.any_mbti": "Jeder MBTI",
390+
"filter.any_neurotype": "Beliebiger Neurotyp",
390391
"filter.any_new_users": "Jegliche neue Nutzer",
391392
"filter.any_orientation": "Jede Orientierung",
392393
"filter.any_politics": "Jede politische Ausrichtung",
@@ -451,6 +452,7 @@
451452
"filter.mine_toggle": "Meine Filter",
452453
"filter.multiple": "Mehrere",
453454
"filter.near": "in der Nähe von",
455+
"filter.neurotype.show_more": "Mehr Neurotypen anzeigen",
454456
"filter.orientation.show_more": "Mehr Orientierungen anzeigen",
455457
"filter.raised_in": "Aufgewachsen",
456458
"filter.relationship.any_connection": "Jede Beziehung",
@@ -752,6 +754,7 @@
752754
"privacy.use.text": "Ihre Daten werden nur verwendet, um die Plattform zu betreiben, zu personalisieren und zu verbessern. Wir verkaufen Ihre persönlichen Informationen nicht an Dritte.",
753755
"privacy.use.title": "2. Wie wir Ihre Daten verwenden",
754756
"profile.about.raised_in": "Aufgewachsen in {location}",
757+
"profile.accessibility_notes": "Barrierefreiheit",
755758
"profile.age_any": "jeden Alters",
756759
"profile.age_between": "zwischen {min} und {max} Jahren",
757760
"profile.age_exact": "genau {min} Jahre",
@@ -1057,7 +1060,19 @@
10571060
"profile.mbti.ESFP": "Unterhalter",
10581061
"profile.mutual": "Gegenseitig",
10591062
"profile.mutual_interests": "Sie haben beide gemeinsame Interessen: {matches}",
1063+
"profile.neurotype": "Neurotyp",
1064+
"profile.neurotype.adhd": "ADHS",
1065+
"profile.neurotype.autistic": "Autistisch",
1066+
"profile.neurotype.details_placeholder": "Was hilfreich zu wissen ist über dein Denken oder Kommunizieren…",
1067+
"profile.neurotype.dyscalculic": "Dyskalkulie",
1068+
"profile.neurotype.dyslexic": "Legasthenie",
1069+
"profile.neurotype.hsp": "Hochsensibel (HSP)",
1070+
"profile.neurotype.neurotypical": "Neurotypisch",
1071+
"profile.neurotype.show_more": "Weitere Optionen anzeigen",
10601072
"profile.not_both_open_to_type": "Sie sind nicht beide für eine(n) {type} offen",
1073+
"profile.optional.accessibility_notes": "Barrierefreiheit",
1074+
"profile.optional.accessibility_notes_hint": "Optional und öffentlich wie der Rest deines Profils. Alles Praktische, das ein gutes Treffen erleichtert — Zugangsbedürfnisse, Energielevel, sensorische Vorlieben, ruhigere Orte. Sag so viel oder so wenig, wie du möchtest.",
1075+
"profile.optional.accessibility_notes_placeholder": "z. B. Ich nutze einen Rollstuhl, stufenlose Orte passen am besten. Fragen sind willkommen.",
10611076
"profile.optional.age": "Alter",
10621077
"profile.optional.age.error_max": "Bitte gib ein gültiges Alter ein",
10631078
"profile.optional.age.error_min": "Du musst mindestens 18 Jahre alt sein",
@@ -1101,6 +1116,7 @@
11011116
"profile.optional.languages": "Gesprochene Sprachen",
11021117
"profile.optional.location": "Standort",
11031118
"profile.optional.mbti": "MBTI-Persönlichkeitstyp",
1119+
"profile.optional.neurotype": "Neurotyp",
11041120
"profile.optional.num_kids": "Aktuelle Anzahl von Kindern",
11051121
"profile.optional.og_card": "Profilkarte",
11061122
"profile.optional.orientation": "Sexuelle Orientierung",

common/messages/fr.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@
387387
"filter.any_interests": "Tout intérêt",
388388
"filter.any_language": "Toutes les langues",
389389
"filter.any_mbti": "Tout MBTI",
390+
"filter.any_neurotype": "Tout neurotype",
390391
"filter.any_new_users": "Tout nouvel utilisateur",
391392
"filter.any_orientation": "Toute orientation",
392393
"filter.any_politics": "Toute orientation politique",
@@ -451,6 +452,7 @@
451452
"filter.mine_toggle": "Mes filtres",
452453
"filter.multiple": "Multiple",
453454
"filter.near": "près de",
455+
"filter.neurotype.show_more": "Afficher plus de neurotypes",
454456
"filter.orientation.show_more": "Afficher plus d'orientations",
455457
"filter.raised_in": "A grandi",
456458
"filter.relationship.any_connection": "Toute relation",
@@ -751,6 +753,7 @@
751753
"privacy.use.text": "Vos données sont utilisées uniquement pour faire fonctionner, personnaliser et améliorer la plateforme. Nous ne vendons pas vos informations personnelles à des tiers.",
752754
"privacy.use.title": "2. Comment nous utilisons vos données",
753755
"profile.about.raised_in": "A grandi à {location}",
756+
"profile.accessibility_notes": "Accessibilité",
754757
"profile.age_any": "de tout âge",
755758
"profile.age_between": "entre {min} et {max} ans",
756759
"profile.age_exact": "exactement {min} ans",
@@ -1056,7 +1059,19 @@
10561059
"profile.mbti.ESFP": "Animateur",
10571060
"profile.mutual": "Mutuel",
10581061
"profile.mutual_interests": "Vous avez tous les deux des intérêts communs : {matches}. Contactez-les ci-dessus!",
1062+
"profile.neurotype": "Neurotype",
1063+
"profile.neurotype.adhd": "TDAH",
1064+
"profile.neurotype.autistic": "Autiste",
1065+
"profile.neurotype.details_placeholder": "Ce qu'il est utile de savoir sur votre façon de penser ou de communiquer…",
1066+
"profile.neurotype.dyscalculic": "Dyscalculique",
1067+
"profile.neurotype.dyslexic": "Dyslexique",
1068+
"profile.neurotype.hsp": "Hypersensible (HSP)",
1069+
"profile.neurotype.neurotypical": "Neurotypique",
1070+
"profile.neurotype.show_more": "Afficher plus d'options",
10591071
"profile.not_both_open_to_type": "Vous n'êtes pas tous les deux ouverts à un(e) {type}",
1072+
"profile.optional.accessibility_notes": "Accessibilité",
1073+
"profile.optional.accessibility_notes_hint": "Facultatif, et public comme le reste de votre profil. Tout ce qui est utile en pratique pour bien vous rencontrer — besoins d'accès, niveau d'énergie, préférences sensorielles, lieux plus calmes. Dites-en autant ou aussi peu que vous voulez.",
1074+
"profile.optional.accessibility_notes_placeholder": "ex. Je me déplace en fauteuil roulant, les lieux sans marches sont donc plus simples. N'hésitez pas à poser des questions.",
10601075
"profile.optional.age": "Âge",
10611076
"profile.optional.age.error_max": "Veuillez entrer un âge valide",
10621077
"profile.optional.age.error_min": "Vous devez avoir au moins 18 ans",
@@ -1100,6 +1115,7 @@
11001115
"profile.optional.languages": "Langues parlées",
11011116
"profile.optional.location": "Localisation",
11021117
"profile.optional.mbti": "Type de personnalité MBTI",
1118+
"profile.optional.neurotype": "Neurotype",
11031119
"profile.optional.num_kids": "Nombre actuel d'enfants",
11041120
"profile.optional.og_card": "Carte de profil",
11051121
"profile.optional.orientation": "Orientation sexuelle",

common/src/api/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,7 @@ export const API = (_apiTypeCheck = {
762762
big5_neuroticism_max: z.coerce.number().optional(),
763763
religion: arraybeSchema.optional(),
764764
orientation: arraybeSchema.optional(),
765+
neurotype: arraybeSchema.optional(),
765766
pref_relation_styles: arraybeSchema.optional(),
766767
pref_romantic_styles: arraybeSchema.optional(),
767768
diet: arraybeSchema.optional(),

common/src/api/zod-types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ const optionalProfilesSchema = z.object({
123123
gender_details: z.string().optional().nullable(),
124124
orientation: z.array(z.string()).optional().nullable(),
125125
orientation_details: z.string().optional().nullable(),
126+
neurotype: z.array(z.string()).optional().nullable(),
127+
neurotype_details: z.string().optional().nullable(),
128+
accessibility_notes: z.string().optional().nullable(),
126129
religion: z.array(z.string()).optional().nullable(),
127130
religious_belief_strength: z.number().optional().nullable(),
128131
religious_beliefs: z.string().optional().nullable(),

common/src/choices.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,27 @@ export const EXTRA_ORIENTATIONS = Object.values(ORIENTATION_CHOICES).filter(
314314
(v) => !DEFAULT_ORIENTATIONS.includes(v as any),
315315
)
316316

317+
// Neurotype is framed as an identity, not a diagnosis: no "undiagnosed" or "prefer not to say" options,
318+
// and no psychiatric conditions. Anything a member doesn't want structured goes in `neurotype_details`.
319+
export const NEUROTYPE_CHOICES = {
320+
Autistic: 'autistic',
321+
ADHD: 'adhd',
322+
Neurotypical: 'neurotypical',
323+
// No combined "AuDHD" option on purpose — that is ticking Autistic and ADHD together.
324+
Dyslexic: 'dyslexic',
325+
Dyscalculic: 'dyscalculic',
326+
'Highly sensitive (HSP)': 'hsp',
327+
} as const
328+
329+
export const DEFAULT_NEUROTYPES = [
330+
NEUROTYPE_CHOICES.Autistic,
331+
NEUROTYPE_CHOICES.ADHD,
332+
NEUROTYPE_CHOICES.Neurotypical,
333+
]
334+
export const EXTRA_NEUROTYPES = Object.values(NEUROTYPE_CHOICES).filter(
335+
(v) => !DEFAULT_NEUROTYPES.includes(v as any),
336+
)
337+
317338
export const LAST_ONLINE_CHOICES = {
318339
now: 'Currently online',
319340
today: 'Today',
@@ -375,6 +396,7 @@ export const INVERTED_CANNABIS_CHOICES = invert(CANNABIS_CHOICES)
375396
export const INVERTED_SUBSTANCE_INTENTION_CHOICES = invert(SUBSTANCE_INTENTION_CHOICES)
376397
export const INVERTED_SUBSTANCE_PREFERENCE_CHOICES = invert(SUBSTANCE_PREFERENCE_CHOICES)
377398
export const INVERTED_ORIENTATION_CHOICES = invert(ORIENTATION_CHOICES)
399+
export const INVERTED_NEUROTYPE_CHOICES = invert(NEUROTYPE_CHOICES)
378400

379401
//Exported types for test files to use when referencing the keys of the choices objects
380402
export type PersonalityKey = keyof typeof MBTI_CHOICES

0 commit comments

Comments
 (0)