@kelviq/js-promotions-ui 0.0.1
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/.env.development +3 -0
- package/.env.production +2 -0
- package/.eslintignore +6 -0
- package/.eslintrc +18 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +1 -0
- package/.lintstagedrc +4 -0
- package/.prettierignore +6 -0
- package/.prettierrc +15 -0
- package/.stylelintignore +6 -0
- package/.stylelintrc +20 -0
- package/CHANGELOG.md +24 -0
- package/DEVELOPER.md +61 -0
- package/LICENSE.md +9 -0
- package/README.md +1 -0
- package/commitlint.config.cjs +4 -0
- package/dts-bundle-generator.config.ts +11 -0
- package/favicon.svg +15 -0
- package/index.html +485 -0
- package/package.json +79 -0
- package/scripts/publish.sh +67 -0
- package/src/dom-utils.ts +6 -0
- package/src/global.d.ts +9 -0
- package/src/http-utils.ts +62 -0
- package/src/index.ts +2 -0
- package/src/public-script.js +44 -0
- package/src/sdk.ts +504 -0
- package/src/types.ts +60 -0
- package/src/utils.ts +146 -0
- package/src/vite-env.d.ts +1 -0
- package/test/dom-utils.test.ts +49 -0
- package/test/http-utils.test.ts +152 -0
- package/test/sdk.test.ts +575 -0
- package/test/setup.ts +83 -0
- package/test/utils.test.ts +137 -0
- package/tsconfig.json +26 -0
- package/vite.config.ts +57 -0
package/index.html
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>KQ Promotions UI — Demo</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
margin: 0;
|
|
11
|
+
padding: 0;
|
|
12
|
+
}
|
|
13
|
+
body {
|
|
14
|
+
font-family: system-ui, sans-serif;
|
|
15
|
+
background: #f5f5f5;
|
|
16
|
+
color: #222;
|
|
17
|
+
}
|
|
18
|
+
h1 {
|
|
19
|
+
font-size: 1.5rem;
|
|
20
|
+
margin-bottom: 1rem;
|
|
21
|
+
}
|
|
22
|
+
h2 {
|
|
23
|
+
font-size: 1.1rem;
|
|
24
|
+
margin-bottom: 0.75rem;
|
|
25
|
+
color: #444;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.page {
|
|
29
|
+
max-width: 900px;
|
|
30
|
+
margin: 0 auto;
|
|
31
|
+
padding: 2rem 1rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Section */
|
|
35
|
+
.section {
|
|
36
|
+
background: #fff;
|
|
37
|
+
border: 1px solid #e0e0e0;
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
padding: 1.5rem;
|
|
40
|
+
margin-bottom: 1.5rem;
|
|
41
|
+
}
|
|
42
|
+
.section-title {
|
|
43
|
+
font-size: 0.7rem;
|
|
44
|
+
font-weight: 700;
|
|
45
|
+
letter-spacing: 0.08em;
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
color: #888;
|
|
48
|
+
margin-bottom: 0.5rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Cards */
|
|
52
|
+
.cards {
|
|
53
|
+
display: flex;
|
|
54
|
+
gap: 1rem;
|
|
55
|
+
flex-wrap: wrap;
|
|
56
|
+
}
|
|
57
|
+
.card {
|
|
58
|
+
background: #f9f9f9;
|
|
59
|
+
border: 1px solid #e0e0e0;
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
padding: 1rem;
|
|
62
|
+
min-width: 160px;
|
|
63
|
+
}
|
|
64
|
+
.card-label {
|
|
65
|
+
font-size: 0.75rem;
|
|
66
|
+
color: #888;
|
|
67
|
+
margin-bottom: 0.25rem;
|
|
68
|
+
}
|
|
69
|
+
.card-name {
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
margin-bottom: 0.5rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Price display */
|
|
75
|
+
.price {
|
|
76
|
+
font-size: 1.8rem;
|
|
77
|
+
font-weight: 700;
|
|
78
|
+
line-height: 1;
|
|
79
|
+
}
|
|
80
|
+
.price sup {
|
|
81
|
+
font-size: 1rem;
|
|
82
|
+
vertical-align: super;
|
|
83
|
+
}
|
|
84
|
+
.price sub {
|
|
85
|
+
font-size: 0.9rem;
|
|
86
|
+
}
|
|
87
|
+
.original-price {
|
|
88
|
+
font-size: 1rem;
|
|
89
|
+
color: #999;
|
|
90
|
+
text-decoration: line-through;
|
|
91
|
+
margin-top: 0.25rem;
|
|
92
|
+
}
|
|
93
|
+
.discount-badge {
|
|
94
|
+
display: inline-block;
|
|
95
|
+
background: #e53e3e;
|
|
96
|
+
color: #fff;
|
|
97
|
+
font-size: 0.7rem;
|
|
98
|
+
font-weight: 700;
|
|
99
|
+
padding: 2px 6px;
|
|
100
|
+
border-radius: 4px;
|
|
101
|
+
margin-top: 0.4rem;
|
|
102
|
+
}
|
|
103
|
+
.coupon {
|
|
104
|
+
font-size: 0.8rem;
|
|
105
|
+
margin-top: 0.4rem;
|
|
106
|
+
color: #555;
|
|
107
|
+
}
|
|
108
|
+
.coupon code {
|
|
109
|
+
background: #fffbea;
|
|
110
|
+
border: 1px dashed #ccc;
|
|
111
|
+
padding: 2px 6px;
|
|
112
|
+
border-radius: 4px;
|
|
113
|
+
font-weight: 600;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Button */
|
|
117
|
+
button {
|
|
118
|
+
background: #3182ce;
|
|
119
|
+
color: #fff;
|
|
120
|
+
border: none;
|
|
121
|
+
border-radius: 5px;
|
|
122
|
+
padding: 0.5rem 1rem;
|
|
123
|
+
cursor: pointer;
|
|
124
|
+
font-size: 0.85rem;
|
|
125
|
+
}
|
|
126
|
+
button:hover {
|
|
127
|
+
background: #2b6cb0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Output box */
|
|
131
|
+
pre {
|
|
132
|
+
background: #1a1a2e;
|
|
133
|
+
color: #a8dadc;
|
|
134
|
+
border-radius: 6px;
|
|
135
|
+
padding: 1rem;
|
|
136
|
+
font-size: 0.78rem;
|
|
137
|
+
overflow-x: auto;
|
|
138
|
+
margin-top: 0.75rem;
|
|
139
|
+
white-space: pre-wrap;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Table */
|
|
143
|
+
table {
|
|
144
|
+
width: 100%;
|
|
145
|
+
border-collapse: collapse;
|
|
146
|
+
font-size: 0.85rem;
|
|
147
|
+
}
|
|
148
|
+
th {
|
|
149
|
+
text-align: left;
|
|
150
|
+
font-size: 0.7rem;
|
|
151
|
+
text-transform: uppercase;
|
|
152
|
+
letter-spacing: 0.05em;
|
|
153
|
+
color: #888;
|
|
154
|
+
padding: 0.4rem 0.5rem;
|
|
155
|
+
border-bottom: 1px solid #eee;
|
|
156
|
+
}
|
|
157
|
+
td {
|
|
158
|
+
padding: 0.5rem;
|
|
159
|
+
border-bottom: 1px solid #f0f0f0;
|
|
160
|
+
}
|
|
161
|
+
td:first-child {
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* kq-discount-applied body class demo */
|
|
166
|
+
body.kq-discount-applied .discount-badge {
|
|
167
|
+
display: inline-block;
|
|
168
|
+
}
|
|
169
|
+
.discount-badge {
|
|
170
|
+
display: none;
|
|
171
|
+
}
|
|
172
|
+
.kq-original-price-display {
|
|
173
|
+
display: none;
|
|
174
|
+
}
|
|
175
|
+
body.kq-discount-applied .kq-original-price-display {
|
|
176
|
+
display: block;
|
|
177
|
+
}
|
|
178
|
+
</style>
|
|
179
|
+
</head>
|
|
180
|
+
<body>
|
|
181
|
+
<div class="page">
|
|
182
|
+
<h1>KQ Promotions UI — Demo</h1>
|
|
183
|
+
|
|
184
|
+
<!-- ─── 1. BANNER ──────────────────────────────────────────────── -->
|
|
185
|
+
<div class="section">
|
|
186
|
+
<div class="section-title">1 · Banner Widget</div>
|
|
187
|
+
<h2>Auto-rendered via <code>widgets[type=BANNER]</code></h2>
|
|
188
|
+
<p style="font-size: 0.85rem; color: #666">
|
|
189
|
+
The banner is injected automatically by the SDK after
|
|
190
|
+
<code>init()</code>. Look above the page for the banner if the API
|
|
191
|
+
returns one.
|
|
192
|
+
</p>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<!-- ─── 2. AUTO PRICE (data-kq-price) ─────────────────────────── -->
|
|
196
|
+
<div class="section">
|
|
197
|
+
<div class="section-title">
|
|
198
|
+
2 · Auto Price Update — <code>data-kq-price</code>
|
|
199
|
+
</div>
|
|
200
|
+
<h2>SDK updates these elements automatically on init</h2>
|
|
201
|
+
<div class="cards">
|
|
202
|
+
<!-- Basic: price only -->
|
|
203
|
+
<div class="card" data-kq-price="9.99">
|
|
204
|
+
<div class="card-label">Basic</div>
|
|
205
|
+
<div class="card-name">Starter</div>
|
|
206
|
+
<div class="price">
|
|
207
|
+
<sup data-kq-currency-symbol></sup
|
|
208
|
+
><span data-kq-price-integer></span>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="discount-badge">Sale!</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<!-- Pro: price + original + decimal -->
|
|
214
|
+
<div class="card">
|
|
215
|
+
<div class="card-label">Pro</div>
|
|
216
|
+
<div class="card-name">Growth</div>
|
|
217
|
+
<div class="price" data-kq-price="49" data-kq-show-decimal="true">
|
|
218
|
+
<sup data-kq-currency-symbol></sup
|
|
219
|
+
><span data-kq-price-integer></span
|
|
220
|
+
><span data-kq-price-decimal-separator></span
|
|
221
|
+
><span data-kq-price-decimal></span>
|
|
222
|
+
</div>
|
|
223
|
+
<div
|
|
224
|
+
class="original-price kq-original-price-display"
|
|
225
|
+
data-kq-original-price-display="79"
|
|
226
|
+
data-kq-rel="growth-price"
|
|
227
|
+
>
|
|
228
|
+
<span data-kq-currency-symbol></span
|
|
229
|
+
><span data-kq-price-formatted></span>
|
|
230
|
+
</div>
|
|
231
|
+
<div class="discount-badge">Limited offer</div>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<!-- Enterprise: formatted price + currency code -->
|
|
235
|
+
<div class="card">
|
|
236
|
+
<div class="card-label">Enterprise</div>
|
|
237
|
+
<div class="card-name">Scale</div>
|
|
238
|
+
<div
|
|
239
|
+
class="price"
|
|
240
|
+
data-kq-price="199"
|
|
241
|
+
data-kq-currency-display="code"
|
|
242
|
+
>
|
|
243
|
+
<span data-kq-price-formatted></span>
|
|
244
|
+
</div>
|
|
245
|
+
<div
|
|
246
|
+
class="original-price kq-original-price-display"
|
|
247
|
+
data-kq-original-price-display="299"
|
|
248
|
+
data-kq-currency-display="code"
|
|
249
|
+
>
|
|
250
|
+
<span data-kq-price-formatted></span>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="discount-badge">Sale!</div>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<!-- Lifetime: no decimal -->
|
|
256
|
+
<div class="card" data-kq-price="499" data-kq-show-decimal="false">
|
|
257
|
+
<div class="card-label">Lifetime</div>
|
|
258
|
+
<div class="card-name">Forever</div>
|
|
259
|
+
<div class="price">
|
|
260
|
+
<sup data-kq-currency-symbol></sup
|
|
261
|
+
><span data-kq-price-integer></span>
|
|
262
|
+
</div>
|
|
263
|
+
<div class="discount-badge">Best value</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<!-- ─── 3. CURRENCY DISPLAY VARIANTS ──────────────────────────── -->
|
|
269
|
+
<div class="section">
|
|
270
|
+
<div class="section-title">3 · Currency Display Variants</div>
|
|
271
|
+
<h2>Same price, different <code>data-kq-currency-display</code></h2>
|
|
272
|
+
<table>
|
|
273
|
+
<tr>
|
|
274
|
+
<th>Option</th>
|
|
275
|
+
<th>Formatted Output</th>
|
|
276
|
+
</tr>
|
|
277
|
+
<tr>
|
|
278
|
+
<td>symbol (default)</td>
|
|
279
|
+
<td data-kq-price="99" data-kq-currency-display="symbol">
|
|
280
|
+
<span data-kq-price-formatted></span>
|
|
281
|
+
</td>
|
|
282
|
+
</tr>
|
|
283
|
+
<tr>
|
|
284
|
+
<td>code</td>
|
|
285
|
+
<td data-kq-price="99" data-kq-currency-display="code">
|
|
286
|
+
<span data-kq-price-formatted></span>
|
|
287
|
+
</td>
|
|
288
|
+
</tr>
|
|
289
|
+
<tr>
|
|
290
|
+
<td>name</td>
|
|
291
|
+
<td data-kq-price="99" data-kq-currency-display="name">
|
|
292
|
+
<span data-kq-price-formatted></span>
|
|
293
|
+
</td>
|
|
294
|
+
</tr>
|
|
295
|
+
</table>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<!-- ─── 4. getUpdatedPrice (JS API) ───────────────────────────── -->
|
|
299
|
+
<div class="section">
|
|
300
|
+
<div class="section-title">
|
|
301
|
+
4 · <code>getUpdatedPrice</code> — JS API
|
|
302
|
+
</div>
|
|
303
|
+
<h2>Fetch a computed price programmatically</h2>
|
|
304
|
+
<button onclick="testGetUpdatedPrice()">
|
|
305
|
+
Run getUpdatedPrice(100)
|
|
306
|
+
</button>
|
|
307
|
+
<pre id="get-price-output">— click the button —</pre>
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
<!-- ─── 5. updatePriceElement (JS API) ───────────────────────── -->
|
|
311
|
+
<div class="section">
|
|
312
|
+
<div class="section-title">
|
|
313
|
+
5 · <code>updatePriceElement</code> — JS API
|
|
314
|
+
</div>
|
|
315
|
+
<h2>Update a specific element by reference</h2>
|
|
316
|
+
<div class="card" style="margin-bottom: 0.75rem">
|
|
317
|
+
<div class="card-label">Target element</div>
|
|
318
|
+
<div
|
|
319
|
+
id="update-element-target"
|
|
320
|
+
data-kq-price="250"
|
|
321
|
+
data-kq-show-decimal="true"
|
|
322
|
+
>
|
|
323
|
+
<div class="price">
|
|
324
|
+
<sup data-kq-currency-symbol></sup
|
|
325
|
+
><span data-kq-price-integer></span
|
|
326
|
+
><span data-kq-price-decimal-separator></span
|
|
327
|
+
><span data-kq-price-decimal></span>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
<button onclick="testUpdatePriceElement()">
|
|
332
|
+
Call updatePriceElement
|
|
333
|
+
</button>
|
|
334
|
+
<pre id="update-element-output">— click the button —</pre>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<!-- ─── 6. updatePrice with template (JS API) ─────────────────── -->
|
|
338
|
+
<div class="section">
|
|
339
|
+
<div class="section-title">
|
|
340
|
+
6 · <code>updatePrice</code> — Batch with Template
|
|
341
|
+
</div>
|
|
342
|
+
<h2>Inject rendered price into arbitrary HTML</h2>
|
|
343
|
+
<div
|
|
344
|
+
id="template-output-1"
|
|
345
|
+
class="card"
|
|
346
|
+
style="margin-bottom: 0.5rem; min-height: 40px"
|
|
347
|
+
></div>
|
|
348
|
+
<div
|
|
349
|
+
id="template-output-2"
|
|
350
|
+
class="card"
|
|
351
|
+
style="margin-bottom: 0.75rem; min-height: 40px"
|
|
352
|
+
></div>
|
|
353
|
+
<button onclick="testUpdatePrice()">Run updatePrice batch</button>
|
|
354
|
+
<pre id="update-price-output">— click the button —</pre>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<!-- ─── 7. API Response ────────────────────────────────────────── -->
|
|
358
|
+
<div class="section">
|
|
359
|
+
<div class="section-title">7 · Raw API Response</div>
|
|
360
|
+
<h2>
|
|
361
|
+
Full <code>apiResponse</code> from last
|
|
362
|
+
<code>getUpdatedPrice</code> call
|
|
363
|
+
</h2>
|
|
364
|
+
<pre id="api-response-output">— run section 4 first —</pre>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
<!-- Set up queue before SDK module loads (module scripts are deferred) -->
|
|
369
|
+
<script>
|
|
370
|
+
window.KQPromotionUISDK =
|
|
371
|
+
window.KQPromotionUISDK ||
|
|
372
|
+
function () {
|
|
373
|
+
(window.KQPromotionUISDK.q = window.KQPromotionUISDK.q || []).push(
|
|
374
|
+
Array.prototype.slice.call(arguments)
|
|
375
|
+
);
|
|
376
|
+
};
|
|
377
|
+
</script>
|
|
378
|
+
|
|
379
|
+
<!-- Load SDK directly from source — Vite handles TS + HMR -->
|
|
380
|
+
<script type="module" src="./src/index.ts"></script>
|
|
381
|
+
|
|
382
|
+
<script>
|
|
383
|
+
function resolvePdSDKFunction(e) {
|
|
384
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
385
|
+
return new Promise(function (resolve, reject) {
|
|
386
|
+
!(function poll() {
|
|
387
|
+
window.KQPromotionUISDK &&
|
|
388
|
+
typeof window.KQPromotionUISDK[e] === "function"
|
|
389
|
+
? window.KQPromotionUISDK[e]
|
|
390
|
+
.apply(null, args)
|
|
391
|
+
.then(resolve)
|
|
392
|
+
.catch(reject)
|
|
393
|
+
: setTimeout(poll, 50);
|
|
394
|
+
})();
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
window.KQPromotionUI = {
|
|
399
|
+
init: function (e) {
|
|
400
|
+
KQPromotionUISDK("init", e);
|
|
401
|
+
},
|
|
402
|
+
getUpdatedPrice: function (e, t) {
|
|
403
|
+
return resolvePdSDKFunction("getUpdatedPrice", e, t);
|
|
404
|
+
},
|
|
405
|
+
updatePriceElement: function (e, t) {
|
|
406
|
+
return resolvePdSDKFunction("updatePriceElement", e, t);
|
|
407
|
+
},
|
|
408
|
+
updatePrice: function (e) {
|
|
409
|
+
return resolvePdSDKFunction("updatePrice", e);
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
KQPromotionUI.init({
|
|
414
|
+
promotionId: "ade58436-0278-45cc-b3a4-3df2d62d340e",
|
|
415
|
+
accessToken:
|
|
416
|
+
"client-463ea556-ae15-4ae3-8728-cfa13f8f8086:58b13c32-4919-419b-8fc9-f6dc0ef64c11",
|
|
417
|
+
showBanner: true,
|
|
418
|
+
baseCurrencyCode: "USD",
|
|
419
|
+
baseCurrencySymbol: "$",
|
|
420
|
+
showDecimal: false,
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// ── Section 4: getUpdatedPrice ──
|
|
424
|
+
function testGetUpdatedPrice() {
|
|
425
|
+
document.getElementById("get-price-output").textContent = "Loading…";
|
|
426
|
+
KQPromotionUI.getUpdatedPrice(100, { showDecimal: true })
|
|
427
|
+
.then(function (result) {
|
|
428
|
+
document.getElementById("get-price-output").textContent =
|
|
429
|
+
JSON.stringify(result, null, 2);
|
|
430
|
+
document.getElementById("api-response-output").textContent =
|
|
431
|
+
JSON.stringify(result.apiResponse, null, 2);
|
|
432
|
+
})
|
|
433
|
+
.catch(function (err) {
|
|
434
|
+
document.getElementById("get-price-output").textContent =
|
|
435
|
+
"Error: " + err;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ── Section 5: updatePriceElement ──
|
|
440
|
+
function testUpdatePriceElement() {
|
|
441
|
+
var el = document.getElementById("update-element-target");
|
|
442
|
+
document.getElementById("update-element-output").textContent =
|
|
443
|
+
"Loading…";
|
|
444
|
+
KQPromotionUI.updatePriceElement(el, { showDecimal: true })
|
|
445
|
+
.then(function (result) {
|
|
446
|
+
document.getElementById("update-element-output").textContent =
|
|
447
|
+
JSON.stringify(result, null, 2);
|
|
448
|
+
})
|
|
449
|
+
.catch(function (err) {
|
|
450
|
+
document.getElementById("update-element-output").textContent =
|
|
451
|
+
"Error: " + err;
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// ── Section 6: updatePrice with template ──
|
|
456
|
+
function testUpdatePrice() {
|
|
457
|
+
document.getElementById("update-price-output").textContent = "Loading…";
|
|
458
|
+
KQPromotionUI.updatePrice([
|
|
459
|
+
{
|
|
460
|
+
element: document.getElementById("template-output-1"),
|
|
461
|
+
price: 29,
|
|
462
|
+
options: { showDecimal: false },
|
|
463
|
+
template:
|
|
464
|
+
"<div class='card-label'>Monthly</div><div class='price'>{{formattedPrice}} <small style='font-size:0.7rem;color:#888'>/mo</small></div>",
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
element: document.getElementById("template-output-2"),
|
|
468
|
+
price: 199,
|
|
469
|
+
options: { showDecimal: true },
|
|
470
|
+
template:
|
|
471
|
+
"<div class='card-label'>Annual</div><div class='price'>{{formattedPrice}} <small style='font-size:0.7rem;color:#888'>/yr</small></div><div class='coupon'>Code: <code>{{currencyCode}}</code></div>",
|
|
472
|
+
},
|
|
473
|
+
])
|
|
474
|
+
.then(function (results) {
|
|
475
|
+
document.getElementById("update-price-output").textContent =
|
|
476
|
+
JSON.stringify(results, null, 2);
|
|
477
|
+
})
|
|
478
|
+
.catch(function (err) {
|
|
479
|
+
document.getElementById("update-price-output").textContent =
|
|
480
|
+
"Error: " + err;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
</script>
|
|
484
|
+
</body>
|
|
485
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kelviq/js-promotions-ui",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"module": "./dist/js-promotions-ui.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/js-promotions-ui.js"
|
|
10
|
+
},
|
|
11
|
+
"./dist/": {
|
|
12
|
+
"import": "./dist/"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "vite --host",
|
|
18
|
+
"build": "rimraf build/**/* && tsc && vite build && dts-bundle-generator --config ./dts-bundle-generator.config.ts && copyfiles ./package.json build",
|
|
19
|
+
"minify:public-script": "terser src/public-script.js -o build/dist/public-script.min.js --compress --mangle",
|
|
20
|
+
"test": "vitest",
|
|
21
|
+
"commit": "cz",
|
|
22
|
+
"test:coverage": "vitest --coverage",
|
|
23
|
+
"lint:scripts": "eslint . --ext .ts",
|
|
24
|
+
"lint:styles": "stylelint ./**/*.{css,scss}",
|
|
25
|
+
"format:scripts": "prettier . --write",
|
|
26
|
+
"format:styles": "stylelint ./**/*.{css,scss} --fix",
|
|
27
|
+
"format": "npm run format:scripts && npm run format:styles",
|
|
28
|
+
"prepare": "husky && echo 'npx lint-staged' > .husky/pre-commit && git add .husky/pre-commit && husky install",
|
|
29
|
+
"uninstall-husky": "npm uninstall husky --no-save && git config --unset core.hooksPath && npx rimraf .husky",
|
|
30
|
+
"prepublishOnly": "npm run build",
|
|
31
|
+
"publish:beta": "bash scripts/publish.sh beta",
|
|
32
|
+
"publish:stable": "bash scripts/publish.sh stable"
|
|
33
|
+
},
|
|
34
|
+
"config": {
|
|
35
|
+
"commitizen": {
|
|
36
|
+
"path": "cz-conventional-changelog"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"lint-staged": {
|
|
40
|
+
"*.{js,ts,tsx,jsx}": [
|
|
41
|
+
"prettier --write",
|
|
42
|
+
"eslint --fix"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@commitlint/cli": "^19.8.1",
|
|
47
|
+
"@commitlint/config-conventional": "^19.8.1",
|
|
48
|
+
"@types/jsdom": "^21.1.7",
|
|
49
|
+
"@types/node": "^22.0.0",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
51
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
52
|
+
"@vitest/coverage-v8": "^2.0.4",
|
|
53
|
+
"commitizen": "^4.3.1",
|
|
54
|
+
"copyfiles": "^2.4.1",
|
|
55
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
56
|
+
"dts-bundle-generator": "^9.5.1",
|
|
57
|
+
"eslint": "^8.57.1",
|
|
58
|
+
"eslint-config-prettier": "^9.1.0",
|
|
59
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
60
|
+
"husky": "^8.0.0",
|
|
61
|
+
"jsdom": "^26.1.0",
|
|
62
|
+
"lint-staged": "^15.5.2",
|
|
63
|
+
"postcss": "^8.4.40",
|
|
64
|
+
"postcss-scss": "^4.0.9",
|
|
65
|
+
"prettier": "^3.5.3",
|
|
66
|
+
"rimraf": "^6.0.1",
|
|
67
|
+
"stylelint": "^16.8.1",
|
|
68
|
+
"stylelint-config-recommended": "^14.0.1",
|
|
69
|
+
"stylelint-config-sass-guidelines": "^12.0.0",
|
|
70
|
+
"stylelint-order": "^6.0.4",
|
|
71
|
+
"stylelint-prettier": "^5.0.2",
|
|
72
|
+
"terser": "^5.43.0",
|
|
73
|
+
"ts-node": "^10.9.2",
|
|
74
|
+
"typescript": "^5.3.3",
|
|
75
|
+
"vite": "^5.3.5",
|
|
76
|
+
"vite-plugin-static-copy": "^3.0.2",
|
|
77
|
+
"vitest": "^2.0.4"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# ── Usage ──
|
|
5
|
+
TAG="${1:-}"
|
|
6
|
+
if [[ "$TAG" != "beta" && "$TAG" != "stable" ]]; then
|
|
7
|
+
echo "Usage: bash scripts/publish.sh <beta|stable>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
12
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
13
|
+
|
|
14
|
+
# ── Step 1: Verify npm identity ──
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Checking npm login status..."
|
|
17
|
+
NPM_USER=$(npm whoami 2>/dev/null) || {
|
|
18
|
+
echo "Error: You are not logged in to npm."
|
|
19
|
+
echo "Run 'npm login' first."
|
|
20
|
+
exit 1
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ── Step 2: Check git state ──
|
|
24
|
+
if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then
|
|
25
|
+
echo ""
|
|
26
|
+
echo "Warning: You have uncommitted changes."
|
|
27
|
+
read -r -p "Continue anyway? (y/N) " git_confirm
|
|
28
|
+
if [[ "$git_confirm" != "y" && "$git_confirm" != "Y" ]]; then
|
|
29
|
+
echo "Aborted."
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ── Step 3: Confirm publish details ──
|
|
35
|
+
echo ""
|
|
36
|
+
echo "Publishing $PACKAGE_NAME"
|
|
37
|
+
echo " Version : $PACKAGE_VERSION"
|
|
38
|
+
echo " Tag : $TAG"
|
|
39
|
+
echo " npm user: $NPM_USER"
|
|
40
|
+
echo ""
|
|
41
|
+
read -r -p "Proceed? (y/N) " confirm
|
|
42
|
+
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
|
43
|
+
echo "Aborted."
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── Step 4: Quality checks ──
|
|
48
|
+
echo ""
|
|
49
|
+
echo "Running tests..."
|
|
50
|
+
npm run test -- --run
|
|
51
|
+
|
|
52
|
+
echo ""
|
|
53
|
+
echo "Building..."
|
|
54
|
+
npm run build
|
|
55
|
+
|
|
56
|
+
# ── Step 5: Publish ──
|
|
57
|
+
echo ""
|
|
58
|
+
if [[ "$TAG" == "beta" ]]; then
|
|
59
|
+
npm publish --tag beta --access public
|
|
60
|
+
else
|
|
61
|
+
npm publish --access public
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# ── Done ──
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Published $PACKAGE_NAME@$PACKAGE_VERSION ($TAG)"
|
|
67
|
+
echo "https://www.npmjs.com/package/$PACKAGE_NAME"
|
package/src/dom-utils.ts
ADDED