notify-me-wl 1.4.4 → 2.0.0

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.
package/index.js DELETED
@@ -1,693 +0,0 @@
1
- // Access token for the API
2
- const ACCESS_TOKEN =
3
- 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJRWlJkS3JabXJmMEk3WkhXRUtqNWRLTEhQanFubWJFeV9iNmpSbHdya1drIn0.eyJleHAiOjE3MjMwMzI3NzIsImlhdCI6MTcyMzAyOTE3MiwianRpIjoiNDJiNGYwM2QtMDNiNS00NmZlLTk5YmItZDQ2NTdhNjk5NGNiIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLmF1LWF3cy50aGV3aXNobGlzdC5pby9hdXRoL3JlYWxtcy90d2NNYWluIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjI2YTIyNTAyLWRhNzQtNDhkMC1iZWFiLTgzY2E0YTlmMDdlOSIsInR5cCI6IkJlYXJlciIsImF6cCI6InR3Yy1wb3MtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjQxYWQwMDc4LWVmYTItNGVjMi1hM2I2LTIzYjBkNDM3YjEzOCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiaHR0cHM6Ly9sb2NhbGhvc3QiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInR3Yy1wb3MtdXNlciIsIm9mZmxpbmVfYWNjZXNzIiwidHdjLXN0b3JlLW93bmVyIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6InRlbmFudGlkIHN0b3JlIHByb2ZpbGUgZW1haWwiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInRlbmFudGlkIjoidmlrdG9yaWEtd29vZHMiLCJuYW1lIjoiTWF0dCBIYW1wc2hpcmUiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJtYXR0QHRoZXdpc2hsaXN0LmlvIiwic3RvcmUiOiIyMDUiLCJnaXZlbl9uYW1lIjoiTWF0dCIsImZhbWlseV9uYW1lIjoiSGFtcHNoaXJlIiwiZW1haWwiOiJtYXR0QHRoZXdpc2hsaXN0LmlvIn0.iZkMgwH74njjXUWvImkJomHYr91Lr8ZGrZGwslEGcV3vbNuoNc5CocvNWW476o-LoSh-LsKf-MLiYN1XvOuPDF3fGoGCEbMh6_M0RJcrhVWogkj81fx4ukvDPCFIjgoDCV9WIuehV9dsSWa7E0irZeE6MUVhLwRIaTzKtxgzUUKrAqBtI_HKpyo8TUGQBiYlrc85QFUyuoKbKg-QaRn_SObRLDB8ooIBJvIlgklXQt1ZYBM2HUOc5L1bAQwfzcrWEvl6eYiQHXCSPqS0rPGoaGC6v5ydBo9VMxtVHGladDHLrO3Gt2BnIGMBoYrKTAmt7j0KABwPyB3CmAIwj_pOBQ'; // Replace `ACCESS_TOKEN` with your actual access token to authenticate API requests.
4
- const TENANT_ID = 'victoria-woods'; // Replace `TENANT_ID` with your actual tenant ID.
5
-
6
- // Session-cached access token obtained from the Shopify App Proxy (proxy auth mode).
7
- let cachedProxyToken = null;
8
-
9
- // Fetch a tenant-scoped access token from the Shopify App Proxy.
10
- // The path is relative/same-origin, so Shopify's App Proxy layer appends and
11
- // signs `shop`, `logged_in_customer_id`, `timestamp`, and `signature`.
12
- // Returns the access token string, or null on failure (e.g. customer not logged in).
13
- async function getProxyAccessToken() {
14
- if (cachedProxyToken) return cachedProxyToken;
15
- try {
16
- const response = await fetch('/apps/twc-sdk/auth/token', {
17
- method: 'GET',
18
- headers: { Accept: 'application/json' },
19
- });
20
- const body = await response.json().catch(() => null);
21
- if (
22
- response.ok &&
23
- body &&
24
- body.success &&
25
- body.data &&
26
- body.data.access_token
27
- ) {
28
- cachedProxyToken = body.data.access_token;
29
- return cachedProxyToken;
30
- }
31
- return null;
32
- } catch (error) {
33
- console.error('Error fetching proxy access token:', error);
34
- return null;
35
- }
36
- }
37
-
38
- const COUNTRY_CODES = [
39
- { code: 'AF', name: 'Afghanistan' }, { code: 'AX', name: 'Åland Islands' },
40
- { code: 'AL', name: 'Albania' }, { code: 'DZ', name: 'Algeria' },
41
- { code: 'AS', name: 'American Samoa' }, { code: 'AD', name: 'Andorra' },
42
- { code: 'AO', name: 'Angola' }, { code: 'AI', name: 'Anguilla' },
43
- { code: 'AG', name: 'Antigua and Barbuda' }, { code: 'AR', name: 'Argentina' },
44
- { code: 'AM', name: 'Armenia' }, { code: 'AW', name: 'Aruba' },
45
- { code: 'AU', name: 'Australia' }, { code: 'AT', name: 'Austria' },
46
- { code: 'AZ', name: 'Azerbaijan' }, { code: 'BS', name: 'Bahamas' },
47
- { code: 'BH', name: 'Bahrain' }, { code: 'BD', name: 'Bangladesh' },
48
- { code: 'BB', name: 'Barbados' }, { code: 'BY', name: 'Belarus' },
49
- { code: 'BE', name: 'Belgium' }, { code: 'BZ', name: 'Belize' },
50
- { code: 'BJ', name: 'Benin' }, { code: 'BM', name: 'Bermuda' },
51
- { code: 'BT', name: 'Bhutan' }, { code: 'BO', name: 'Bolivia' },
52
- { code: 'BA', name: 'Bosnia and Herzegovina' }, { code: 'BW', name: 'Botswana' },
53
- { code: 'BR', name: 'Brazil' }, { code: 'BN', name: 'Brunei' },
54
- { code: 'BG', name: 'Bulgaria' }, { code: 'BF', name: 'Burkina Faso' },
55
- { code: 'BI', name: 'Burundi' }, { code: 'CV', name: 'Cabo Verde' },
56
- { code: 'KH', name: 'Cambodia' }, { code: 'CM', name: 'Cameroon' },
57
- { code: 'CA', name: 'Canada' }, { code: 'KY', name: 'Cayman Islands' },
58
- { code: 'CF', name: 'Central African Republic' }, { code: 'TD', name: 'Chad' },
59
- { code: 'CL', name: 'Chile' }, { code: 'CN', name: 'China' },
60
- { code: 'CO', name: 'Colombia' }, { code: 'KM', name: 'Comoros' },
61
- { code: 'CG', name: 'Congo' }, { code: 'CD', name: 'Congo, DR' },
62
- { code: 'CK', name: 'Cook Islands' }, { code: 'CR', name: 'Costa Rica' },
63
- { code: 'HR', name: 'Croatia' }, { code: 'CU', name: 'Cuba' },
64
- { code: 'CW', name: 'Curaçao' }, { code: 'CY', name: 'Cyprus' },
65
- { code: 'CZ', name: 'Czech Republic' }, { code: 'DK', name: 'Denmark' },
66
- { code: 'DJ', name: 'Djibouti' }, { code: 'DM', name: 'Dominica' },
67
- { code: 'DO', name: 'Dominican Republic' }, { code: 'EC', name: 'Ecuador' },
68
- { code: 'EG', name: 'Egypt' }, { code: 'SV', name: 'El Salvador' },
69
- { code: 'GQ', name: 'Equatorial Guinea' }, { code: 'ER', name: 'Eritrea' },
70
- { code: 'EE', name: 'Estonia' }, { code: 'SZ', name: 'Eswatini' },
71
- { code: 'ET', name: 'Ethiopia' }, { code: 'FO', name: 'Faroe Islands' },
72
- { code: 'FJ', name: 'Fiji' }, { code: 'FI', name: 'Finland' },
73
- { code: 'FR', name: 'France' }, { code: 'GF', name: 'French Guiana' },
74
- { code: 'PF', name: 'French Polynesia' }, { code: 'GA', name: 'Gabon' },
75
- { code: 'GM', name: 'Gambia' }, { code: 'GE', name: 'Georgia' },
76
- { code: 'DE', name: 'Germany' }, { code: 'GH', name: 'Ghana' },
77
- { code: 'GI', name: 'Gibraltar' }, { code: 'GR', name: 'Greece' },
78
- { code: 'GL', name: 'Greenland' }, { code: 'GD', name: 'Grenada' },
79
- { code: 'GP', name: 'Guadeloupe' }, { code: 'GU', name: 'Guam' },
80
- { code: 'GT', name: 'Guatemala' }, { code: 'GN', name: 'Guinea' },
81
- { code: 'GW', name: 'Guinea-Bissau' }, { code: 'GY', name: 'Guyana' },
82
- { code: 'HT', name: 'Haiti' }, { code: 'HN', name: 'Honduras' },
83
- { code: 'HK', name: 'Hong Kong' }, { code: 'HU', name: 'Hungary' },
84
- { code: 'IS', name: 'Iceland' }, { code: 'IN', name: 'India' },
85
- { code: 'ID', name: 'Indonesia' }, { code: 'IR', name: 'Iran' },
86
- { code: 'IQ', name: 'Iraq' }, { code: 'IE', name: 'Ireland' },
87
- { code: 'IM', name: 'Isle of Man' }, { code: 'IL', name: 'Israel' },
88
- { code: 'IT', name: 'Italy' }, { code: 'JM', name: 'Jamaica' },
89
- { code: 'JP', name: 'Japan' }, { code: 'JE', name: 'Jersey' },
90
- { code: 'JO', name: 'Jordan' }, { code: 'KZ', name: 'Kazakhstan' },
91
- { code: 'KE', name: 'Kenya' }, { code: 'KI', name: 'Kiribati' },
92
- { code: 'KR', name: 'Korea, South' }, { code: 'XK', name: 'Kosovo' },
93
- { code: 'KW', name: 'Kuwait' }, { code: 'KG', name: 'Kyrgyzstan' },
94
- { code: 'LA', name: 'Laos' }, { code: 'LV', name: 'Latvia' },
95
- { code: 'LB', name: 'Lebanon' }, { code: 'LS', name: 'Lesotho' },
96
- { code: 'LR', name: 'Liberia' }, { code: 'LY', name: 'Libya' },
97
- { code: 'LI', name: 'Liechtenstein' }, { code: 'LT', name: 'Lithuania' },
98
- { code: 'LU', name: 'Luxembourg' }, { code: 'MO', name: 'Macao' },
99
- { code: 'MG', name: 'Madagascar' }, { code: 'MW', name: 'Malawi' },
100
- { code: 'MY', name: 'Malaysia' }, { code: 'MV', name: 'Maldives' },
101
- { code: 'ML', name: 'Mali' }, { code: 'MT', name: 'Malta' },
102
- { code: 'MH', name: 'Marshall Islands' }, { code: 'MQ', name: 'Martinique' },
103
- { code: 'MR', name: 'Mauritania' }, { code: 'MU', name: 'Mauritius' },
104
- { code: 'MX', name: 'Mexico' }, { code: 'FM', name: 'Micronesia' },
105
- { code: 'MD', name: 'Moldova' }, { code: 'MC', name: 'Monaco' },
106
- { code: 'MN', name: 'Mongolia' }, { code: 'ME', name: 'Montenegro' },
107
- { code: 'MS', name: 'Montserrat' }, { code: 'MA', name: 'Morocco' },
108
- { code: 'MZ', name: 'Mozambique' }, { code: 'MM', name: 'Myanmar' },
109
- { code: 'NA', name: 'Namibia' }, { code: 'NR', name: 'Nauru' },
110
- { code: 'NP', name: 'Nepal' }, { code: 'NL', name: 'Netherlands' },
111
- { code: 'NC', name: 'New Caledonia' }, { code: 'NZ', name: 'New Zealand' },
112
- { code: 'NI', name: 'Nicaragua' }, { code: 'NE', name: 'Niger' },
113
- { code: 'NG', name: 'Nigeria' }, { code: 'NU', name: 'Niue' },
114
- { code: 'MK', name: 'North Macedonia' }, { code: 'NO', name: 'Norway' },
115
- { code: 'OM', name: 'Oman' }, { code: 'PK', name: 'Pakistan' },
116
- { code: 'PW', name: 'Palau' }, { code: 'PS', name: 'Palestine' },
117
- { code: 'PA', name: 'Panama' }, { code: 'PG', name: 'Papua New Guinea' },
118
- { code: 'PY', name: 'Paraguay' }, { code: 'PE', name: 'Peru' },
119
- { code: 'PH', name: 'Philippines' }, { code: 'PL', name: 'Poland' },
120
- { code: 'PT', name: 'Portugal' }, { code: 'PR', name: 'Puerto Rico' },
121
- { code: 'QA', name: 'Qatar' }, { code: 'RE', name: 'Réunion' },
122
- { code: 'RO', name: 'Romania' },
123
- { code: 'RW', name: 'Rwanda' }, { code: 'KN', name: 'Saint Kitts and Nevis' },
124
- { code: 'LC', name: 'Saint Lucia' }, { code: 'VC', name: 'Saint Vincent and the Grenadines' },
125
- { code: 'WS', name: 'Samoa' }, { code: 'SM', name: 'San Marino' },
126
- { code: 'SA', name: 'Saudi Arabia' }, { code: 'SN', name: 'Senegal' },
127
- { code: 'RS', name: 'Serbia' }, { code: 'SC', name: 'Seychelles' },
128
- { code: 'SL', name: 'Sierra Leone' }, { code: 'SG', name: 'Singapore' },
129
- { code: 'SK', name: 'Slovakia' }, { code: 'SI', name: 'Slovenia' },
130
- { code: 'SB', name: 'Solomon Islands' }, { code: 'SO', name: 'Somalia' },
131
- { code: 'ZA', name: 'South Africa' }, { code: 'SS', name: 'South Sudan' },
132
- { code: 'ES', name: 'Spain' }, { code: 'LK', name: 'Sri Lanka' },
133
- { code: 'SD', name: 'Sudan' }, { code: 'SR', name: 'Suriname' },
134
- { code: 'SE', name: 'Sweden' }, { code: 'CH', name: 'Switzerland' },
135
- { code: 'SY', name: 'Syria' }, { code: 'TW', name: 'Taiwan' },
136
- { code: 'TJ', name: 'Tajikistan' }, { code: 'TZ', name: 'Tanzania' },
137
- { code: 'TH', name: 'Thailand' }, { code: 'TL', name: 'Timor-Leste' },
138
- { code: 'TG', name: 'Togo' }, { code: 'TO', name: 'Tonga' },
139
- { code: 'TT', name: 'Trinidad and Tobago' }, { code: 'TN', name: 'Tunisia' },
140
- { code: 'TR', name: 'Turkey' }, { code: 'TM', name: 'Turkmenistan' },
141
- { code: 'TC', name: 'Turks and Caicos Islands' }, { code: 'TV', name: 'Tuvalu' },
142
- { code: 'UG', name: 'Uganda' }, { code: 'UA', name: 'Ukraine' },
143
- { code: 'AE', name: 'United Arab Emirates' }, { code: 'GB', name: 'United Kingdom' },
144
- { code: 'US', name: 'United States' }, { code: 'UY', name: 'Uruguay' },
145
- { code: 'UZ', name: 'Uzbekistan' }, { code: 'VU', name: 'Vanuatu' },
146
- { code: 'VE', name: 'Venezuela' }, { code: 'VN', name: 'Vietnam' },
147
- { code: 'VG', name: 'Virgin Islands, British' }, { code: 'VI', name: 'Virgin Islands, U.S.' },
148
- { code: 'YE', name: 'Yemen' }, { code: 'ZM', name: 'Zambia' },
149
- { code: 'ZW', name: 'Zimbabwe' },
150
- ];
151
-
152
- // Detect country/province/market context using priority fallback chain
153
- function detectCountryContext() {
154
- const ctx = { countryCode: null, provinceCode: null, marketId: null };
155
-
156
- // Priority 1: Shopify localization/country context
157
- const shopify = window.Shopify;
158
- if (shopify) {
159
- if (typeof shopify.country === 'string') ctx.countryCode = shopify.country;
160
- const mkt = shopify.markets?.currentMarket;
161
- if (mkt) {
162
- if (mkt.countryCode) ctx.countryCode = mkt.countryCode;
163
- if (mkt.id != null) ctx.marketId = String(mkt.id);
164
- }
165
- if (!ctx.countryCode && typeof shopify.locale === 'string') {
166
- const parts = shopify.locale.split('-');
167
- if (parts.length > 1) ctx.countryCode = parts[parts.length - 1].toUpperCase();
168
- }
169
- }
170
-
171
- // Priority 2: Existing customer shipping country
172
- if (!ctx.countryCode || !ctx.provinceCode) {
173
- const addr = window.customer?.default_address;
174
- if (addr) {
175
- if (!ctx.countryCode && addr.country_code) ctx.countryCode = addr.country_code;
176
- if (!ctx.provinceCode && addr.province_code) ctx.provinceCode = addr.province_code;
177
- }
178
- }
179
-
180
- // Priority 3: Browser locale fallback
181
- if (!ctx.countryCode) {
182
- const parts = (navigator.language || '').split('-');
183
- if (parts.length > 1) ctx.countryCode = parts[parts.length - 1].toUpperCase();
184
- }
185
-
186
- return ctx;
187
- }
188
-
189
- document.addEventListener('DOMContentLoaded', function () {
190
- const wrapper = document.getElementById('notification-widget');
191
- if (!wrapper) return;
192
-
193
- // Define and apply styles. Everything is namespaced under #twc-nm-overlay and
194
- // uses `font: inherit` so the widget blends into the host store's theme
195
- // without colliding with (or being broken by) the store's own CSS.
196
- const styles = `
197
- .notification-btn {
198
- display: inline-block;
199
- width: 100%;
200
- padding: 12px 24px;
201
- background: #fff;
202
- border: 1px solid #111827;
203
- border-radius: 8px;
204
- color: #111827;
205
- font: inherit;
206
- text-align: center;
207
- text-transform: uppercase;
208
- cursor: pointer;
209
- }
210
- .notification-btn:hover {
211
- background: #111827;
212
- color: #fff;
213
- }
214
- #twc-nm-overlay {
215
- position: fixed;
216
- inset: 0;
217
- z-index: 99999;
218
- display: flex;
219
- align-items: center;
220
- justify-content: center;
221
- padding: 16px;
222
- background: rgba(17, 17, 17, 0.55);
223
- opacity: 0;
224
- visibility: hidden;
225
- transition: opacity 200ms ease;
226
- }
227
- #twc-nm-overlay.is-open {
228
- opacity: 1;
229
- visibility: visible;
230
- }
231
- #twc-nm-overlay * {
232
- box-sizing: border-box;
233
- }
234
- #twc-nm-overlay .twc-nm-card {
235
- position: relative;
236
- width: 100%;
237
- max-width: 400px;
238
- max-height: calc(100vh - 32px);
239
- overflow-y: auto;
240
- padding: 24px;
241
- background: #fff;
242
- color: #111827;
243
- border-radius: 12px;
244
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
245
- font: inherit;
246
- transform: translateY(8px);
247
- transition: transform 200ms ease;
248
- }
249
- #twc-nm-overlay.is-open .twc-nm-card {
250
- transform: translateY(0);
251
- }
252
- #twc-nm-overlay .twc-nm-close {
253
- position: absolute;
254
- top: 12px;
255
- right: 12px;
256
- width: 32px;
257
- height: 32px;
258
- display: flex;
259
- align-items: center;
260
- justify-content: center;
261
- padding: 0;
262
- border: none;
263
- background: transparent;
264
- border-radius: 6px;
265
- font-size: 24px;
266
- line-height: 1;
267
- color: #6b7280;
268
- cursor: pointer;
269
- }
270
- #twc-nm-overlay .twc-nm-close:hover {
271
- background: #f3f4f6;
272
- color: #111827;
273
- }
274
- #twc-nm-overlay .twc-nm-title {
275
- margin: 0 32px 4px 0;
276
- font-size: 18px;
277
- font-weight: 600;
278
- }
279
- #twc-nm-overlay .twc-nm-text {
280
- margin: 0 0 16px;
281
- font-size: 14px;
282
- line-height: 1.5;
283
- color: #6b7280;
284
- }
285
- #twc-nm-overlay .twc-nm-form {
286
- display: flex;
287
- flex-direction: column;
288
- gap: 14px;
289
- }
290
- #twc-nm-overlay .twc-nm-field {
291
- display: flex;
292
- flex-direction: column;
293
- gap: 6px;
294
- }
295
- #twc-nm-overlay .twc-nm-label {
296
- font-size: 13px;
297
- font-weight: 500;
298
- color: #374151;
299
- }
300
- #twc-nm-overlay .twc-nm-input,
301
- #twc-nm-overlay .twc-nm-select {
302
- width: 100%;
303
- padding: 10px 12px;
304
- font-size: 14px;
305
- font-family: inherit;
306
- color: #111827;
307
- background: #fff;
308
- border: 1px solid #e5e7eb;
309
- border-radius: 8px;
310
- }
311
- #twc-nm-overlay .twc-nm-input:focus,
312
- #twc-nm-overlay .twc-nm-select:focus {
313
- outline: none;
314
- border-color: #111827;
315
- box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);
316
- }
317
- #twc-nm-overlay .twc-nm-select {
318
- appearance: none;
319
- -webkit-appearance: none;
320
- padding-right: 36px;
321
- cursor: pointer;
322
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
323
- background-repeat: no-repeat;
324
- background-position: right 12px center;
325
- }
326
- #twc-nm-overlay .twc-nm-checks {
327
- display: flex;
328
- flex-direction: column;
329
- gap: 10px;
330
- }
331
- #twc-nm-overlay .twc-nm-check {
332
- display: flex;
333
- align-items: center;
334
- gap: 8px;
335
- font-size: 13px;
336
- color: #374151;
337
- cursor: pointer;
338
- }
339
- #twc-nm-overlay .twc-nm-check input {
340
- width: 16px;
341
- height: 16px;
342
- margin: 0;
343
- accent-color: #111827;
344
- }
345
- #twc-nm-overlay .twc-nm-status {
346
- font-size: 13px;
347
- line-height: 1.5;
348
- padding: 10px 12px;
349
- border-radius: 8px;
350
- }
351
- #twc-nm-overlay .twc-nm-status--error {
352
- color: #b91c1c;
353
- background: #fef2f2;
354
- }
355
- #twc-nm-overlay .twc-nm-status--success {
356
- color: #15803d;
357
- background: #f0fdf4;
358
- }
359
- #twc-nm-overlay .twc-nm-submit {
360
- width: 100%;
361
- padding: 12px 16px;
362
- font-size: 14px;
363
- font-weight: 600;
364
- font-family: inherit;
365
- color: #fff;
366
- background: #111827;
367
- border: none;
368
- border-radius: 8px;
369
- cursor: pointer;
370
- transition: background 150ms ease;
371
- }
372
- #twc-nm-overlay .twc-nm-submit:hover {
373
- background: #374151;
374
- }
375
- #twc-nm-overlay .twc-nm-submit:disabled {
376
- opacity: 0.6;
377
- cursor: default;
378
- }
379
- @media (prefers-reduced-motion: reduce) {
380
- #twc-nm-overlay,
381
- #twc-nm-overlay .twc-nm-card {
382
- transition: none;
383
- }
384
- #twc-nm-overlay .twc-nm-card {
385
- transform: none;
386
- }
387
- }
388
- `;
389
-
390
- const styleSheet = document.createElement('style');
391
- styleSheet.type = 'text/css';
392
- styleSheet.innerText = styles;
393
- document.head.appendChild(styleSheet);
394
-
395
- // Create and append the overlay for the popup
396
- const overlay = document.createElement('div');
397
- overlay.innerHTML = `
398
- <div id="twc-nm-overlay" role="dialog" aria-modal="true" aria-labelledby="popup-title">
399
- <div class="twc-nm-card">
400
- <button type="button" id="popup-close" class="twc-nm-close" aria-label="Close">&times;</button>
401
- <h2 id="popup-title" class="twc-nm-title"></h2>
402
- <p id="popup-text" class="twc-nm-text"></p>
403
- <form id="popup-form" class="twc-nm-form">
404
- <div class="twc-nm-field">
405
- <label class="twc-nm-label" for="twc-nm-size">Size</label>
406
- <select class="twc-nm-select" id="twc-nm-size" name="select-size" required>
407
- <option value="" disabled selected>Select a size</option>
408
- </select>
409
- </div>
410
- </form>
411
- </div>
412
- </div>
413
- `;
414
-
415
- wrapper.appendChild(overlay);
416
-
417
- const overlayEl = document.getElementById('twc-nm-overlay');
418
- const popupClose = document.getElementById('popup-close');
419
- const popupOpenButton = document.getElementById('popup-open');
420
- const popupTitle = document.getElementById('popup-title');
421
- const popupText = document.getElementById('popup-text');
422
- const sizeSelect = document.querySelector("select[name='select-size']");
423
- const form = document.getElementById('popup-form');
424
-
425
- if (
426
- !overlayEl ||
427
- !popupClose ||
428
- !popupOpenButton ||
429
- !popupTitle ||
430
- !sizeSelect
431
- )
432
- return;
433
-
434
- // Parse fields and type to be included in the form from data attribute
435
- const fields = JSON.parse(
436
- popupOpenButton.getAttribute('data-fields') || '["email"]',
437
- );
438
- const type = popupOpenButton.getAttribute('data-type') || 'notify-me';
439
- // Auth mode: 'token' (default, hardcoded ACCESS_TOKEN) or 'proxy' (Shopify App Proxy).
440
- const authMode = popupOpenButton.getAttribute('data-auth') || 'token';
441
- // Tenant for X-Twc-Tenant header; used in both auth modes.
442
- const tenant = popupOpenButton.getAttribute('data-tenant') || TENANT_ID;
443
- const typeConfig = {
444
- 'notify-me': {
445
- text: 'Register to receive a notification as soon as this item is back in stock',
446
- buttonText: 'Notify Me',
447
- },
448
- 'coming-soon': {
449
- text: 'Register your interest to hear more about this item',
450
- buttonText: 'Register Interest',
451
- },
452
- };
453
-
454
- // Get product data from Shopify's global variable
455
- const productData = window?.currentProduct;
456
-
457
- const countryCtx = detectCountryContext();
458
- const resolvedMarketId =
459
- popupOpenButton.getAttribute('data-market-id') || countryCtx.marketId;
460
-
461
- // Map for form fields
462
- popupText.innerText = typeConfig[type].text;
463
-
464
- const countryOptions = COUNTRY_CODES.map(
465
- (c) =>
466
- `<option value="${c.code}"${c.code === countryCtx.countryCode ? ' selected' : ''}>${c.name}</option>`,
467
- ).join('');
468
-
469
- // Wrap an input/select in a labeled field row.
470
- const field = (id, label, control) =>
471
- `<div class="twc-nm-field"><label class="twc-nm-label" for="${id}">${label}</label>${control}</div>`;
472
-
473
- const fieldMap = {
474
- email: field(
475
- 'twc-nm-email',
476
- 'Email',
477
- `<input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="you@example.com" required />`,
478
- ),
479
- mobile: field(
480
- 'twc-nm-mobile',
481
- 'Mobile',
482
- `<input class="twc-nm-input" id="twc-nm-mobile" name="mobile" type="tel" placeholder="Mobile number" required />`,
483
- ),
484
- firstName: field(
485
- 'twc-nm-firstname',
486
- 'First name',
487
- `<input class="twc-nm-input" id="twc-nm-firstname" name="firstName" type="text" placeholder="First name" required />`,
488
- ),
489
- lastName: field(
490
- 'twc-nm-lastname',
491
- 'Last name',
492
- `<input class="twc-nm-input" id="twc-nm-lastname" name="lastName" type="text" placeholder="Last name" required />`,
493
- ),
494
- countryCode: field(
495
- 'twc-nm-country',
496
- 'Country',
497
- `<select class="twc-nm-select" id="twc-nm-country" name="countryCode"><option value="">Select country</option>${countryOptions}</select>`,
498
- ),
499
- provinceCode: field(
500
- 'twc-nm-province',
501
- 'State / Province',
502
- `<input class="twc-nm-input" id="twc-nm-province" name="provinceCode" type="text" placeholder="State / province code" value="${countryCtx.provinceCode || ''}" />`,
503
- ),
504
- };
505
-
506
- // Add fields to the form based on the parsed fields
507
- fields.forEach((name) => {
508
- if (fieldMap[name]) {
509
- form.insertAdjacentHTML('beforeend', fieldMap[name]);
510
- }
511
- });
512
-
513
- form.insertAdjacentHTML(
514
- 'beforeend',
515
- `<div class="twc-nm-checks">
516
- <label class="twc-nm-check"><input type="checkbox" name="mailList" /> Email me store updates</label>
517
- <label class="twc-nm-check"><input type="checkbox" name="mailListSms" /> Text me store updates</label>
518
- </div>`,
519
- );
520
- form.insertAdjacentHTML(
521
- 'beforeend',
522
- `<div id="popup-status" class="twc-nm-status" role="status" aria-live="polite" hidden></div>`,
523
- );
524
- form.insertAdjacentHTML(
525
- 'beforeend',
526
- `<button type="submit" class="twc-nm-submit">${typeConfig[type].buttonText}</button>`,
527
- );
528
-
529
- const popupStatus = document.getElementById('popup-status');
530
- const submitBtn = form.querySelector('.twc-nm-submit');
531
-
532
- // Inline status messaging (replaces alert()).
533
- function setStatus(kind, message) {
534
- popupStatus.textContent = message;
535
- popupStatus.className = `twc-nm-status twc-nm-status--${kind}`;
536
- popupStatus.hidden = false;
537
- }
538
- function clearStatus() {
539
- popupStatus.hidden = true;
540
- popupStatus.textContent = '';
541
- }
542
-
543
- // Open / close the popup.
544
- let variantsPopulated = false;
545
- let lastFocused = null;
546
-
547
- function onKeydown(event) {
548
- if (event.key === 'Escape') closePopup();
549
- }
550
-
551
- function openPopup() {
552
- lastFocused = document.activeElement;
553
- overlayEl.classList.add('is-open');
554
- document.body.style.overflow = 'hidden';
555
- document.addEventListener('keydown', onKeydown);
556
- }
557
-
558
- function closePopup() {
559
- overlayEl.classList.remove('is-open');
560
- document.body.style.overflow = '';
561
- document.removeEventListener('keydown', onKeydown);
562
- clearStatus();
563
- if (lastFocused && typeof lastFocused.focus === 'function') {
564
- lastFocused.focus();
565
- }
566
- }
567
-
568
- function showPopup() {
569
- openPopup();
570
-
571
- if (!productData) {
572
- form.hidden = true;
573
- setStatus('error', 'Product information is unavailable. Please refresh the page.');
574
- return;
575
- }
576
-
577
- popupTitle.textContent = productData.title;
578
-
579
- if (!variantsPopulated) {
580
- (productData.variants || []).forEach((variant) => {
581
- const option = document.createElement('option');
582
- option.value = variant.title;
583
- option.textContent = variant.title;
584
- sizeSelect.appendChild(option);
585
- });
586
- variantsPopulated = true;
587
- }
588
-
589
- const firstControl = form.querySelector('select, input');
590
- if (firstControl) firstControl.focus();
591
- }
592
-
593
- popupOpenButton.addEventListener('click', showPopup);
594
- popupClose.addEventListener('click', closePopup);
595
- // Close when clicking the backdrop (but not the card).
596
- overlayEl.addEventListener('click', function (event) {
597
- if (event.target === overlayEl) closePopup();
598
- });
599
-
600
- // Form submission handler
601
- form.addEventListener('submit', async function (event) {
602
- event.preventDefault();
603
-
604
- const selectedVariant = (productData?.variants || []).find(
605
- (variant) => variant.title === sizeSelect.value,
606
- );
607
- if (!selectedVariant) {
608
- setStatus('error', 'Please select a size.');
609
- return;
610
- }
611
-
612
- const formData = {
613
- variantRef: selectedVariant.id,
614
- email: form.querySelector("input[name='email']").value,
615
- subscribe: form.querySelector("input[name='mailList']").checked,
616
- subscribeSms: form.querySelector("input[name='mailListSms']").checked,
617
- };
618
-
619
- if (type === 'coming-soon') {
620
- formData.comingSoon = true;
621
- } else {
622
- formData.notifyMe = true;
623
- }
624
- if (fields.includes('firstName')) {
625
- formData.firstName = form.querySelector("input[name='firstName']").value;
626
- }
627
- if (fields.includes('lastName')) {
628
- formData.lastName = form.querySelector("input[name='lastName']").value;
629
- }
630
- if (fields.includes('mobile')) {
631
- formData.mobile = form.querySelector("input[name='mobile']").value;
632
- }
633
-
634
- // Country / province / market — manual fields take precedence over auto-detected values
635
- if (fields.includes('countryCode')) {
636
- const selected = form.querySelector("select[name='countryCode']").value;
637
- if (selected) formData.countryCode = selected;
638
- } else if (countryCtx.countryCode) {
639
- formData.countryCode = countryCtx.countryCode;
640
- }
641
-
642
- if (fields.includes('provinceCode')) {
643
- const val = form.querySelector("input[name='provinceCode']").value;
644
- if (val) formData.provinceCode = val;
645
- } else if (countryCtx.provinceCode) {
646
- formData.provinceCode = countryCtx.provinceCode;
647
- }
648
-
649
- if (resolvedMarketId) formData.marketId = resolvedMarketId;
650
-
651
- clearStatus();
652
- const originalButtonText = submitBtn.textContent;
653
- submitBtn.disabled = true;
654
- submitBtn.textContent = 'Sending…';
655
-
656
- try {
657
- // Resolve the Authorization token based on auth mode.
658
- let authToken = ACCESS_TOKEN;
659
- if (authMode === 'proxy') {
660
- const proxyToken = await getProxyAccessToken();
661
- if (!proxyToken) {
662
- setStatus('error', 'Please log in to your account to continue.');
663
- return;
664
- }
665
- authToken = `Bearer ${proxyToken}`;
666
- }
667
-
668
- const url = `https://api.au-sandbox.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest`;
669
- const response = await fetch(url, {
670
- method: 'POST',
671
- headers: {
672
- 'Content-Type': 'application/json',
673
- Authorization: authToken,
674
- 'X-Twc-Tenant': tenant,
675
- },
676
- body: JSON.stringify(formData),
677
- });
678
-
679
- if (response.ok) {
680
- setStatus('success', "You're on the list. We'll be in touch.");
681
- setTimeout(closePopup, 1500);
682
- } else {
683
- setStatus('error', 'Something went wrong. Please try again.');
684
- }
685
- } catch (error) {
686
- console.error('Error submitting form:', error);
687
- setStatus('error', 'Something went wrong. Please try again.');
688
- } finally {
689
- submitBtn.disabled = false;
690
- submitBtn.textContent = originalButtonText;
691
- }
692
- });
693
- });