abmp-npm 1.8.37 → 1.8.39

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.
@@ -1,182 +1,16 @@
1
- const { DEFAULT_SEO_DESCRIPTION, ADDRESS_STATUS_TYPES } = require('../public/consts');
1
+ const { DEFAULT_SEO_DESCRIPTION, ABMP_LOGO_URL } = require('../public');
2
2
 
3
3
  const { getMemberBySlug } = require('./members-data-methods');
4
- const { formatDateToMonthYear } = require('./utils');
5
-
6
- /**
7
- * Generates SEO title for member profile
8
- * @param {string} fullName - Member's full name
9
- * @param {Array<string>} areasOfPractices - Member's areas of practice
10
- * @returns {string} SEO title
11
- */
12
- function generateSEOTitle(fullName, areasOfPractices) {
13
- return `${fullName}${
14
- areasOfPractices && areasOfPractices.length > 0
15
- ? ` | ${areasOfPractices.slice(0, 3).join(', ')}`
16
- : ''
17
- } | ABMP Member`;
18
- }
19
-
20
- /**
21
- * Strips HTML tags and decodes HTML entities from a string
22
- * @param {string} html - HTML string to clean
23
- * @returns {string} Cleaned text
24
- */
25
- function stripHtmlTags(html) {
26
- if (!html) return '';
27
- // Remove HTML tags and decode HTML entities
28
- return html
29
- .replace(/<[^>]*>/g, '') // Remove HTML tags
30
- .replace(/&nbsp;/g, ' ') // Replace non-breaking spaces
31
- .replace(/&amp;/g, '&') // Replace encoded ampersands
32
- .replace(/&lt;/g, '<') // Replace encoded less than
33
- .replace(/&gt;/g, '>') // Replace encoded greater than
34
- .replace(/&quot;/g, '"') // Replace encoded quotes
35
- .replace(/&#39;/g, "'") // Replace encoded apostrophes
36
- .replace(/\s+/g, ' ') // Replace multiple whitespace with single space
37
- .trim(); // Remove leading/trailing whitespace
38
- }
39
-
40
- /**
41
- * Check if member has student membership
42
- * @param {Object} member - Member object
43
- * @param {boolean} checkAssociation - Whether to check for specific association
44
- * @param {string} siteAssociation - Site association to check for
45
- * @param {string} studentType - Student membership type
46
- * @returns {boolean} True if member has student membership
47
- */
48
- function hasStudentMembership(member, checkAssociation, siteAssociation, studentType) {
49
- const memberships = member?.memberships;
50
- if (!Array.isArray(memberships)) return false;
51
-
52
- return memberships.some(membership => {
53
- const isStudent = membership.membertype === studentType;
54
- const hasCorrectAssociation = !checkAssociation || membership.association === siteAssociation;
55
- return isStudent && hasCorrectAssociation;
56
- });
57
- }
58
-
59
- /**
60
- * Check if member should have student badge
61
- * @param {Object} member - Member object
62
- * @param {string} siteAssociation - Site association
63
- * @param {string} studentType - Student membership type
64
- * @returns {boolean} True if should have badge
65
- */
66
- function shouldHaveStudentBadge(member, siteAssociation, studentType) {
67
- return hasStudentMembership(member, true, siteAssociation, studentType);
68
- }
69
-
70
- /**
71
- * Get addresses by status, excluding main address
72
- * @param {Array} addresses - All addresses
73
- * @param {Array} addressDisplayOption - Display options
74
- * @returns {Array} Processed addresses
75
- */
76
- function getAddressesByStatus(
77
- addresses = [],
78
- addressDisplayOption = [],
79
- formatAddress,
80
- getMainAddress,
81
- generateId
82
- ) {
83
- const visible = addresses.filter(addr => addr.addressStatus !== ADDRESS_STATUS_TYPES.DONT_SHOW);
84
- if (visible.length < 2) {
85
- return [];
86
- }
87
- const opts = Array.isArray(addressDisplayOption) ? addressDisplayOption : [];
88
- const mainOpt = opts.find(o => o.isMain);
89
- const mainKey = mainOpt ? mainOpt.key : visible[0].key;
90
- return visible
91
- .filter(addr => addr?.key !== mainKey)
92
- .map(addr => {
93
- const addressString = formatAddress(addr);
94
- return addressString ? { _id: generateId(), address: addressString } : null;
95
- })
96
- .filter(Boolean);
97
- }
98
-
99
- /**
100
- * Get member profile data formatted for display
101
- * @param {Object} member - Member object
102
- * @param {Object} utils - Utility functions (formatAddress, getMainAddress, generateId)
103
- * @param {Object} constants - Constants (siteAssociation, studentType, pacStaffType)
104
- * @returns {Object} Formatted profile data
105
- */
106
- function getMemberProfileData(member, utils, constants) {
107
- if (!member) {
108
- throw new Error('member is required');
109
- }
110
- const { formatAddress, getMainAddress, generateId } = utils;
111
- const { siteAssociation, studentType, pacStaffType } = constants;
112
-
113
- const addresses = member.addresses || [];
114
- const licenceNo = member.licenses
115
- ?.map(val => val.license)
116
- .filter(Boolean)
117
- .join(', ');
118
- const processedAddresses = getAddressesByStatus(
119
- member.addresses,
120
- member.addressDisplayOption,
121
- formatAddress,
122
- getMainAddress,
123
- generateId
124
- );
125
-
126
- const memberships = member.memberships || [];
127
- const abmp = memberships.find(m => m.association === siteAssociation);
128
-
129
- const areasOfPractices =
130
- member.areasOfPractices
131
- ?.filter(item => typeof item === 'string' && item.trim().length > 0)
132
- .map(item => item.trim())
133
- .sort((a, b) =>
134
- a.localeCompare(b, undefined, {
135
- sensitivity: 'base',
136
- numeric: true,
137
- })
138
- ) || [];
139
-
140
- const mainAddress = getMainAddress(member.addressDisplayOption, addresses);
141
-
142
- return {
143
- mainAddress: mainAddress,
144
- testimonials: member.testimonial || [],
145
- licenceNo,
146
- processedAddresses,
147
- memberSince: (member.showABMP && abmp && formatDateToMonthYear(abmp?.membersince)) || '',
148
- shouldHaveStudentBadge: shouldHaveStudentBadge(member, siteAssociation, studentType),
149
- logoImage: member.logoImage,
150
- fullName: member.fullName,
151
- profileImage: member.profileImage,
152
- showContactForm: member.showContactForm,
153
- bookingUrl: member.bookingUrl,
154
- aboutService: member.aboutService,
155
- businessName: (member.showBusinessName && member.businessName) || '',
156
- phone: member.toShowPhone || '',
157
- areasOfPractices,
158
- gallery: member.gallery,
159
- bannerImages: member.bannerImages,
160
- showWixUrl: member.showWixUrl,
161
- _id: member._id,
162
- url: member.url,
163
- city: mainAddress?.city || '',
164
- state: mainAddress?.state || '',
165
- isPrivateMember: member.memberships.some(membership => membership.membertype === pacStaffType),
166
- };
167
- }
4
+ const { generateSEOTitle, stripHtmlTags, getMemberProfileData } = require('./routers-utils');
168
5
 
169
6
  /**
170
7
  * Profile router handler
171
8
  * @param {Object} request - Router request object
172
9
  * @param {Object} dependencies - Dependencies (ok, notFound, redirect, sendStatus)
173
- * @param {Object} utils - Utility functions
174
- * @param {Object} constants - Constants
175
10
  * @returns {Promise} Router response
176
11
  */
177
- async function profileRouter(request, dependencies, utils, constants) {
12
+ async function profileRouter(request, dependencies) {
178
13
  const { ok, notFound, redirect, sendStatus } = dependencies;
179
- const { abmpLogoUrl } = constants;
180
14
 
181
15
  const slug = request.path[0];
182
16
  if (!slug) {
@@ -193,10 +27,10 @@ async function profileRouter(request, dependencies, utils, constants) {
193
27
  return notFound();
194
28
  }
195
29
 
196
- const profileData = getMemberProfileData(member, utils, constants);
30
+ const profileData = getMemberProfileData(member);
197
31
 
198
32
  if (profileData && profileData.showWixUrl) {
199
- const ogImage = profileData.profileImage || profileData.logoImage || abmpLogoUrl;
33
+ const ogImage = profileData.profileImage || profileData.logoImage || ABMP_LOGO_URL;
200
34
  const seoTitle = generateSEOTitle(profileData.fullName, profileData.areasOfPractices);
201
35
  // Use stripped HTML from aboutService rich text content
202
36
  let description = stripHtmlTags(profileData.aboutService) || DEFAULT_SEO_DESCRIPTION;
@@ -345,6 +179,7 @@ function profileSiteMap(_sitemapRequest, _dependencies, _fetchAllItemsInParallel
345
179
  module.exports = {
346
180
  profileRouter,
347
181
  profileSiteMap,
182
+ // Re-export utilities for backward compatibility
348
183
  getMemberProfileData,
349
184
  generateSEOTitle,
350
185
  stripHtmlTags,
@@ -0,0 +1,164 @@
1
+ const {
2
+ ADDRESS_STATUS_TYPES,
3
+ SITE_ASSOCIATION,
4
+ MEMBERSHIPS_TYPES,
5
+ formatAddress,
6
+ getMainAddress,
7
+ generateId,
8
+ } = require('../public');
9
+
10
+ const { formatDateToMonthYear } = require('./utils');
11
+
12
+ /**
13
+ * Generates SEO title for member profile
14
+ * @param {string} fullName - Member's full name
15
+ * @param {Array<string>} areasOfPractices - Member's areas of practice
16
+ * @returns {string} SEO title
17
+ */
18
+ function generateSEOTitle(fullName, areasOfPractices) {
19
+ return `${fullName}${
20
+ areasOfPractices && areasOfPractices.length > 0
21
+ ? ` | ${areasOfPractices.slice(0, 3).join(', ')}`
22
+ : ''
23
+ } | ABMP Member`;
24
+ }
25
+
26
+ /**
27
+ * Strips HTML tags and decodes HTML entities from a string
28
+ * @param {string} html - HTML string to clean
29
+ * @returns {string} Cleaned text
30
+ */
31
+ function stripHtmlTags(html) {
32
+ if (!html) return '';
33
+ // Remove HTML tags and decode HTML entities
34
+ return html
35
+ .replace(/<[^>]*>/g, '') // Remove HTML tags
36
+ .replace(/&nbsp;/g, ' ') // Replace non-breaking spaces
37
+ .replace(/&amp;/g, '&') // Replace encoded ampersands
38
+ .replace(/&lt;/g, '<') // Replace encoded less than
39
+ .replace(/&gt;/g, '>') // Replace encoded greater than
40
+ .replace(/&quot;/g, '"') // Replace encoded quotes
41
+ .replace(/&#39;/g, "'") // Replace encoded apostrophes
42
+ .replace(/\s+/g, ' ') // Replace multiple whitespace with single space
43
+ .trim(); // Remove leading/trailing whitespace
44
+ }
45
+
46
+ /**
47
+ * Check if member has student membership
48
+ * @param {Object} member - Member object
49
+ * @param {boolean} checkAssociation - Whether to check for specific association
50
+ * @returns {boolean} True if member has student membership
51
+ */
52
+ function hasStudentMembership(member, checkAssociation) {
53
+ const memberships = member?.memberships;
54
+ if (!Array.isArray(memberships)) return false;
55
+
56
+ return memberships.some(membership => {
57
+ const isStudent = membership.membertype === MEMBERSHIPS_TYPES.STUDENT;
58
+ const hasCorrectAssociation = !checkAssociation || membership.association === SITE_ASSOCIATION;
59
+ return isStudent && hasCorrectAssociation;
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Check if member should have student badge
65
+ * @param {Object} member - Member object
66
+ * @returns {boolean} True if should have badge
67
+ */
68
+ function shouldHaveStudentBadge(member) {
69
+ return hasStudentMembership(member, true);
70
+ }
71
+
72
+ /**
73
+ * Get addresses by status, excluding main address
74
+ * @param {Array} addresses - All addresses
75
+ * @param {Array} addressDisplayOption - Display options
76
+ * @returns {Array} Processed addresses
77
+ */
78
+ function getAddressesByStatus(addresses = [], addressDisplayOption = []) {
79
+ const visible = addresses.filter(addr => addr.addressStatus !== ADDRESS_STATUS_TYPES.DONT_SHOW);
80
+ if (visible.length < 2) {
81
+ return [];
82
+ }
83
+ const opts = Array.isArray(addressDisplayOption) ? addressDisplayOption : [];
84
+ const mainOpt = opts.find(o => o.isMain);
85
+ const mainKey = mainOpt ? mainOpt.key : visible[0].key;
86
+ return visible
87
+ .filter(addr => addr?.key !== mainKey)
88
+ .map(addr => {
89
+ const addressString = formatAddress(addr);
90
+ return addressString ? { _id: generateId(), address: addressString } : null;
91
+ })
92
+ .filter(Boolean);
93
+ }
94
+
95
+ /**
96
+ * Get member profile data formatted for display
97
+ * @param {Object} member - Member object
98
+ * @returns {Object} Formatted profile data
99
+ */
100
+ function getMemberProfileData(member) {
101
+ if (!member) {
102
+ throw new Error('member is required');
103
+ }
104
+
105
+ const addresses = member.addresses || [];
106
+ const licenceNo = member.licenses
107
+ ?.map(val => val.license)
108
+ .filter(Boolean)
109
+ .join(', ');
110
+ const processedAddresses = getAddressesByStatus(member.addresses, member.addressDisplayOption);
111
+
112
+ const memberships = member.memberships || [];
113
+ const abmp = memberships.find(m => m.association === SITE_ASSOCIATION);
114
+
115
+ const areasOfPractices =
116
+ member.areasOfPractices
117
+ ?.filter(item => typeof item === 'string' && item.trim().length > 0)
118
+ .map(item => item.trim())
119
+ .sort((a, b) =>
120
+ a.localeCompare(b, undefined, {
121
+ sensitivity: 'base',
122
+ numeric: true,
123
+ })
124
+ ) || [];
125
+
126
+ const mainAddress = getMainAddress(member.addressDisplayOption, addresses);
127
+
128
+ return {
129
+ mainAddress: mainAddress,
130
+ testimonials: member.testimonial || [],
131
+ licenceNo,
132
+ processedAddresses,
133
+ memberSince: (member.showABMP && abmp && formatDateToMonthYear(abmp?.membersince)) || '',
134
+ shouldHaveStudentBadge: shouldHaveStudentBadge(member),
135
+ logoImage: member.logoImage,
136
+ fullName: member.fullName,
137
+ profileImage: member.profileImage,
138
+ showContactForm: member.showContactForm,
139
+ bookingUrl: member.bookingUrl,
140
+ aboutService: member.aboutService,
141
+ businessName: (member.showBusinessName && member.businessName) || '',
142
+ phone: member.toShowPhone || '',
143
+ areasOfPractices,
144
+ gallery: member.gallery,
145
+ bannerImages: member.bannerImages,
146
+ showWixUrl: member.showWixUrl,
147
+ _id: member._id,
148
+ url: member.url,
149
+ city: mainAddress?.city || '',
150
+ state: mainAddress?.state || '',
151
+ isPrivateMember: member.memberships.some(
152
+ membership => membership.membertype === MEMBERSHIPS_TYPES.PAC_STAFF
153
+ ),
154
+ };
155
+ }
156
+
157
+ module.exports = {
158
+ generateSEOTitle,
159
+ stripHtmlTags,
160
+ hasStudentMembership,
161
+ shouldHaveStudentBadge,
162
+ getAddressesByStatus,
163
+ getMemberProfileData,
164
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "1.8.37",
3
+ "version": "1.8.39",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
package/public/consts.js CHANGED
@@ -94,6 +94,17 @@ const DEFAULT_BUSINESS_NAME_TEXT = 'Business name not provided';
94
94
 
95
95
  const DEFAULT_PROFILE_IMAGE =
96
96
  'https://static.wixstatic.com/media/1d7134_e052e9b1d0a543d0980650e16dd6d374~mv2.jpg';
97
+
98
+ const ABMP_LOGO_URL =
99
+ 'https://static.wixstatic.com/media/3eb9c9_b7447dc19d1b48cc99348a828cf77278~mv2.png';
100
+
101
+ const SITE_ASSOCIATION = 'ABMP';
102
+
103
+ const MEMBERSHIPS_TYPES = {
104
+ STUDENT: 'Student',
105
+ PAC_STAFF: 'PAC STAFF',
106
+ };
107
+
97
108
  module.exports = {
98
109
  REGEX,
99
110
  COLLECTIONS,
@@ -107,4 +118,7 @@ module.exports = {
107
118
  FREE_WEBSITE_TEXT_STATES,
108
119
  DEFAULT_BUSINESS_NAME_TEXT,
109
120
  DEFAULT_PROFILE_IMAGE,
121
+ ABMP_LOGO_URL,
122
+ SITE_ASSOCIATION,
123
+ MEMBERSHIPS_TYPES,
110
124
  };
package/public/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  module.exports = {
2
2
  ...require('./consts'),
3
3
  ...require('./messages'),
4
+ ...require('./Utils/sharedUtils'),
4
5
  };