abmp-npm 1.1.135 → 1.1.137

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.
@@ -0,0 +1,194 @@
1
+ const { incrementUrlCounter, extractBaseUrl } = require('../daily-pull/utils');
2
+ const {
3
+ normalizeUrlForComparison,
4
+ sortByUrlCounterDescending,
5
+ extractUrlCounter,
6
+ } = require('../utils');
7
+
8
+ // ─── Helpers ─────────────────────────────────────────────────────────
9
+
10
+ /**
11
+ * Simulates getMemberBySlug's normalized-comparison branch using
12
+ * the ACTUAL production sort comparator imported from utils.js.
13
+ */
14
+ function simulateGetHighestMember(allMembers, slug) {
15
+ const matching = allMembers.filter(
16
+ m => m.url && normalizeUrlForComparison(m.url) === slug.toLowerCase()
17
+ );
18
+ matching.sort(sortByUrlCounterDescending);
19
+ return matching[0] || null;
20
+ }
21
+
22
+ /**
23
+ * Simulates ensureUniqueUrl's counter-increment logic given the
24
+ * "highest" member returned by getMemberBySlug.
25
+ */
26
+ function simulateEnsureUniqueUrl(baseSlug, highestMember) {
27
+ if (!highestMember || !highestMember.url) return baseSlug;
28
+ return incrementUrlCounter(highestMember?.url, baseSlug);
29
+ }
30
+
31
+ // ─── Test data ───────────────────────────────────────────────────────
32
+
33
+ function buildMembersInDb() {
34
+ return [
35
+ { memberId: 1, url: 'firstNameLastName' },
36
+ { memberId: 2, url: 'firstNameLastName-1' },
37
+ { memberId: 3, url: 'firstNameLastName-2' },
38
+ { memberId: 4, url: 'firstNameLastName-3' },
39
+ { memberId: 5, url: 'firstNameLastName-4' },
40
+ { memberId: 6, url: 'firstNameLastName-5' },
41
+ { memberId: 7, url: 'firstNameLastName-6' },
42
+ { memberId: 8, url: 'firstNameLastName-7' },
43
+ { memberId: 9, url: 'firstNameLastName-8' },
44
+ { memberId: 10, url: 'firstNameLastName-9' },
45
+ { memberId: 11, url: 'firstNameLastName-10' },
46
+ { memberId: 12, url: 'firstNameLastName-11' },
47
+ { memberId: 13, url: 'firstNameLastName-12' },
48
+ ];
49
+ }
50
+
51
+ function buildLargeCounterMembers() {
52
+ const members = [];
53
+ for (let i = 0; i <= 105; i++) {
54
+ members.push({ memberId: i, url: i === 0 ? 'testUser' : `testUser-${i}` });
55
+ }
56
+ return members;
57
+ }
58
+
59
+ // ─── Tests ───────────────────────────────────────────────────────────
60
+
61
+ describe('Production sort: getMemberBySlug must return the HIGHEST counter', () => {
62
+ test('should return -12 as the highest URL (not -9)', () => {
63
+ const members = buildMembersInDb();
64
+ const highest = simulateGetHighestMember(members, 'firstnamelastname');
65
+
66
+ expect(highest.url).toBe('firstNameLastName-12');
67
+ });
68
+
69
+ test('should sort -12 above -9 in descending order', () => {
70
+ const members = buildMembersInDb();
71
+ const sorted = [...members]
72
+ .filter(m => normalizeUrlForComparison(m.url) === 'firstnamelastname')
73
+ .sort(sortByUrlCounterDescending);
74
+ const sortedUrls = sorted.map(m => m.url);
75
+
76
+ const indexOf9 = sortedUrls.indexOf('firstNameLastName-9');
77
+ const indexOf12 = sortedUrls.indexOf('firstNameLastName-12');
78
+
79
+ expect(indexOf12).toBeLessThan(indexOf9);
80
+ });
81
+
82
+ test('should handle large counters (100+) correctly', () => {
83
+ const members = buildLargeCounterMembers();
84
+ const highest = simulateGetHighestMember(members, 'testuser');
85
+
86
+ expect(highest.url).toBe('testUser-105');
87
+ });
88
+
89
+ test('should handle hyphenated names (mary-jane) correctly', () => {
90
+ const members = [
91
+ { memberId: 1, url: 'mary-jane' },
92
+ { memberId: 2, url: 'mary-jane-1' },
93
+ { memberId: 3, url: 'mary-jane-2' },
94
+ { memberId: 4, url: 'mary-jane-10' },
95
+ ];
96
+
97
+ const highest = simulateGetHighestMember(members, 'mary-jane');
98
+ expect(highest.url).toBe('mary-jane-10');
99
+ });
100
+ });
101
+
102
+ describe('Production sort: ensureUniqueUrl must generate a truly unique URL', () => {
103
+ test('should generate -13 (not -10) when DB has URLs up to -12', () => {
104
+ const members = buildMembersInDb();
105
+ const highest = simulateGetHighestMember(members, 'firstnamelastname');
106
+ const newUrl = simulateEnsureUniqueUrl('firstNameLastName', highest);
107
+
108
+ expect(newUrl).toBe('firstNameLastName-13');
109
+
110
+ const existingUrls = members.map(m => m.url);
111
+ expect(existingUrls).not.toContain(newUrl);
112
+ });
113
+
114
+ test('repeated daily pulls should produce sequential unique URLs', () => {
115
+ const members = buildMembersInDb();
116
+ const generatedUrls = [];
117
+
118
+ for (let day = 0; day < 5; day++) {
119
+ const highest = simulateGetHighestMember(members, 'firstnamelastname');
120
+ const newUrl = simulateEnsureUniqueUrl('firstNameLastName', highest);
121
+ generatedUrls.push(newUrl);
122
+ members.push({ memberId: 100 + day, url: newUrl });
123
+ }
124
+
125
+ expect(new Set(generatedUrls).size).toBe(5);
126
+ expect(generatedUrls).toEqual([
127
+ 'firstNameLastName-13',
128
+ 'firstNameLastName-14',
129
+ 'firstNameLastName-15',
130
+ 'firstNameLastName-16',
131
+ 'firstNameLastName-17',
132
+ ]);
133
+ });
134
+
135
+ test('should NOT produce duplicates (the -10 bug)', () => {
136
+ const members = buildMembersInDb();
137
+ const highest = simulateGetHighestMember(members, 'firstnamelastname');
138
+ const newUrl = simulateEnsureUniqueUrl('firstNameLastName', highest);
139
+
140
+ expect(newUrl).not.toBe('firstNameLastName-10');
141
+ });
142
+ });
143
+
144
+ describe('Production sort: ensureUniqueUrlsInBatch single-member path', () => {
145
+ test('incrementUrlCounter should produce a unique URL from highest DB member', () => {
146
+ const members = buildMembersInDb();
147
+ const dbMember = simulateGetHighestMember(members, 'firstnamelastname');
148
+ const result = incrementUrlCounter(dbMember.url, 'firstNameLastName');
149
+
150
+ expect(result).toBe('firstNameLastName-13');
151
+
152
+ const existingUrls = members.map(m => m.url);
153
+ expect(existingUrls).not.toContain(result);
154
+ });
155
+ });
156
+
157
+ describe('Utility functions', () => {
158
+ test('normalizeUrlForComparison strips trailing counter', () => {
159
+ expect(normalizeUrlForComparison('firstNameLastName')).toBe('firstnamelastname');
160
+ expect(normalizeUrlForComparison('firstNameLastName-1')).toBe('firstnamelastname');
161
+ expect(normalizeUrlForComparison('firstNameLastName-9')).toBe('firstnamelastname');
162
+ expect(normalizeUrlForComparison('firstNameLastName-10')).toBe('firstnamelastname');
163
+ expect(normalizeUrlForComparison('firstNameLastName-100')).toBe('firstnamelastname');
164
+ });
165
+
166
+ test('extractUrlCounter returns numeric counter', () => {
167
+ expect(extractUrlCounter('firstNameLastName')).toBe(-1);
168
+ expect(extractUrlCounter('firstNameLastName-1')).toBe(1);
169
+ expect(extractUrlCounter('firstNameLastName-9')).toBe(9);
170
+ expect(extractUrlCounter('firstNameLastName-10')).toBe(10);
171
+ expect(extractUrlCounter('firstNameLastName-100')).toBe(100);
172
+ expect(extractUrlCounter(null)).toBe(-1);
173
+ expect(extractUrlCounter('')).toBe(-1);
174
+ });
175
+
176
+ test('extractBaseUrl strips numeric counter', () => {
177
+ expect(extractBaseUrl('firstNameLastName-10')).toBe('firstNameLastName');
178
+ expect(extractBaseUrl('firstNameLastName-1')).toBe('firstNameLastName');
179
+ expect(extractBaseUrl('firstNameLastName')).toBe('firstNameLastName');
180
+ expect(extractBaseUrl('john-doe-3')).toBe('john-doe');
181
+ });
182
+
183
+ test('incrementUrlCounter increments correctly', () => {
184
+ expect(incrementUrlCounter('firstNameLastName-9', 'firstNameLastName')).toBe(
185
+ 'firstNameLastName-10'
186
+ );
187
+ expect(incrementUrlCounter('firstNameLastName-10', 'firstNameLastName')).toBe(
188
+ 'firstNameLastName-11'
189
+ );
190
+ expect(incrementUrlCounter('firstNameLastName', 'firstNameLastName')).toBe(
191
+ 'firstNameLastName-1'
192
+ );
193
+ });
194
+ });
@@ -1,12 +1,8 @@
1
1
  const { bulkSaveMembers, getMemberBySlug } = require('../members-data-methods');
