@pradip1995/segment-jewelry-subscribe 0.1.2 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/segment-jewelry-subscribe",
3
- "version": "0.1.2",
3
+ "version": "0.1.7",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/segment.css CHANGED
@@ -156,8 +156,9 @@
156
156
  }
157
157
 
158
158
  .jewelry-subscribe__privacy-icon {
159
+ display: inline-flex;
159
160
  flex-shrink: 0;
160
- font-size: 0.8125rem;
161
+ color: #d4a017;
161
162
  line-height: 1;
162
163
  }
163
164
 
@@ -248,6 +249,120 @@
248
249
  }
249
250
  }
250
251
 
252
+ /* No background image — centered editorial signup */
253
+ .jewelry-subscribe--editorial {
254
+ background: var(--jewelry-subscribe-editorial-bg, #ffffff);
255
+ border-top: 1px solid rgba(19, 38, 77, 0.06);
256
+ }
257
+
258
+ .jewelry-subscribe--editorial .jewelry-subscribe__inner {
259
+ padding: clamp(3.5rem, 7vw, 5.5rem) 1.25rem clamp(3.75rem, 8vw, 5.75rem);
260
+ }
261
+
262
+ .jewelry-subscribe--editorial .jewelry-subscribe__content {
263
+ max-width: 42rem;
264
+ }
265
+
266
+ .jewelry-subscribe--editorial .jewelry-subscribe__intro {
267
+ margin-bottom: 2.25rem;
268
+ }
269
+
270
+ .jewelry-subscribe--editorial .jewelry-subscribe__eyebrow {
271
+ margin: 0 0 1.15rem;
272
+ font-family: var(--font-sans, system-ui, sans-serif);
273
+ font-size: 0.6875rem;
274
+ font-weight: 600;
275
+ letter-spacing: 0.34em;
276
+ text-transform: uppercase;
277
+ color: var(--jewelry-subscribe-eyebrow, #5a6b7d);
278
+ }
279
+
280
+ .jewelry-subscribe--editorial .jewelry-subscribe__title {
281
+ margin: 0 0 1.35rem;
282
+ font-family: var(--font-display, var(--font-heading, Georgia, serif));
283
+ font-size: clamp(2.35rem, 5.8vw, 3.65rem);
284
+ font-weight: 500;
285
+ line-height: 1.04;
286
+ letter-spacing: -0.02em;
287
+ color: var(--jewelry-subscribe-title, #13264d);
288
+ }
289
+
290
+ .jewelry-subscribe--editorial .jewelry-subscribe__title::after {
291
+ content: "";
292
+ display: block;
293
+ width: 3.25rem;
294
+ height: 1px;
295
+ margin: 1.35rem auto 0;
296
+ background: var(--jewelry-subscribe-title-rule, rgba(19, 38, 77, 0.22));
297
+ }
298
+
299
+ .jewelry-subscribe--editorial .jewelry-subscribe__description {
300
+ margin: 0 auto;
301
+ max-width: 30rem;
302
+ font-family: var(--font-sans, system-ui, sans-serif);
303
+ font-size: clamp(0.9375rem, 1.55vw, 1.05rem);
304
+ font-weight: 400;
305
+ line-height: 1.75;
306
+ color: var(--jewelry-subscribe-description, #737373);
307
+ }
308
+
309
+ .jewelry-subscribe--editorial .jewelry-subscribe__form {
310
+ max-width: 34rem;
311
+ padding: 0.3rem;
312
+ border: 1px solid rgba(19, 38, 77, 0.12);
313
+ border-radius: 999px;
314
+ background: #ffffff;
315
+ box-shadow:
316
+ 0 1px 0 rgba(255, 255, 255, 0.9) inset,
317
+ 0 18px 48px rgba(19, 38, 77, 0.08);
318
+ }
319
+
320
+ .jewelry-subscribe--editorial .jewelry-subscribe__input {
321
+ min-height: 3.25rem;
322
+ padding: 0.875rem 1.15rem;
323
+ border: 0;
324
+ border-radius: 999px;
325
+ background: transparent;
326
+ }
327
+
328
+ .jewelry-subscribe--editorial .jewelry-subscribe__input:focus {
329
+ outline: none;
330
+ }
331
+
332
+ .jewelry-subscribe--editorial .jewelry-subscribe__submit {
333
+ min-height: 3.25rem;
334
+ min-width: 9.25rem;
335
+ padding: 0.875rem 1.65rem;
336
+ border-radius: 999px;
337
+ letter-spacing: 0.12em;
338
+ }
339
+
340
+ .jewelry-subscribe--editorial .jewelry-subscribe__privacy {
341
+ margin-top: 1.15rem;
342
+ font-size: 0.75rem;
343
+ color: var(--jewelry-subscribe-privacy, #5a6b7d);
344
+ }
345
+
346
+ @media (max-width: 640px) {
347
+ .jewelry-subscribe--editorial .jewelry-subscribe__inner {
348
+ padding: 2.75rem 1rem 3rem;
349
+ }
350
+
351
+ .jewelry-subscribe--editorial .jewelry-subscribe__form {
352
+ flex-direction: column;
353
+ gap: 0.65rem;
354
+ padding: 0.75rem;
355
+ border-radius: 1.25rem;
356
+ }
357
+
358
+ .jewelry-subscribe--editorial .jewelry-subscribe__input,
359
+ .jewelry-subscribe--editorial .jewelry-subscribe__submit {
360
+ border-radius: 0.85rem;
361
+ width: 100%;
362
+ min-width: 0;
363
+ }
364
+ }
365
+
251
366
  @media (min-width: 768px) {
252
367
  .jewelry-subscribe__inner {
253
368
  padding-left: 2rem;
@@ -257,4 +372,208 @@
257
372
  .jewelry-subscribe__background::after {
258
373
  background: transparent;
259
374
  }
375
+
376
+ /* Jewelry sits on the left; signup sits in the open pale area on the right. */
377
+ .jewelry-subscribe--banner {
378
+ display: flex;
379
+ align-items: center;
380
+ justify-content: flex-end;
381
+ min-height: clamp(240px, 21vw, 340px);
382
+ }
383
+
384
+ .jewelry-subscribe--banner .jewelry-subscribe__background-image {
385
+ object-position: center center;
386
+ }
387
+
388
+ .jewelry-subscribe--banner .jewelry-subscribe__inner {
389
+ width: min(34rem, 46%);
390
+ max-width: none;
391
+ margin: 0 clamp(1.5rem, 5vw, 4.75rem) 0 0;
392
+ padding: 1.15rem 0;
393
+ }
394
+
395
+ .jewelry-subscribe--banner .jewelry-subscribe__content {
396
+ max-width: none;
397
+ margin: 0;
398
+ }
399
+
400
+ .jewelry-subscribe--banner .jewelry-subscribe__eyebrow {
401
+ margin-bottom: 0.2rem;
402
+ font-size: clamp(1.05rem, 1.55vw, 1.45rem);
403
+ }
404
+
405
+ .jewelry-subscribe--banner .jewelry-subscribe__title {
406
+ margin-bottom: 0.45rem;
407
+ font-size: clamp(1.9rem, 3.05vw, 3rem);
408
+ line-height: 1.05;
409
+ }
410
+
411
+ .jewelry-subscribe--banner .jewelry-subscribe__description {
412
+ margin-bottom: 0.85rem;
413
+ font-size: clamp(0.78rem, 1.05vw, 0.95rem);
414
+ line-height: 1.45;
415
+ }
416
+
417
+ .jewelry-subscribe--banner .jewelry-subscribe__form {
418
+ max-width: none;
419
+ }
420
+
421
+ .jewelry-subscribe--banner .jewelry-subscribe__input {
422
+ min-height: 2.7rem;
423
+ background: #ffffff;
424
+ border-color: rgba(19, 38, 77, 0.16);
425
+ }
426
+
427
+ .jewelry-subscribe--banner .jewelry-subscribe__submit {
428
+ min-height: 2.7rem;
429
+ min-width: 8.25rem;
430
+ padding: 0.7rem 1.35rem;
431
+ }
432
+
433
+ .jewelry-subscribe--banner .jewelry-subscribe__privacy {
434
+ margin-top: 0.65rem;
435
+ font-size: 0.72rem;
436
+ }
437
+ }
438
+
439
+ /* Image left, signup panel right (reference newsletter block) */
440
+ .jewelry-subscribe--split {
441
+ display: flex;
442
+ flex-direction: column;
443
+ background: var(--jewelry-subscribe-split-panel-bg, #f9f4f2);
444
+ }
445
+
446
+ .jewelry-subscribe--split .jewelry-subscribe__media {
447
+ position: relative;
448
+ width: 100%;
449
+ min-height: clamp(220px, 42vw, 320px);
450
+ flex-shrink: 0;
451
+ }
452
+
453
+ .jewelry-subscribe--split .jewelry-subscribe__media-image {
454
+ object-fit: cover;
455
+ object-position: center center;
456
+ }
457
+
458
+ .jewelry-subscribe--split .jewelry-subscribe__panel {
459
+ flex: 1;
460
+ display: flex;
461
+ align-items: center;
462
+ justify-content: center;
463
+ padding: clamp(2rem, 5vw, 3.25rem) clamp(1.5rem, 4vw, 3.5rem);
464
+ box-sizing: border-box;
465
+ background: var(--jewelry-subscribe-split-panel-bg, #f9f4f2);
466
+ }
467
+
468
+ .jewelry-subscribe--split .jewelry-subscribe__content {
469
+ width: 100%;
470
+ max-width: 22rem;
471
+ margin: 0;
472
+ text-align: left;
473
+ }
474
+
475
+ .jewelry-subscribe--split .jewelry-subscribe__eyebrow {
476
+ margin-bottom: 0.5rem;
477
+ font-family: var(--font-sans, system-ui, sans-serif);
478
+ font-size: 0.6875rem;
479
+ font-weight: 600;
480
+ letter-spacing: 0.22em;
481
+ text-transform: uppercase;
482
+ }
483
+
484
+ .jewelry-subscribe--split .jewelry-subscribe__title {
485
+ margin-bottom: 0.65rem;
486
+ font-family: var(--font-sans, system-ui, sans-serif);
487
+ font-size: clamp(1.35rem, 2.8vw, 1.65rem);
488
+ font-weight: 500;
489
+ line-height: 1.25;
490
+ letter-spacing: -0.01em;
491
+ color: var(--jewelry-subscribe-title, #13264d);
492
+ }
493
+
494
+ .jewelry-subscribe--split .jewelry-subscribe__description {
495
+ margin-bottom: 1.35rem;
496
+ font-size: 0.875rem;
497
+ line-height: 1.55;
498
+ color: var(--jewelry-subscribe-description, #6b7280);
499
+ }
500
+
501
+ .jewelry-subscribe--split .jewelry-subscribe__form--stacked {
502
+ flex-direction: column;
503
+ align-items: stretch;
504
+ gap: 0.65rem;
505
+ max-width: none;
506
+ margin: 0;
507
+ }
508
+
509
+ .jewelry-subscribe--split .jewelry-subscribe__field--with-icon {
510
+ display: flex;
511
+ align-items: center;
512
+ gap: 0.5rem;
513
+ padding: 0 0.85rem;
514
+ border: 1px solid rgba(19, 38, 77, 0.14);
515
+ background: rgba(255, 255, 255, 0.65);
516
+ }
517
+
518
+ .jewelry-subscribe--split .jewelry-subscribe__field-icon {
519
+ display: inline-flex;
520
+ flex-shrink: 0;
521
+ color: rgba(19, 38, 77, 0.45);
522
+ line-height: 0;
523
+ }
524
+
525
+ .jewelry-subscribe--split .jewelry-subscribe__input {
526
+ flex: 1;
527
+ min-height: 2.85rem;
528
+ padding: 0.75rem 0;
529
+ border: 0;
530
+ background: transparent;
531
+ }
532
+
533
+ .jewelry-subscribe--split .jewelry-subscribe__input:focus {
534
+ outline: none;
535
+ }
536
+
537
+ .jewelry-subscribe--split .jewelry-subscribe__field--with-icon:focus-within {
538
+ outline: 2px solid rgba(19, 38, 77, 0.22);
539
+ outline-offset: -2px;
540
+ }
541
+
542
+ .jewelry-subscribe--split .jewelry-subscribe__submit {
543
+ width: 100%;
544
+ min-width: 0;
545
+ min-height: 2.85rem;
546
+ padding: 0.8rem 1.25rem;
547
+ font-size: 0.8125rem;
548
+ font-weight: 500;
549
+ letter-spacing: 0.04em;
550
+ text-transform: none;
551
+ }
552
+
553
+ .jewelry-subscribe--split .jewelry-subscribe__privacy {
554
+ justify-content: flex-start;
555
+ margin-top: 0.85rem;
556
+ font-size: 0.6875rem;
557
+ }
558
+
559
+ @media (min-width: 768px) {
560
+ .jewelry-subscribe--split {
561
+ flex-direction: row;
562
+ align-items: stretch;
563
+ min-height: clamp(280px, 28vw, 360px);
564
+ }
565
+
566
+ .jewelry-subscribe--split .jewelry-subscribe__media {
567
+ width: 50%;
568
+ min-height: 0;
569
+ }
570
+
571
+ .jewelry-subscribe--split .jewelry-subscribe__panel {
572
+ width: 50%;
573
+ padding: clamp(2rem, 4vw, 3rem) clamp(2rem, 5vw, 4rem);
574
+ }
575
+
576
+ .jewelry-subscribe--split .jewelry-subscribe__content {
577
+ max-width: 24rem;
578
+ }
260
579
  }
package/src/segment.tsx CHANGED
@@ -18,8 +18,9 @@ export default function JewelrySubscribe({
18
18
  emailPlaceholder = "Enter your email...",
19
19
  submitLabel = "Sign Up",
20
20
  privacyText = "We Respect your Privacy, Unsubscribe at any time",
21
- backgroundImageUrl = "/images/subscribe/subscribe-bg-desktop.png",
21
+ backgroundImageUrl,
22
22
  mobileBackgroundImageUrl,
23
+ layout = "center",
23
24
  }: {
24
25
  eyebrow?: string
25
26
  title?: string
@@ -30,6 +31,8 @@ export default function JewelrySubscribe({
30
31
  backgroundImageUrl?: string
31
32
  /** Optional. Jewelry/texture only — no baked-in copy or form. Falls back to desktop. */
32
33
  mobileBackgroundImageUrl?: string
34
+ /** `banner` overlays signup on a full-bleed photo; `split` is image left + form panel right. */
35
+ layout?: "center" | "banner" | "split"
33
36
  }) {
34
37
  const [email, setEmail] = useState("")
35
38
  const [submitStatus, setSubmitStatus] = useState<"idle" | "loading" | "success" | "error">("idle")
@@ -37,7 +40,10 @@ export default function JewelrySubscribe({
37
40
 
38
41
  const desktopBg = backgroundImageUrl?.trim() || mobileBackgroundImageUrl?.trim() || ""
39
42
  const mobileBg = mobileBackgroundImageUrl?.trim() || desktopBg
40
- const hasSeparateMobileBg = Boolean(mobileBg && mobileBg !== desktopBg)
43
+ const hasBackground = Boolean(desktopBg)
44
+ const hasSeparateMobileBg = Boolean(hasBackground && mobileBg && mobileBg !== desktopBg)
45
+ const useBannerLayout = hasBackground && layout === "banner"
46
+ const useSplitLayout = hasBackground && layout === "split"
41
47
 
42
48
  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
43
49
  event.preventDefault()
@@ -53,58 +59,106 @@ export default function JewelrySubscribe({
53
59
  setSubmitStatus("loading")
54
60
  setSubmitMessage("")
55
61
 
56
- const result = await subscribeNewsletter({ email: sanitizedEmail })
62
+ try {
63
+ const result = await subscribeNewsletter({ email: sanitizedEmail })
57
64
 
58
- if (result.success) {
59
- trackFormSubmit({ form_type: "jewelry_subscribe" })
60
- setSubmitStatus("success")
61
- setSubmitMessage("You're subscribed. Watch your inbox for exclusive updates.")
62
- setEmail("")
63
- } else {
65
+ if (result.success) {
66
+ trackFormSubmit({ form_type: "jewelry_subscribe" })
67
+ setSubmitStatus("success")
68
+ setSubmitMessage("You're subscribed. Watch your inbox for exclusive updates.")
69
+ setEmail("")
70
+ } else {
71
+ setSubmitStatus("error")
72
+ setSubmitMessage(result.error || "Something went wrong. Please try again.")
73
+ }
74
+ } catch {
64
75
  setSubmitStatus("error")
65
- setSubmitMessage(result.error || "Something went wrong. Please try again.")
76
+ setSubmitMessage("Could not reach the server. Please try again.")
66
77
  }
67
78
  }
68
79
 
69
- if (!desktopBg) return null
70
-
71
80
  return (
72
- <section className="jewelry-subscribe" aria-labelledby="jewelry-subscribe-title">
73
- <div className="jewelry-subscribe__background" aria-hidden="true">
74
- <Image
75
- src={desktopBg}
76
- alt=""
77
- fill
78
- className={
79
- hasSeparateMobileBg
80
- ? "jewelry-subscribe__background-image jewelry-subscribe__background-image--desktop"
81
- : "jewelry-subscribe__background-image jewelry-subscribe__background-image--single"
82
- }
83
- sizes="100vw"
84
- unoptimized
85
- />
86
- {hasSeparateMobileBg ? (
81
+ <section
82
+ className={`jewelry-subscribe${useBannerLayout ? " jewelry-subscribe--banner" : ""}${useSplitLayout ? " jewelry-subscribe--split" : ""}${!hasBackground ? " jewelry-subscribe--editorial" : ""}`}
83
+ aria-labelledby="jewelry-subscribe-title"
84
+ >
85
+ {useSplitLayout ? (
86
+ <div className="jewelry-subscribe__media" aria-hidden="true">
87
87
  <Image
88
- src={mobileBg}
88
+ src={desktopBg}
89
89
  alt=""
90
90
  fill
91
- className="jewelry-subscribe__background-image jewelry-subscribe__background-image--mobile"
91
+ className="jewelry-subscribe__media-image"
92
+ sizes="(min-width: 768px) 50vw, 100vw"
93
+ unoptimized
94
+ />
95
+ </div>
96
+ ) : hasBackground ? (
97
+ <div className="jewelry-subscribe__background" aria-hidden="true">
98
+ <Image
99
+ src={desktopBg}
100
+ alt=""
101
+ fill
102
+ className={
103
+ hasSeparateMobileBg
104
+ ? "jewelry-subscribe__background-image jewelry-subscribe__background-image--desktop"
105
+ : "jewelry-subscribe__background-image jewelry-subscribe__background-image--single"
106
+ }
92
107
  sizes="100vw"
93
108
  unoptimized
94
109
  />
95
- ) : null}
96
- </div>
110
+ {hasSeparateMobileBg ? (
111
+ <Image
112
+ src={mobileBg}
113
+ alt=""
114
+ fill
115
+ className="jewelry-subscribe__background-image jewelry-subscribe__background-image--mobile"
116
+ sizes="100vw"
117
+ unoptimized
118
+ />
119
+ ) : null}
120
+ </div>
121
+ ) : null}
97
122
 
98
- <div className="jewelry-subscribe__inner">
123
+ <div className={useSplitLayout ? "jewelry-subscribe__panel" : "jewelry-subscribe__inner"}>
99
124
  <div className="jewelry-subscribe__content">
100
- {eyebrow ? <p className="jewelry-subscribe__eyebrow">{eyebrow}</p> : null}
101
- <h2 id="jewelry-subscribe-title" className="jewelry-subscribe__title">
102
- {title}
103
- </h2>
104
- {description ? <p className="jewelry-subscribe__description">{description}</p> : null}
125
+ <div className="jewelry-subscribe__intro">
126
+ {eyebrow ? <p className="jewelry-subscribe__eyebrow">{eyebrow}</p> : null}
127
+ <h2 id="jewelry-subscribe-title" className="jewelry-subscribe__title">
128
+ {title}
129
+ </h2>
130
+ {description ? <p className="jewelry-subscribe__description">{description}</p> : null}
131
+ </div>
105
132
 
106
- <form onSubmit={handleSubmit} className="jewelry-subscribe__form">
107
- <label className="jewelry-subscribe__field">
133
+ <form
134
+ onSubmit={handleSubmit}
135
+ className={`jewelry-subscribe__form${useSplitLayout ? " jewelry-subscribe__form--stacked" : ""}`}
136
+ >
137
+ <label
138
+ className={`jewelry-subscribe__field${useSplitLayout ? " jewelry-subscribe__field--with-icon" : ""}`}
139
+ >
140
+ {useSplitLayout ? (
141
+ <span className="jewelry-subscribe__field-icon" aria-hidden="true">
142
+ <svg viewBox="0 0 20 16" width="18" height="14" fill="none">
143
+ <rect
144
+ x="1"
145
+ y="2.5"
146
+ width="18"
147
+ height="11"
148
+ rx="1"
149
+ stroke="currentColor"
150
+ strokeWidth="1.25"
151
+ />
152
+ <path
153
+ d="M1 4.5 10 10.5 19 4.5"
154
+ stroke="currentColor"
155
+ strokeWidth="1.25"
156
+ strokeLinecap="round"
157
+ strokeLinejoin="round"
158
+ />
159
+ </svg>
160
+ </span>
161
+ ) : null}
108
162
  <span className="sr-only">Email</span>
109
163
  <input
110
164
  type="email"
@@ -128,7 +182,14 @@ export default function JewelrySubscribe({
128
182
  {privacyText ? (
129
183
  <p className="jewelry-subscribe__privacy">
130
184
  <span className="jewelry-subscribe__privacy-icon" aria-hidden="true">
131
- 🔒
185
+ <svg viewBox="0 0 16 16" width="13" height="13" fill="none">
186
+ <rect x="3.2" y="7" width="9.6" height="6.2" rx="1" fill="currentColor" />
187
+ <path
188
+ d="M5.2 7V5.1a2.8 2.8 0 0 1 5.6 0V7"
189
+ stroke="currentColor"
190
+ strokeWidth="1.4"
191
+ />
192
+ </svg>
132
193
  </span>
133
194
  {privacyText}
134
195
  </p>
@@ -1,4 +1,4 @@
1
- import { loaderBus } from "@pradip1995/commerce-core/util/loader-bus"
1
+ "use server"
2
2
 
3
3
  export async function subscribeNewsletter({
4
4
  email,
@@ -10,15 +10,13 @@ export async function subscribeNewsletter({
10
10
  source?: string
11
11
  }): Promise<{ success: boolean; error?: string }> {
12
12
  const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY
13
- const baseUrl =
14
- process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || process.env.MEDUSA_BACKEND_URL || "http://localhost:9000"
13
+ const baseUrl = process.env.MEDUSA_BACKEND_URL || "http://127.0.0.1:9000"
15
14
 
16
15
  if (!publishableKey) {
17
16
  return { success: false, error: "Configuration error: Publishable API key is missing." }
18
17
  }
19
18
 
20
19
  try {
21
- loaderBus.begin()
22
20
  const response = await fetch(`${baseUrl}/store/contact-email-subscriptions`, {
23
21
  method: "POST",
24
22
  headers: {
@@ -31,20 +29,16 @@ export async function subscribeNewsletter({
31
29
  source,
32
30
  ...(name ? { name } : {}),
33
31
  }),
32
+ cache: "no-store",
34
33
  })
35
34
 
36
35
  if (!response.ok) {
37
- const error = await response.text().catch(() => "Unknown error")
38
- return { success: false, error: error || `HTTP ${response.status}: Failed to subscribe` }
36
+ const error = await response.text().catch(() => "")
37
+ return { success: false, error: error || `Could not subscribe (HTTP ${response.status}).` }
39
38
  }
40
39
 
41
40
  return { success: true }
42
- } catch (error) {
43
- return {
44
- success: false,
45
- error: error instanceof Error ? error.message : "An error occurred while subscribing",
46
- }
47
- } finally {
48
- loaderBus.end()
41
+ } catch {
42
+ return { success: false, error: "Could not reach the server. Please try again." }
49
43
  }
50
44
  }