abmp-npm 2.0.77 → 2.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/package.json +8 -1
  2. package/.claude/skills/wix-data-query/SKILL.md +0 -167
  3. package/.claude/skills/wix-data-query/references/backup-archive.md +0 -136
  4. package/.claude/skills/wix-data-query/references/members-data-latest.md +0 -125
  5. package/.claude/skills/wix-data-query/references/recipes.md +0 -264
  6. package/.claude/worktrees/fix-save-button-disable/.husky/pre-commit +0 -19
  7. package/.claude/worktrees/fix-save-button-disable/.prettierignore +0 -7
  8. package/.claude/worktrees/fix-save-button-disable/.prettierrc.json +0 -16
  9. package/.claude/worktrees/fix-save-button-disable/README.md +0 -1
  10. package/.claude/worktrees/fix-save-button-disable/backend/README-member-contact-flows.md +0 -136
  11. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  12. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  13. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/login-email-sync.test.js +0 -49
  14. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  15. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/url-uniqueness.test.js +0 -194
  16. package/.claude/worktrees/fix-save-button-disable/backend/automations-methods.js +0 -13
  17. package/.claude/worktrees/fix-save-button-disable/backend/cms-data-methods.js +0 -251
  18. package/.claude/worktrees/fix-save-button-disable/backend/consts.js +0 -58
  19. package/.claude/worktrees/fix-save-button-disable/backend/contacts-methods.js +0 -67
  20. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/bulk-process-methods.js +0 -222
  21. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/consts.js +0 -39
  22. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/index.js +0 -5
  23. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/process-member-methods.js +0 -213
  24. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/schedule-methods.js +0 -41
  25. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/sync-to-cms-methods.js +0 -161
  26. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/utils.js +0 -151
  27. package/.claude/worktrees/fix-save-button-disable/backend/data-hooks.js +0 -29
  28. package/.claude/worktrees/fix-save-button-disable/backend/dev-only-methods.js +0 -58
  29. package/.claude/worktrees/fix-save-button-disable/backend/elevated-modules.js +0 -18
  30. package/.claude/worktrees/fix-save-button-disable/backend/forms-methods.js +0 -52
  31. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/httpFunctions.js +0 -86
  32. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/index.js +0 -3
  33. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/interests.js +0 -35
  34. package/.claude/worktrees/fix-save-button-disable/backend/index.js +0 -19
  35. package/.claude/worktrees/fix-save-button-disable/backend/jobs.js +0 -158
  36. package/.claude/worktrees/fix-save-button-disable/backend/login/generate-member-session-token.js +0 -27
  37. package/.claude/worktrees/fix-save-button-disable/backend/login/index.js +0 -8
  38. package/.claude/worktrees/fix-save-button-disable/backend/login/qa-login-methods.js +0 -54
  39. package/.claude/worktrees/fix-save-button-disable/backend/login/sso-methods.js +0 -159
  40. package/.claude/worktrees/fix-save-button-disable/backend/member-contact-orchestration.js +0 -111
  41. package/.claude/worktrees/fix-save-button-disable/backend/members-area-methods.js +0 -88
  42. package/.claude/worktrees/fix-save-button-disable/backend/members-data-methods.js +0 -789
  43. package/.claude/worktrees/fix-save-button-disable/backend/pac-api-methods.js +0 -54
  44. package/.claude/worktrees/fix-save-button-disable/backend/routers/index.js +0 -3
  45. package/.claude/worktrees/fix-save-button-disable/backend/routers/methods.js +0 -191
  46. package/.claude/worktrees/fix-save-button-disable/backend/routers/utils.js +0 -128
  47. package/.claude/worktrees/fix-save-button-disable/backend/search-filters-methods.js +0 -124
  48. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-primary-methods.js +0 -256
  49. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-visibility-methods.js +0 -160
  50. package/.claude/worktrees/fix-save-button-disable/backend/tasks/consts.js +0 -34
  51. package/.claude/worktrees/fix-save-button-disable/backend/tasks/daily-pull-check-methods.js +0 -78
  52. package/.claude/worktrees/fix-save-button-disable/backend/tasks/email-normalize-methods.js +0 -134
  53. package/.claude/worktrees/fix-save-button-disable/backend/tasks/index.js +0 -10
  54. package/.claude/worktrees/fix-save-button-disable/backend/tasks/migration-methods.js +0 -46
  55. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-configs.js +0 -251
  56. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-helpers-methods.js +0 -433
  57. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-process-methods.js +0 -606
  58. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-migration-methods.js +0 -378
  59. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-space-fix-methods.js +0 -174
  60. package/.claude/worktrees/fix-save-button-disable/backend/utils.js +0 -277
  61. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  62. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-ids.js +0 -159
  63. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-urls.js +0 -201
  64. package/.claude/worktrees/fix-save-button-disable/eslint.config.js +0 -120
  65. package/.claude/worktrees/fix-save-button-disable/index.js +0 -5
  66. package/.claude/worktrees/fix-save-button-disable/package-lock.json +0 -12160
  67. package/.claude/worktrees/fix-save-button-disable/package.json +0 -60
  68. package/.claude/worktrees/fix-save-button-disable/pages/ContactUs.js +0 -129
  69. package/.claude/worktrees/fix-save-button-disable/pages/Home.js +0 -779
  70. package/.claude/worktrees/fix-save-button-disable/pages/LearnMore.js +0 -27
  71. package/.claude/worktrees/fix-save-button-disable/pages/LoadingPage.js +0 -29
  72. package/.claude/worktrees/fix-save-button-disable/pages/Profile.js +0 -354
  73. package/.claude/worktrees/fix-save-button-disable/pages/QAPage.js +0 -39
  74. package/.claude/worktrees/fix-save-button-disable/pages/SaveAlerts.js +0 -13
  75. package/.claude/worktrees/fix-save-button-disable/pages/SelectBannerImages.js +0 -46
  76. package/.claude/worktrees/fix-save-button-disable/pages/deleteConfirm.js +0 -19
  77. package/.claude/worktrees/fix-save-button-disable/pages/index.js +0 -12
  78. package/.claude/worktrees/fix-save-button-disable/pages/personalDetails.js +0 -2486
  79. package/.claude/worktrees/fix-save-button-disable/public/Utils/homePage.js +0 -798
  80. package/.claude/worktrees/fix-save-button-disable/public/Utils/personalDetailsUtils.js +0 -25
  81. package/.claude/worktrees/fix-save-button-disable/public/Utils/sharedUtils.js +0 -235
  82. package/.claude/worktrees/fix-save-button-disable/public/consts.js +0 -113
  83. package/.claude/worktrees/fix-save-button-disable/public/index.js +0 -5
  84. package/.claude/worktrees/fix-save-button-disable/public/messages.js +0 -16
  85. package/.claude/worktrees/fix-save-button-disable/public/sso-auth-methods.js +0 -43
  86. package/.husky/pre-commit +0 -19
  87. package/.prettierignore +0 -7
  88. package/.prettierrc.json +0 -16
  89. package/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  90. package/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  91. package/backend/__tests__/login-email-sync.test.js +0 -49
  92. package/backend/__tests__/map-link.test.js +0 -115
  93. package/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  94. package/backend/__tests__/url-uniqueness.test.js +0 -194
  95. package/backend/__tests__/url-validation.test.js +0 -69
  96. package/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  97. package/dev-only-scripts/find-duplicate-ids.js +0 -159
  98. package/dev-only-scripts/find-duplicate-urls.js +0 -201
  99. package/eslint.config.js +0 -120