2
+ const { extractUrlCounter } = require('../utils');
2
3
 
3
4
  const { generateUpdatedMemberData } = require('./process-member-methods');
4
- const {
5
- changeWixMembersEmails,
6
- extractUrlCounter,
7
- incrementUrlCounter,
8
- extractBaseUrl,
9
- } = require('./utils');
5
+ const { changeWixMembersEmails, incrementUrlCounter, extractBaseUrl } = require('./utils');
10
6
 
11
7
  /**
12
8
  * Ensures unique URLs within a batch of members by deduplicating URLs
@@ -20,7 +16,8 @@ async function ensureUniqueUrlsInBatch(memberDataList) {
20
16
  return memberDataList;
21
17
  }
22
18
 
23
- // Group members by their normalized base URL
19
+ // Group members by their normalized base URL (case-insensitive to avoid
20
+ // "John-12" and "john-12" being treated as different when slugs are matched case-insensitively)
24
21
  const urlGroups = new Map();
25
22
 
26
23
  memberDataList.forEach(member => {
@@ -28,16 +25,17 @@ async function ensureUniqueUrlsInBatch(memberDataList) {
28
25
  return;
29
26
  }
30
27
 
31
- // Extract the base URL (without any counter) for grouping
32
28
  const baseUrl = extractBaseUrl(member.url);
33
- if (!urlGroups.has(baseUrl)) {
34
- urlGroups.set(baseUrl, []);
29
+ const groupKey = baseUrl.toLowerCase();
30
+ if (!urlGroups.has(groupKey)) {
31
+ urlGroups.set(groupKey, []);
35
32
  }
36
- urlGroups.get(baseUrl).push(member);
33
+ urlGroups.get(groupKey).push(member);
37
34
  });
38
35
 
39
36
  // For each group, check database and assign unique URLs sequentially
40
- for (const [baseUrl, members] of urlGroups.entries()) {
37
+ for (const [groupKey, members] of urlGroups.entries()) {
38
+ const baseUrl = groupKey; // lowercase for consistent slug assignment
41
39
  if (members.length <= 1) {
42
40
  // Single member - still check DB to ensure it doesn't conflict with other pages
43
41
  const member = members[0];
@@ -80,14 +78,9 @@ async function ensureUniqueUrlsInBatch(memberDataList) {
80
78
  let batchMaxCounter = -1;
81
79
  members.forEach(member => {
82
80
  const originalUrl = member.url;
83
- const urlParts = originalUrl.split('-');
84
- const lastSegment = urlParts[urlParts.length - 1];
85
- const isNumeric = /^\d+$/.test(lastSegment);
86
- if (isNumeric) {
87
- const counter = parseInt(lastSegment, 10);
88
- if (counter > batchMaxCounter) {
89
- batchMaxCounter = counter;
90
- }
81
+ const urlCounter = extractUrlCounter(originalUrl);
82
+ if (urlCounter > batchMaxCounter) {
83
+ batchMaxCounter = urlCounter;
91
84
  }
92
85
  });
93
86
 
@@ -1,7 +1,9 @@
1
+ const { ADDRESS_STATUS_TYPES } = require('../../public/consts');
1
2
  const { findMemberById, getMemberBySlug } = require('../members-data-methods');
2
3
  const { isValidArray, generateGeoHash } = require('../utils');
3
4
 
4
5
  const { MEMBER_ACTIONS, DEFAULT_MEMBER_DISPLAY_SETTINGS } = require('./consts');
6
+ const { incrementUrlCounter } = require('./utils');
5
7
  const { validateCoreMemberData, containsNonEnglish, createFullName } = require('./utils');
6
8
 
7
9
  /**
@@ -43,9 +45,7 @@ const ensureUniqueUrl = async ({ url, memberId, fullName }) => {
43
45
  console.log(
44
46
  `Found member with same url ${existingMember.url} for memberId ${memberId} and URL ${uniqueUrl}, increasing counter by 1`
45
47
  );
46
- const lastSegment = existingMember.url.split('-').pop() || '0';
47
- const lastCounter = parseInt(lastSegment, 10) || 0;
48
- uniqueUrl = `${uniqueUrl}-${lastCounter + 1}`;
48
+ uniqueUrl = incrementUrlCounter(existingMember.url, uniqueUrl);
49
49
  }
50
50
  if (uniqueUrl !== baseUrl) {
51
51
  console.log(`URL conflict resolved: ${baseUrl} -> ${uniqueUrl} for member ${memberId}`);
@@ -82,7 +82,21 @@ async function generateUpdatedMemberData({ inputMemberData, currentPageNumber })
82
82
 
83
83
  // Only add address data for new members
84
84
  if (!existingDbMember && isValidArray(inputMemberData.addresses)) {
85
- updatedMemberData.addresses = inputMemberData.addresses;
85
+ const normalizedAddresses = inputMemberData.addresses.map((address, index) => {
86
+ const key = address?.key || `address_${index}`;
87
+ const rawStatus = address?.addressStatus;
88
+ const resolvedStatus =
89
+ index === 0 && rawStatus === ADDRESS_STATUS_TYPES.DONT_SHOW
90
+ ? ADDRESS_STATUS_TYPES.STATE_CITY_ZIP
91
+ : rawStatus || ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
92
+ return {
93
+ ...address,
94
+ key,
95
+ addressStatus: resolvedStatus,
96
+ };
97
+ });
98
+ updatedMemberData.addresses = normalizedAddresses;
99
+ updatedMemberData.addressDisplayOption = [{ key: normalizedAddresses[0].key, isMain: true }];
86
100
  }
87
101
 
88
102
  return { ...updatedMemberData, isNewToDb: !existingDbMember };
@@ -1,4 +1,5 @@
1
1
  const { updateWixMemberLoginEmail } = require('../members-area-methods');
2
+ const { extractUrlCounter } = require('../utils');
2
3
 
3
4
  const { MEMBER_ACTIONS } = require('./consts');
4
5
 
@@ -15,21 +16,12 @@ const changeWixMembersEmails = async toChangeWixMembersEmails => {
15
16
  );
16
17
  };
17
18
 
18
- const extractUrlCounter = url => {
19
- if (!url) return -1;
20
- const lastSegment = url.split('-').pop() || '0';
21
- const isNumeric = /^\d+$/.test(lastSegment);
22
- return isNumeric ? parseInt(lastSegment, 10) : -1;
23
- };
24
-
25
19
  const extractBaseUrl = url => {
26
20
  if (!url) return url;
27
- const urlParts = url.split('-');
28
- const lastSegment = urlParts[urlParts.length - 1];
29
- const isNumeric = /^\d+$/.test(lastSegment);
30
- if (isNumeric && urlParts.length > 1) {
21
+ const lastCounter = extractUrlCounter(url);
22
+ if (lastCounter > 0) {
31
23
  // Remove the numeric counter to get the base URL
32
- return urlParts.slice(0, -1).join('-');
24
+ return url.split('-').slice(0, -1).join('-');
33
25
  }
34
26
  // No counter found, return the URL as-is
35
27
  return url;
@@ -49,9 +41,7 @@ const incrementUrlCounter = (existingUrl, baseUrl) => {
49
41
  console.log(
50
42
  `Found member with same url ${existingUrl} for baseUrl ${baseUrl}, increasing counter by 1`
51
43
  );
52
- const lastSegment = existingUrl.split('-').pop() || '0';
53
- const isNumeric = /^\d+$/.test(lastSegment);
54
- const lastCounter = isNumeric ? parseInt(lastSegment, 10) : 0;
44
+ const lastCounter = Math.max(0, extractUrlCounter(existingUrl));
55
45
  return `${baseUrl}-${lastCounter + 1}`;
56
46
  }
57
47
 
@@ -118,7 +108,6 @@ module.exports = {
118
108
  validateCoreMemberData,
119
109
  containsNonEnglish,
120
110
  createFullName,
121
- extractUrlCounter,
122
111
  incrementUrlCounter,
123
112
  extractBaseUrl,
124
113
  };
@@ -1,6 +1,7 @@
1
1
  const { COLLECTIONS } = require('../public/consts');
2
2
 
3
3
  const { ensureUniqueUrlsInBatch } = require('./daily-pull/bulk-process-methods');
4
+ const { ensureUniqueUrl } = require('./daily-pull/process-member-methods');
4
5
  const { wixData } = require('./elevated-modules');
5
6
  const { bulkSaveMembers } = require('./members-data-methods');
6
7
  const { queryAllItems } = require('./utils');
@@ -27,4 +28,31 @@ async function copyContactIdToWixMemberId() {
27
28
  return await bulkSaveMembers(updatedMembers, COLLECTIONS.MEMBERS_DATA);
28
29
  }
29
30
 
30
- module.exports = { deduplicateURls, copyContactIdToWixMemberId };
31
+ async function createMissingUrls() {
32
+ const query = wixData.query(COLLECTIONS.MEMBERS_DATA).isEmpty('url');
33
+ const members = await queryAllItems(query);
34
+ console.log(
35
+ 'membersWithoutUrls info',
36
+ JSON.stringify({
37
+ count: members.length,
38
+ membersIds: members.map(m => m.memberId),
39
+ })
40
+ );
41
+
42
+ const membersWithGeneratedUrlsPromises = members.map(async member => ({
43
+ ...member,
44
+ url: await ensureUniqueUrl({
45
+ url: member.url,
46
+ memberId: member.memberId,
47
+ fullName: member.fullName,
48
+ }),
49
+ }));
50
+ const membersWithGeneratedUrls = await Promise.all(membersWithGeneratedUrlsPromises);
51
+ //recheck urls in same batch to avoid duplicates
52
+ const uniqueUrlsUpdatedMembers = await ensureUniqueUrlsInBatch(membersWithGeneratedUrls);
53
+ const urls = uniqueUrlsUpdatedMembers.map(m => m.url).filter(Boolean);
54
+ console.log('unique urls', [...new Set(urls)]);
55
+ return await bulkSaveMembers(uniqueUrlsUpdatedMembers, COLLECTIONS.MEMBERS_DATA);
56
+ }
57
+
58
+ module.exports = { deduplicateURls, copyContactIdToWixMemberId, createMissingUrls };
package/backend/jobs.js CHANGED
@@ -68,6 +68,20 @@ async function scheduleCreateContactsFromMembersTask() {
68
68
  }
69
69
  }
70
70
 
71
+ async function scheduleFixPrimaryAddressForMembersTask() {
72
+ try {
73
+ console.log('scheduleFixPrimaryAddressForMembers started!');
74
+ return await taskManager().schedule({
75
+ name: TASKS_NAMES.scheduleFixPrimaryAddressForMembers,
76
+ data: {},
77
+ type: 'scheduled',
78
+ });
79
+ } catch (error) {
80
+ console.error(`Failed to scheduleFixPrimaryAddressForMembers: ${error.message}`);
81
+ throw new Error(`Failed to scheduleFixPrimaryAddressForMembers: ${error.message}`);
82
+ }
83
+ }
84
+
71
85
  async function updateSiteMapS3() {
72
86
  try {
73
87
  return await taskManager().schedule({
@@ -85,4 +99,5 @@ module.exports = {
85
99
  scheduleDailyPullTask,
86
100
  updateSiteMapS3,
87
101
  scheduleCreateContactsFromMembersTask,
102
+ scheduleFixPrimaryAddressForMembersTask,
88
103
  };
@@ -10,6 +10,7 @@ const { createSiteMember, getCurrentMember } = require('./members-area-methods')
10
10
  const {
11
11
  chunkArray,
12
12
  normalizeUrlForComparison,
13
+ sortByUrlCounterDescending,
13
14
  queryAllItems,
14
15
  generateGeoHash,
15
16
  searchAllItems,
@@ -176,7 +177,7 @@ async function getMemberBySlug({
176
177
  //remove trailing "-1", "-2", etc.
177
178
  item => item.url && normalizeUrlForComparison(item.url) === slug.toLowerCase()
178
179
  )
179
- .sort((a, b) => b.url.toLowerCase().localeCompare(a.url.toLowerCase()));
180
+ .sort(sortByUrlCounterDescending);
180
181
  }
181
182
  if (matchingMembers.length > 1) {
182
183
  const queryResultMsg = `Multiple members found with same slug ${slug} membersIds are : [${matchingMembers
@@ -410,7 +411,8 @@ async function getMembersWithWixUrl() {
410
411
  .eq('showWixUrl', true)
411
412
  .ne('action', MEMBER_ACTIONS.DROP)
412
413
  .ne('memberships.membertype', MEMBERSHIPS_TYPES.PAC_STAFF)
413
- .isNotEmpty('url')
414
+ .ne('memberships.membertype', MEMBERSHIPS_TYPES.STUDENT)
415
+ //.isNotEmpty('url') - not used because it's not working as expected
414
416
  .limit(1000);
415
417
  let currentResults = await membersQuery.find();
416
418
  let i = 0;
@@ -422,7 +424,9 @@ async function getMembersWithWixUrl() {
422
424
  i++;
423
425
  }
424
426
  console.log('i is ', i);
425
- const filtered = allItems.filter(item => typeof item.url === 'string' && !item.url.includes('/'));
427
+ const filtered = allItems.filter(
428
+ item => typeof item.url === 'string' && !item.url.includes('/') && item.url !== ''
429
+ );
426
430
  console.log('filtered is ', filtered.length);
427
431
  return filtered;
428
432
  }
@@ -47,10 +47,12 @@ const createRoutersHandlers = wixRouterMethods => {
47
47
  }
48
48
  const profileUrl = `${request.baseUrl}/${PAGES_PATHS.PROFILE}/${profileData.url}`;
49
49
  const isPrivateMember = profileData.isPrivateMember;
50
+ const isStudent = profileData.shouldHaveStudentBadge;
51
+ const shouldNoIndex = isPrivateMember || isStudent;
50
52
  const seoData = {
51
53
  title: seoTitle,
52
54
  description: description,
53
- noIndex: isPrivateMember,
55
+ noIndex: shouldNoIndex,
54
56
  metaTags: [
55
57
  {
56
58
  name: 'description',
@@ -69,7 +71,7 @@ const createRoutersHandlers = wixRouterMethods => {
69
71
  },
70
72
  {
71
73
  name: 'robots',
72
- content: isPrivateMember ? 'noindex, nofollow' : 'index, follow',
74
+ content: shouldNoIndex ? 'noindex, nofollow' : 'index, follow',
73
75
  },
74
76
  // Open Graph tags
75
77
  {
@@ -66,6 +66,11 @@ function transformMemberToProfileData(member, siteAssociation) {
66
66
  numeric: true,
67
67
  })
68
68
  ) || [];
69
+
70
+ const phones = Array.isArray(member.phones) ? member.phones : [];
71
+ const toShowPhone = member.toShowPhone || '';
72
+ const phone = toShowPhone && phones.includes(toShowPhone) ? toShowPhone : '';
73
+
69
74
  return {
70
75
  mainAddress,
71
76
  testimonials: member.testimonial || [],
@@ -84,7 +89,7 @@ function transformMemberToProfileData(member, siteAssociation) {
84
89
  bookingUrl: member.bookingUrl,
85
90
  aboutService: member.aboutService,
86
91
  businessName: (member.showBusinessName && member.businessName) || '',
87
- phone: member.toShowPhone || '',
92
+ phone,
88
93
  areasOfPractices,
89
94
  gallery: member.gallery,
90
95
  bannerImages: member.bannerImages,