package/package.json CHANGED
@@ -1,7 +1,14 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.77",
3
+ "version": "2.0.78",
4
4
  "main": "index.js",
5
+ "files": [
6
+ "index.js",
7
+ "backend",
8
+ "pages",
9
+ "public",
10
+ "!backend/__tests__"
11
+ ],
5
12
  "scripts": {
6
13
  "check-cycles": "madge --circular .",
7
14
  "test": "jest backend/__tests__",
@@ -1,167 +0,0 @@
1
- ---
2
- name: wix-data-query
3
- description: Query the ABMP/ASCP/AHP Wix CMS collections (MembersDataLatest, SiteConfigs, etc.) via the Wix Data REST API to investigate member data issues. Use when a bug report, Monday ticket, or support escalation references a specific member ID, profile slug, or email — e.g. "services don't appear on the website", "book now link missing", "member still showing after they dropped", "expired license rendering", "address/lat-long is wrong", "upgraded membership didn't sync". Also use when you need a collection's real field names, types, or query operators before writing backend code.
4
- ---
5
-
6
- # Wix Data queries for member data investigations
7
-
8
- Read the live CMS record before theorising. Most "the website is wrong" tickets are
9
- answered in one query: the site renders what is in `MembersDataLatest`, so if the field
10
- is wrong there, the bug is in the sync (`backend/daily-pull/`), not in the UI.
11
-
12
- ## 1. Pick the site
13
-
14
- Every association is a **separate Wix site with its own copy of the collections**. A member
15
- who exists on ABMP may not exist on ASCP. Always confirm which site the ticket is about —
16
- the profile URL tells you (`abmpmembers.com` → ABMP, `ascpskincare.com` → ASCP).
17
-
18
- | Site | siteId | Env |
19
- | ---------------------- | -------------------------------------- | ---- |
20
- | ABMP Members Directory | `384d680a-2870-4086-bda0-9894ce4503b8` | prod |
21
- | ASCP Members Directory | `1cb02bba-3a36-45e0-bdb4-1a1a2cfe2fdc` | prod |
22
- | AHP Members Directory | `5553798e-c71e-4a58-9b9e-515803823429` | prod |
23
- | Test ABMP Members | `cd9fca47-63d3-4538-b26c-1f91ad0a9420` | test |
24
- | Test ASCP Members | `8c031731-3f58-4d5f-b7dc-6ccabd1b5722` | test |
25
- | Test AHP Members | `4535a35f-439d-4558-8e68-9000258e2a2a` | test |
26
-
27
- Collection IDs are in [`public/consts.js`](../../../public/consts.js) under `COLLECTIONS`.
28
- The main one is `MembersDataLatest`.
29
-
30
- ## 2. Pick the auth path
31
-
32
- **Path A — Wix MCP (preferred when the `CallWixSiteAPI` tool is available).** No secrets to
33
- handle; auth is already managed. This is the path used to validate every recipe in this skill.
34
-
35
- ```
36
- CallWixSiteAPI(
37
- siteId: "384d680a-2870-4086-bda0-9894ce4503b8",
38
- url: "https://www.wixapis.com/wix-data/v2/items/query",
39
- method: "POST",
40
- sourceDocUrl: "https://dev.wix.com/docs/api-reference/business-solutions/cms/data-items/query-data-items",
41
- body: { ...see recipes... }
42
- )
43
- ```
44
-
45
- **Path B — raw REST**, for scripts, CI, or any session without the MCP. Requires an admin API
46
- key from the [API Keys Manager](https://manage.wix.com/account/api-keys). Two headers, per the
47
- [auth docs](https://dev.wix.com/docs/api-reference/articles/authentication/api-keys/make-api-calls-with-an-api-key):
48
-
49
- ```bash
50
- curl -s -X POST 'https://www.wixapis.com/wix-data/v2/items/query' \
51
- -H "Authorization: $WIX_API_KEY" \
52
- -H "wix-site-id: 384d680a-2870-4086-bda0-9894ce4503b8" \
53
- -H 'Content-Type: application/json' \
54
- -d '{"dataCollectionId":"MembersDataLatest","query":{"filter":{"memberId":731898},"paging":{"limit":1}}}'
55
- ```
56
-
57
- Read the key from the environment (`$WIX_API_KEY`). Never paste a key into a file, a commit,
58
- a Monday comment, or a chat message. `MembersDataLatest` is `read: ADMIN` /
59
- `itemRead: PRIVILEGED`, so a visitor token will return nothing — this is expected, not a bug.
60
-
61
- ## 3. Query
62
-
63
- Full cookbook with copy-paste bodies: [references/recipes.md](references/recipes.md).
64
- The single most common one — look a member up by profile slug:
65
-
66
- ```json
67
- {
68
- "dataCollectionId": "MembersDataLatest",
69
- "query": { "filter": { "url": "karriknowles" }, "paging": { "limit": 1 } }
70
- }
71
- ```
72
-
73
- By numeric PAC member ID (note: **number, not string**):
74
-
75
- ```json
76
- {
77
- "dataCollectionId": "MembersDataLatest",
78
- "query": { "filter": { "memberId": 731898 }, "paging": { "limit": 1 } }
79
- }
80
- ```
81
-
82
- ## 4. Read the result against the field reference
83
-
84
- [references/members-data-latest.md](references/members-data-latest.md) lists every field, its
85
- type, and — importantly — the **traps**. The ones that cause wrong conclusions:
86
-
87
- - **`firstName`, `lastName`, `phone`, `toShowPhone` are encrypted.** They support only
88
- `EQ`, `NE`, `HAS_SOME`, `EXISTS`. A `CONTAINS`/`STARTS_WITH` name search **fails** — it does
89
- not silently return nothing, it errors. Search by `fullName` (not encrypted) instead.
90
- - **Duplicate legacy field pairs exist**: `showAbmp` _and_ `showABMP`, `apiBookingUrl` _and_
91
- `APIBookingUrl`. Check both before concluding a value is missing.
92
- - **A `show*` boolean gates almost every "X doesn't appear on the site" ticket.** The data can
93
- be perfectly correct and still not render because `showBookingUrl` / `showWebsite` /
94
- `showContactForm` / `showName` is `false`. Check the flag before blaming the sync.
95
- - **`isVisible` and `action`** control directory presence. `action: "drop"` sets
96
- `isVisible: false` (see `backend/daily-pull/process-member-methods.js`). A member who should
97
- have been dropped but is still listed will show `action` other than `drop`, or
98
- `isVisible: true` — that points at the PAC API payload, not at Wix.
99
- - **Two date formats, and mixing them fails silently.** `_createdDate` / `_updatedDate` are
100
- real `DATETIME` fields using `{"$date":"...Z"}`. But dates inside the `memberships` and
101
- `licenses` arrays are **plain ISO strings** (`"2027-06-12T00:00:00"` — no `Z`, no ms) and
102
- must be compared as strings. Verified on ABMP prod: filtering
103
- `memberships.expiration` with `$date` returns **0**; the same filter as a string returns
104
- **1008**. A zero here is far more often a wrong filter than a clean bill of health.
105
- - **Totals:** `returnTotalCount` does not return a `total` on this collection. Use
106
- `POST https://www.wixapis.com/wix-data/v2/items/count` (body: `dataCollectionId` +
107
- top-level `filter`, no `query` wrapper) → `{"totalCount": N}`.
108
-
109
- - **`$eq` is case-sensitive; `$contains` is case-insensitive.** Verified on live ABMP: filtering
110
- `url` with `"alisadanaeknowles"` matches the stored `"AlisaDanaeKnowles"` under `$contains`
111
- and returns **nothing** under `$eq`. Stored slugs really are mixed case, so an exact-match
112
- lookup on a user-supplied slug will silently miss. This is why `getMemberBySlug` originally
113
- reached for full-text search.
114
-
115
- ## 5. The historical PAC feed archive
116
-
117
- The live CMS answers _"what is true now"_. It cannot answer _"what did PAC actually send"_ or
118
- _"what did this member originally ask for"_ — and those are usually the questions that settle a
119
- dispute. For that, use the backup archive: [references/backup-archive.md](references/backup-archive.md).
120
-
121
- ## 6. Writing data
122
-
123
- Read [§7 Rules](#7-rules) first. Writes need explicit approval, every time.
124
-
125
- **Prefer `PATCH` over `PUT`.** `PUT` (Update Data Item) replaces the entire payload — miss a
126
- field and you wipe it. `PATCH` changes only what you name:
127
-
128
- ```
129
- PATCH https://www.wixapis.com/wix-data/v2/items/<itemId>
130
- {
131
- "dataCollectionId": "MembersDataLatest",
132
- "patch": {
133
- "dataItemId": "<itemId>",
134
- "fieldModifications": [
135
- { "fieldPath": "optOut", "action": "SET_FIELD", "setFieldOptions": { "value": true } }
136
- ]
137
- }
138
- }
139
- ```
140
-
141
- **Hiding a member is better than deleting one.** `optOut: true` is reversible, keeps the record,
142
- and **survives the nightly sync** — `optOut` is written only in `getNewMemberOnlyFields`, which
143
- returns `{}` for members that already exist, and it is absent from the always-update list in
144
- `backend/daily-pull/process-member-methods.js`. Deleting is irreversible, and if the member is
145
- still in PAC's feed the next sync simply recreates them as new.
146
-
147
- Deleting, when that is genuinely what was asked for:
148
-
149
- ```
150
- DELETE https://www.wixapis.com/wix-data/v2/items/<itemId>?dataCollectionId=MembersDataLatest
151
- ```
152
-
153
- Always verify afterwards with `/items/count` on the member id and report the result.
154
-
155
- ## 7. Rules
156
-
157
- - **Read-only by default.** Query, count, distinct, aggregate, get-schema are all fine to run
158
- unprompted during an investigation.
159
- - **Never write to a production collection without explicit approval in this conversation.**
160
- Inserts, updates, patches, and `TRUNCATE` change live member-facing data. State exactly what
161
- you intend to change and on which site, and wait for a yes. When a fix needs testing, use the
162
- Test site IDs above.
163
- - **Don't paste member PII into external systems.** These records contain real names, emails,
164
- phone numbers, and home addresses. Quote the minimum needed — a member ID and the one wrong
165
- field — when writing a Monday comment or a commit message.
166
- - Ground endpoints in docs, not memory. If you need an endpoint this skill doesn't cover, find
167
- it with `SearchWixRESTDocumentation` first.
@@ -1,136 +0,0 @@
1
- # The PAC feed archive (`abmp-backup-api`)
2
-
3
- A local archive of **raw PAC API responses**, in the repo behind `BACKUP_API_URL`
4
- (`backend/consts.js`). On this machine:
5
-
6
- ```
7
- /Users/Matheusa/Documents/GitHub/abmp-backup-api/backups/
8
- ```
9
-
10
- It is the only record of **what PAC actually sent** and of members' **pre-migration display
11
- preferences**. Neither exists anywhere in the live CMS.
12
-
13
- ## Layout
14
-
15
- Bucketed by the PAC `action` value; each file is a verbatim API page.
16
-
17
- | Folder | Files | Records |
18
- | ------------------ | --------------- | ---------------------- |
19
- | `new/` | `page1.json` | 233 |
20
- | `new_Nov18th/` | `page1..5.json` | 11,960 (full snapshot) |
21
- | `update/` | `page1.json` | 954 |
22
- | `drop/` | `page1.json` | **416** |
23
- | `failed requests/` | — | empty |
24
-
25
- 13,563 records, 13,478 unique members, all four associations
26
- (ABMP 5,586 / ASCP 6,656 / AHP 1,526 / ANP 176 memberships). Captured **Nov–Dec 2025**.
27
-
28
- Shape matches the live PAC API exactly:
29
-
30
- ```json
31
- {
32
- "total_results": 954,
33
- "page_results": 954,
34
- "total_pages": 1,
35
- "results": [
36
- {
37
- "memberid": 1493418,
38
- "firstname": "Andrea",
39
- "lastname": "Williams",
40
- "email": "…",
41
- "phones": [],
42
- "url": "…",
43
- "action": "update",
44
- "licenses": [],
45
- "addresses": [],
46
- "memberships": [],
47
- "migrationData": {}
48
- }
49
- ]
50
- }
51
- ```
52
-
53
- ## `migrationData` — the part that matters
54
-
55
- Present on **every** record and absent from the live CMS. These are the members' legacy display
56
- preferences, carried over at migration:
57
-
58
- | Key | Answers the ticket type |
59
- | ------------------------------------------------ | --------------------------------------------------------- |
60
- | `opted_out` | _"I asked not to be listed"_ |
61
- | `interests` | _"my services don't appear"_ (becomes `areasOfPractices`) |
62
- | `website`, `logo_url` | _"my website/logo used to show"_ |
63
- | `schedule_code` | _"my booking link is missing"_ |
64
- | `show_phone`, `addressinfo`, `show_member_since` | _"my details display wrong"_ |
65
- | `detailtext` | original profile blurb |
66
-
67
- ## ⚠️ Limits — read before concluding anything
68
-
69
- 1. **It is a single Nov–Dec 2025 snapshot, not a continuous log.** There is no record of any
70
- later sync.
71
- 2. **It does not contain every member.** 13,478 records against ~9,400 on the AHP site alone.
72
- 3. Therefore **absence proves nothing.** "Not in the backup" does **not** mean "PAC never sent
73
- it" — the single most tempting wrong inference here.
74
-
75
- Presence, by contrast, is solid evidence: if a member is in `drop/`, PAC really did send a drop
76
- for them.
77
-
78
- ## Recipe: compare legacy preference against live state
79
-
80
- Index the archive by `memberid`, then batch-query the CMS. This is how the opted-out check below
81
- was done.
82
-
83
- ```js
84
- const fs = require('fs');
85
- const dir = '/Users/Matheusa/Documents/GitHub/abmp-backup-api/backups';
86
- const idx = new Map();
87
- for (const d of fs.readdirSync(dir)) {
88
- const p = `${dir}/${d}`;
89
- if (!fs.statSync(p).isDirectory()) continue;
90
- for (const f of fs.readdirSync(p)) {
91
- if (!f.endsWith('.json')) continue;
92
- for (const r of JSON.parse(fs.readFileSync(`${p}/${f}`, 'utf8')).results || []) {
93
- idx.set(r.memberid, { bucket: d, action: r.action, mig: r.migrationData });
94
- }
95
- }
96
- }
97
- ```
98
-
99
- Then, against the live site — `$hasSome` acts as "in" for a NUMBER field:
100
-
101
- ```json
102
- {
103
- "dataCollectionId": "MembersDataLatest",
104
- "query": {
105
- "filter": { "memberId": { "$hasSome": [804491, 817198, 137584] } },
106
- "fields": ["memberId", "fullName", "optOut", "isVisible", "action"],
107
- "paging": { "limit": 40 }
108
- }
109
- }
110
- ```
111
-
112
- ## Finding on record (2026-08-04)
113
-
114
- 40 members had `migrationData.opted_out === true`. 32 are still active (`new`/`update`); of those
115
- **29 correctly carry `optOut: true` today**, but **3 do not and are publicly visible on ABMP**:
116
-
117
- | memberId | Name |
118
- | -------- | -------------------------- |
119
- | 1091946 | Jessica Solorzano-Peterson |
120
- | 881021 | Arlette Underwood |
121
- | 765272 | Summer Fairbanks |
122
-
123
- **Not confirmed as a bug.** `optOut` is member-editable, so these three may have deliberately
124
- opted back in since migration. That cannot be distinguished remotely — the nightly sync touches
125
- `_updatedDate` on every record, so it is not a usable signal. Raise with PAC rather than assume,
126
- and do not change a member's `optOut` on the strength of this alone.
127
-
128
- ## Other uses
129
-
130
- - **"Was this member ever actually dropped?"** — the `drop/` bucket is ground truth, and is the
131
- unanswerable question behind the _multies not renewing_ ticket.
132
- - **Provenance** — telling a PAC-sourced record from a Wix-created test account. PAC-sourced
133
- records have a real sequential `memberId` (~1.2M–1.8M), a `pageNumber` on the CMS side (written
134
- only while processing a page of the PAC API response), and ISO `membersince`/`expiration`.
135
- Wix-made test accounts use ids like `11111111`, throwaway email domains, and hand-typed date
136
- strings such as `"November 2012"`.
@@ -1,125 +0,0 @@
1
- # `MembersDataLatest` field reference
2
-
3
- Captured from the live **ABMP Members Directory** collection schema
4
- (`GET https://www.wixapis.com/wix-data/v2/collections/MembersDataLatest`) on 2026-08-04,
5
- collection revision `102`. Re-run that call to refresh — the schema is the source of truth,
6
- this file is a convenience copy.
7
-
8
- Permissions: `read/insert/update/remove: ADMIN`, `dataPermissions.itemRead: PRIVILEGED`.
9
- Paging modes: `OFFSET`, `CURSOR`. Supports `COUNT`, `DISTINCT`, `AGGREGATE`.
10
-
11
- ## Identity
12
-
13
- | Field | Type | Notes |
14
- | ------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
- | `_id` | TEXT | Wix item ID (system) |
16
- | `memberId` | **NUMBER** | PAC member ID. Filter with a number, not a string. |
17
- | `url` | TEXT | Profile slug, e.g. `karriknowles` → `/profile/karriknowles`. Uniqueness enforced by `ensureUniqueUrl` in `backend/daily-pull/process-member-methods.js`. |
18
- | `generatedUrl` | BOOLEAN | True when the slug was auto-generated rather than PAC-supplied |
19
- | `wixMemberId` | TEXT | Wix Members app ID |
20
- | `wixContactId` | TEXT | Wix CRM contact ID |
21
- | `contactId` | TEXT | Legacy contact ID field |
22
- | `_owner` | TEXT | System |
23
- | `_createdDate` / `_updatedDate` | DATETIME | `{"$date":"...Z"}` shape |
24
- | `pageNumber` | NUMBER | Which PAC API page last wrote this record — useful for tracing a sync run |
25
-
26
- ## Name and contact
27
-
28
- | Field | Type | Notes |
29
- | ----------------------------------- | -------------- | ----------------------------------------------------------------------------------- |
30
- | `firstName` | TEXT | 🔒 **encrypted** — only `EQ`, `NE`, `HAS_SOME`, `EXISTS` |
31
- | `lastName` | TEXT | 🔒 **encrypted** — same restriction |
32
- | `fullName` | TEXT | Not encrypted → **use this for name searches** (`CONTAINS`, `STARTS_WITH` all work) |
33
- | `businessName` / `showBusinessName` | TEXT / BOOLEAN | |
34
- | `email` | TEXT | Login email |
35
- | `contactFormEmail` | TEXT | Where contact-form mail goes; diverges from `email` by design |
36
- | `phone` | TEXT | 🔒 **encrypted** |
37
- | `toShowPhone` | TEXT | 🔒 **encrypted** — the phone actually rendered |
38
- | `phones` | ARRAY | Full list from PAC |
39
-
40
- ## Membership and licensing
41
-
42
- | Field | Type | Notes |
43
- | --------------- | ------- | -------------------------------------------------------------------------------------------- |
44
- | `action` | TEXT | From the PAC API: `new` / `update` / `drop` / `none`. Drives `isVisible`. |
45
- | `isVisible` | BOOLEAN | `action !== 'drop'`. Controls directory listing. |
46
- | `optOut` | BOOLEAN | Member-chosen suppression, independent of `action` |
47
- | `memberships` | ARRAY | `{association, membertype, expiration, membersince}` |
48
- | `licenses` | ARRAY | `{association, state, license, exempt}` — filtered per-site by `filterLicensesByAssociation` |
49
- | `showLicenseNo` | BOOLEAN | |
50
-
51
- ## Location
52
-
53
- | Field | Type | Notes |
54
- | ---------------------- | ------ | ---------------------------------------------------------------------------------- |
55
- | `addresses` | ARRAY | `{key, line1, line2, city, state, postalcode, latitude, longitude, addressStatus}` |
56
- | `addressDisplayOption` | ARRAY | `[{key, isMain}]` — which address is primary |
57
- | `addressInfo` | OBJECT | Map of address `key` → display mode |
58
- | `locHash` | ARRAY | Geohash (precision 3, see `GEO_HASH_PRECISION`) used for proximity search |
59
-
60
- `addressStatus` values come from `ADDRESS_STATUS_TYPES` in `public/consts.js`:
61
- `full_address`, `state_city_zip`, `dont_show`.
62
-
63
- ## Profile content
64
-
65
- | Field | Type | Notes |
66
- | ------------------ | ------------- | ----------------------------------------------------------------------------------------------------- |
67
- | `areasOfPractices` | ARRAY | **This is the "services" list** members complain about. There is no field literally named `services`. |
68
- | `aboutService` | RICH_TEXT | HTML string |
69
- | `testimonial` | ARRAY | Free-text testimonials |
70
- | `gallery` | MEDIA_GALLERY | |
71
- | `bannerImages` | ARRAY | |
72
- | `profileImage` | IMAGE | `wix:image://` URI |
73
- | `logoImage` | URL | |
74
- | `title` | TEXT | Default CMS field, generally unused |
75
-
76
- ## Display flags — check these first on "X doesn't appear" tickets
77
-
78
- | Field | Type |
79
- | ----------------------------- | ------- | --------------------------------- |
80
- | `showName` | BOOLEAN |
81
- | `showPhone` | BOOLEAN |
82
- | `showWebsite` | BOOLEAN |
83
- | `showWixUrl` | BOOLEAN |
84
- | `showContactForm` | BOOLEAN |
85
- | `showBookingUrl` | BOOLEAN |
86
- | `showBusinessName` | BOOLEAN |
87
- | `showLicenseNo` | BOOLEAN |
88
- | `showAbmp` **and** `showABMP` | BOOLEAN | ⚠️ two separate fields both exist |
89
-
90
- ## Links
91
-
92
- | Field | Type | Notes |
93
- | --------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
94
- | `website` | URL | Member's own site |
95
- | `bookingUrl` | URL | Member-entered booking link; rendered only when `showBookingUrl` is `true` |
96
- | `apiBookingUrl` **and** `APIBookingUrl` | TEXT | ⚠️ two separate fields. Can contain a raw HTML embed blob (e.g. a Genbook `<script>` badge), not just a URL — don't assume it parses as a URL. |
97
-
98
- ## Known traps
99
-
100
- 1. **Encrypted fields reject substring operators.** `CONTAINS` on `firstName` errors out. Use
101
- `fullName`.
102
- 2. **Duplicate-case field pairs** (`showAbmp`/`showABMP`, `apiBookingUrl`/`APIBookingUrl`) are
103
- real and both queryable. A value "missing" from one may be present in the other.
104
- 3. **`memberId` is a NUMBER.** `{"memberId": "731898"}` matches nothing and returns an empty
105
- list rather than an error — the most common false "member not found".
106
- 4. **Per-site collections.** Absence on one site is not absence everywhere.
107
- 5. **Eventually consistent.** A write may not be visible to the next immediate query.
108
- 6. **Two different date formats.** Only true `DATETIME` fields (`_createdDate`,
109
- `_updatedDate`) use the `{"$date":"...Z"}` form. Dates _inside_ the `memberships` and
110
- `licenses` arrays — notably `memberships.expiration` and `memberships.membersince` — are
111
- **plain ISO strings without a `Z` or milliseconds** (`"2027-06-12T00:00:00"`) and must be
112
- filtered as strings. Using `$date` against them silently returns zero rows rather than
113
- erroring, which reads as "no affected members" when there may be thousands.
114
- 7. **`returnTotalCount` yields no `total`** on this collection — use
115
- `POST /wix-data/v2/items/count` instead.
116
- 8. **`{"$ne": ""}` also matches rows where the field is absent.** Verified 2026-08-04: filtering
117
- `{"website": {"$ne": ""}}` returned 300 rows whose projected `website` and `bookingUrl` came
118
- back empty — the field simply wasn't set on them. So `$ne ""` is _not_ "has a value", and any
119
- count built on it is inflated. To mean "actually has a value", pair it with
120
- `{"$exists": true}` or filter/verify client-side after projecting the field.
121
-
122
- ## No regex filtering
123
-
124
- Wix Data has no regex operator, so you cannot ask the API questions like "domains containing a
125
- digit". Project the field, page through, and evaluate in JS.