create-brainerce-store 1.20.1 → 1.22.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-brainerce-store",
3
- "version": "1.20.1",
3
+ "version": "1.22.0",
4
4
  "description": "Scaffold a production-ready e-commerce storefront connected to Brainerce",
5
5
  "bin": {
6
6
  "create-brainerce-store": "dist/index.js"
@@ -1,31 +1,31 @@
1
- import type { NextConfig } from 'next';
2
-
3
- const nextConfig: NextConfig = {
4
- images: {
5
- remotePatterns: [{ protocol: 'https', hostname: '**' }],
6
- },
7
- async headers() {
8
- return [
9
- {
10
- source: '/(.*)',
11
- headers: [
12
- {
13
- key: 'Content-Security-Policy',
14
- value: [
15
- "default-src 'self'",
16
- "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.meshulam.co.il https://meshulam.co.il https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://js.stripe.com https://pay.google.com",
17
- "style-src 'self' 'unsafe-inline' https://cdn.meshulam.co.il",
18
- "img-src 'self' data: blob: https:",
19
- "font-src 'self' data:",
20
- "frame-src 'self' https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://*.creditguard.co.il https://js.stripe.com https://hooks.stripe.com https://pay.google.com",
21
- "connect-src 'self' https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://google.com https://pay.google.com https://*.stripe.com https://*.creditguard.co.il",
22
- "worker-src 'self' blob:",
23
- ].join('; '),
24
- },
25
- ],
26
- },
27
- ];
28
- },
29
- };
30
-
31
- export default nextConfig;
1
+ import type { NextConfig } from 'next';
2
+
3
+ const nextConfig: NextConfig = {
4
+ images: {
5
+ remotePatterns: [{ protocol: 'https', hostname: '**' }],
6
+ },
7
+ async headers() {
8
+ return [
9
+ {
10
+ source: '/(.*)',
11
+ headers: [
12
+ {
13
+ key: 'Content-Security-Policy',
14
+ value: [
15
+ "default-src 'self'",
16
+ "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.meshulam.co.il https://meshulam.co.il https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://js.stripe.com https://pay.google.com",
17
+ "style-src 'self' 'unsafe-inline' https://cdn.meshulam.co.il",
18
+ "img-src 'self' data: blob: https:",
19
+ "font-src 'self' data:",
20
+ "frame-src 'self' https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://*.creditguard.co.il https://js.stripe.com https://hooks.stripe.com https://pay.google.com https://secure.cardcom.solutions",
21
+ "connect-src 'self' https://*.meshulam.co.il https://grow.link https://*.grow.link https://*.grow.security https://google.com https://pay.google.com https://*.stripe.com https://*.creditguard.co.il",
22
+ "worker-src 'self' blob:",
23
+ ].join('; '),
24
+ },
25
+ ],
26
+ },
27
+ ];
28
+ },
29
+ };
30
+
31
+ export default nextConfig;
@@ -154,9 +154,6 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
154
154
  initialized.current = true;
155
155
 
156
156
  const client = getClient();
157
- // For iframe-based providers, redirect inside the iframe goes to a
158
- // lightweight callback page that sends postMessage to the parent window.
159
- // For redirect-based providers, go straight to order-confirmation.
160
157
  const iframeSuccessUrl = `${window.location.origin}/payment-complete?checkout_id=${checkoutId}`;
161
158
  const iframeFailedUrl = `${window.location.origin}/payment-complete?checkout_id=${checkoutId}&failed=true`;
162
159
  const redirectSuccessUrl = `${window.location.origin}/order-confirmation?checkout_id=${checkoutId}`;
@@ -537,15 +534,45 @@ export function PaymentStep({ checkoutId, className }: PaymentStepProps) {
537
534
 
538
535
  if (sdk.renderType === 'iframe') {
539
536
  return (
540
- <div className={cn('py-4', className)}>
541
- <iframe
542
- src={paymentIntent.clientSecret}
543
- className="w-full border-0"
544
- style={{ minHeight: '500px' }}
545
- title={t('payment')}
546
- allow="payment"
547
- />
548
- </div>
537
+ <>
538
+ {/* Modal overlay */}
539
+ <div className="fixed inset-0 z-50 flex items-start justify-center overflow-y-auto bg-black/50 py-6 backdrop-blur-sm">
540
+ <div className="relative mx-4 w-full max-w-md rounded-2xl bg-white shadow-2xl">
541
+ {/* Close button */}
542
+ <button
543
+ onClick={() => {
544
+ window.location.href = `/checkout?checkout_id=${checkoutId}&canceled=true`;
545
+ }}
546
+ className="absolute end-3 top-3 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-white/80 text-gray-500 shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-700"
547
+ aria-label="Close"
548
+ >
549
+ <svg
550
+ width="14"
551
+ height="14"
552
+ viewBox="0 0 14 14"
553
+ fill="none"
554
+ stroke="currentColor"
555
+ strokeWidth="2"
556
+ strokeLinecap="round"
557
+ >
558
+ <path d="M1 1l12 12M13 1L1 13" />
559
+ </svg>
560
+ </button>
561
+ <iframe
562
+ src={paymentIntent.clientSecret}
563
+ className="w-full rounded-2xl border-0"
564
+ style={{ height: '80vh' }}
565
+ title={t('payment')}
566
+ allow="payment"
567
+ />
568
+ </div>
569
+ </div>
570
+ {/* Placeholder so the checkout layout doesn't collapse */}
571
+ <div className={cn('flex flex-col items-center justify-center py-12', className)}>
572
+ <LoadingSpinner size="lg" />
573
+ <p className="text-muted-foreground mt-4 text-sm">{t('preparingPayment')}</p>
574
+ </div>
575
+ </>
549
576
  );
550
577
  }
551
578
 
@@ -0,0 +1,399 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 20 14% 4%;
8
+ --foreground: 38 25% 86%;
9
+ --primary: 38 60% 55%;
10
+ --primary-foreground: 20 14% 4%;
11
+ --secondary: 20 10% 10%;
12
+ --secondary-foreground: 38 25% 86%;
13
+ --muted: 20 10% 12%;
14
+ --muted-foreground: 30 10% 50%;
15
+ --accent: 38 60% 55%;
16
+ --accent-foreground: 20 14% 4%;
17
+ --destructive: 0 72% 51%;
18
+ --destructive-foreground: 38 25% 86%;
19
+ --border: 30 10% 16%;
20
+ --radius: 0.25rem;
21
+
22
+ /* Luxury-specific tokens */
23
+ --gold: 38 60% 55%;
24
+ --gold-light: 38 50% 72%;
25
+ --gold-dark: 38 70% 40%;
26
+ --surface: 20 12% 7%;
27
+ --surface-elevated: 20 10% 10%;
28
+ }
29
+
30
+ * {
31
+ @apply border-border;
32
+ }
33
+
34
+ body {
35
+ @apply bg-background text-foreground antialiased;
36
+ letter-spacing: 0.01em;
37
+ line-height: 1.7;
38
+ }
39
+
40
+ /* Luxury typography — editorial serif hierarchy */
41
+ h1,
42
+ h2,
43
+ h3,
44
+ h4 {
45
+ letter-spacing: 0.04em;
46
+ font-weight: 500;
47
+ line-height: 1.2;
48
+ }
49
+
50
+ h1 {
51
+ text-transform: uppercase;
52
+ letter-spacing: 0.1em;
53
+ font-weight: 400;
54
+ line-height: 1.1;
55
+ }
56
+
57
+ h2 {
58
+ text-transform: uppercase;
59
+ letter-spacing: 0.06em;
60
+ }
61
+
62
+ /* Elegant link underline animation */
63
+ a:not([class*="bg-"]) {
64
+ position: relative;
65
+ transition: color 0.3s ease, opacity 0.3s ease;
66
+ }
67
+
68
+ main a:not([class*="bg-"]):not([class*="group"])::after {
69
+ content: '';
70
+ position: absolute;
71
+ bottom: -1px;
72
+ left: 0;
73
+ width: 0;
74
+ height: 1px;
75
+ background: hsl(var(--gold));
76
+ transition: width 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
77
+ }
78
+
79
+ main a:not([class*="bg-"]):not([class*="group"]):hover::after {
80
+ width: 100%;
81
+ }
82
+
83
+ /* Refined text selection */
84
+ ::selection {
85
+ background: hsl(var(--gold) / 0.3);
86
+ color: hsl(38 25% 96%);
87
+ }
88
+
89
+ /* Subtle body texture overlay */
90
+ body::before {
91
+ content: '';
92
+ position: fixed;
93
+ inset: 0;
94
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.015'/%3E%3C/svg%3E");
95
+ pointer-events: none;
96
+ z-index: 9999;
97
+ }
98
+
99
+ /* Custom scrollbar */
100
+ ::-webkit-scrollbar {
101
+ width: 5px;
102
+ }
103
+
104
+ ::-webkit-scrollbar-track {
105
+ background: hsl(20 14% 4%);
106
+ }
107
+
108
+ ::-webkit-scrollbar-thumb {
109
+ background: hsl(30 10% 18%);
110
+ border-radius: 3px;
111
+ }
112
+
113
+ ::-webkit-scrollbar-thumb:hover {
114
+ background: hsl(var(--gold) / 0.5);
115
+ }
116
+
117
+ /* Smooth focus states */
118
+ :focus-visible {
119
+ outline: 1px solid hsl(var(--gold) / 0.5);
120
+ outline-offset: 3px;
121
+ }
122
+ }
123
+
124
+ @layer components {
125
+ /* ─── Buttons ─── */
126
+
127
+ /* Primary CTA — gold with shimmer sweep */
128
+ button[class*="bg-primary"],
129
+ a[class*="bg-primary"] {
130
+ position: relative;
131
+ overflow: hidden;
132
+ font-weight: 500;
133
+ letter-spacing: 0.08em;
134
+ text-transform: uppercase;
135
+ font-size: 0.78rem;
136
+ border: 1px solid hsl(var(--gold) / 0.3);
137
+ transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
138
+ }
139
+
140
+ button[class*="bg-primary"]:hover,
141
+ a[class*="bg-primary"]:hover {
142
+ box-shadow:
143
+ 0 0 20px hsl(var(--gold) / 0.2),
144
+ 0 4px 16px hsl(0 0% 0% / 0.3);
145
+ border-color: hsl(var(--gold) / 0.6);
146
+ opacity: 1 !important;
147
+ }
148
+
149
+ button[class*="bg-primary"]::after,
150
+ a[class*="bg-primary"]::after {
151
+ content: '';
152
+ position: absolute;
153
+ top: -50%;
154
+ left: -75%;
155
+ width: 50%;
156
+ height: 200%;
157
+ background: linear-gradient(
158
+ 90deg,
159
+ transparent,
160
+ hsl(var(--gold-light) / 0.12),
161
+ hsl(var(--gold-light) / 0.2),
162
+ transparent
163
+ );
164
+ transform: skewX(-20deg);
165
+ transition: left 0.7s ease;
166
+ pointer-events: none;
167
+ }
168
+
169
+ button[class*="bg-primary"]:hover::after,
170
+ a[class*="bg-primary"]:hover::after {
171
+ left: 130%;
172
+ }
173
+
174
+ /* Secondary/ghost buttons — thin border, minimal */
175
+ [class*="bg-secondary"] {
176
+ border: 1px solid hsl(var(--border));
177
+ transition: all 0.3s ease;
178
+ letter-spacing: 0.04em;
179
+ text-transform: uppercase;
180
+ font-size: 0.8rem;
181
+ }
182
+
183
+ [class*="bg-secondary"]:hover {
184
+ border-color: hsl(var(--gold) / 0.4);
185
+ background: hsl(var(--surface-elevated)) !important;
186
+ }
187
+
188
+ /* ─── Product Cards ─── */
189
+
190
+ [class*="group"][class*="border"] {
191
+ transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
192
+ border-color: hsl(var(--border) / 0.5);
193
+ background: hsl(var(--surface));
194
+ }
195
+
196
+ [class*="group"][class*="border"]:hover {
197
+ border-color: hsl(var(--gold) / 0.25);
198
+ box-shadow:
199
+ 0 8px 40px hsl(0 0% 0% / 0.5),
200
+ 0 0 0 1px hsl(var(--gold) / 0.08);
201
+ }
202
+
203
+ /* Product image — slow cinematic zoom */
204
+ [class*="group-hover\\:scale-105"] {
205
+ transition: transform 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) !important;
206
+ }
207
+
208
+ /* Product image overlay on hover — subtle dark gradient */
209
+ [class*="group"] [class*="aspect-square"] {
210
+ position: relative;
211
+ }
212
+
213
+ [class*="group"]:hover [class*="aspect-square"]::after {
214
+ content: '';
215
+ position: absolute;
216
+ inset: 0;
217
+ background: linear-gradient(
218
+ to top,
219
+ hsl(20 14% 4% / 0.3),
220
+ transparent 50%
221
+ );
222
+ pointer-events: none;
223
+ transition: opacity 0.5s ease;
224
+ }
225
+
226
+ /* ─── Form Elements ─── */
227
+
228
+ input[class*="border-border"],
229
+ select[class*="border-border"],
230
+ textarea[class*="border-border"] {
231
+ background: hsl(var(--surface)) !important;
232
+ transition: all 0.3s ease;
233
+ }
234
+
235
+ input[class*="border-border"]:focus,
236
+ select[class*="border-border"]:focus,
237
+ textarea[class*="border-border"]:focus {
238
+ border-color: hsl(var(--gold) / 0.5) !important;
239
+ box-shadow: 0 0 0 3px hsl(var(--gold) / 0.08);
240
+ background: hsl(var(--surface-elevated)) !important;
241
+ }
242
+
243
+ /* Placeholder text */
244
+ input::placeholder,
245
+ textarea::placeholder {
246
+ letter-spacing: 0.03em;
247
+ font-size: 0.85rem;
248
+ }
249
+
250
+ /* ─── Navigation & Header ─── */
251
+
252
+ header[class*="sticky"] {
253
+ backdrop-filter: blur(16px) saturate(1.2);
254
+ background: hsl(20 14% 4% / 0.8) !important;
255
+ border-bottom: 1px solid hsl(var(--gold) / 0.08) !important;
256
+ }
257
+
258
+ /* Nav links */
259
+ nav a {
260
+ letter-spacing: 0.06em;
261
+ text-transform: uppercase;
262
+ font-size: 0.75rem;
263
+ font-weight: 500;
264
+ }
265
+
266
+ /* ─── Badges & Tags ─── */
267
+
268
+ [class*="bg-muted"][class*="text-xs"] {
269
+ text-transform: uppercase;
270
+ letter-spacing: 0.1em;
271
+ font-size: 0.6rem;
272
+ font-weight: 600;
273
+ border: 1px solid hsl(var(--border));
274
+ }
275
+
276
+ /* Sale badges — dark red, refined */
277
+ [class*="bg-destructive"] {
278
+ background: hsl(0 60% 38%) !important;
279
+ font-weight: 600;
280
+ letter-spacing: 0.06em;
281
+ text-transform: uppercase;
282
+ font-size: 0.65rem;
283
+ }
284
+
285
+ /* ─── Price Display ─── */
286
+
287
+ /* Gold gradient on prices */
288
+ [class*="font-bold"][class*="text-lg"],
289
+ [class*="font-bold"][class*="text-xl"],
290
+ [class*="font-bold"][class*="text-2xl"] {
291
+ background: linear-gradient(
292
+ 135deg,
293
+ hsl(var(--gold-light)),
294
+ hsl(var(--gold)),
295
+ hsl(var(--gold-dark))
296
+ );
297
+ -webkit-background-clip: text;
298
+ -webkit-text-fill-color: transparent;
299
+ background-clip: text;
300
+ }
301
+
302
+ /* Strikethrough original prices */
303
+ [class*="line-through"] {
304
+ opacity: 0.4;
305
+ }
306
+
307
+ /* ─── Dividers & Separators ─── */
308
+
309
+ hr {
310
+ border-color: hsl(var(--border) / 0.3);
311
+ }
312
+
313
+ /* Decorative gold separator */
314
+ main > section + section {
315
+ border-top: 1px solid hsl(var(--gold) / 0.06);
316
+ }
317
+
318
+ /* ─── Cart & Checkout ─── */
319
+
320
+ /* Sticky summary panel */
321
+ [class*="sticky"][class*="top-"] {
322
+ background: hsl(var(--surface));
323
+ border: 1px solid hsl(var(--border) / 0.5);
324
+ }
325
+ }
326
+
327
+ /* ─── Animations ─── */
328
+
329
+ /* Elegant fade-in-up */
330
+ @keyframes luxury-fade-in {
331
+ from {
332
+ opacity: 0;
333
+ transform: translateY(16px);
334
+ }
335
+ to {
336
+ opacity: 1;
337
+ transform: translateY(0);
338
+ }
339
+ }
340
+
341
+ main > * {
342
+ animation: luxury-fade-in 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
343
+ }
344
+
345
+ main > *:nth-child(2) {
346
+ animation-delay: 0.1s;
347
+ }
348
+
349
+ main > *:nth-child(3) {
350
+ animation-delay: 0.2s;
351
+ }
352
+
353
+ main > *:nth-child(4) {
354
+ animation-delay: 0.3s;
355
+ }
356
+
357
+ /* Staggered grid item reveal */
358
+ @keyframes luxury-grid-item {
359
+ from {
360
+ opacity: 0;
361
+ transform: translateY(12px) scale(0.98);
362
+ }
363
+ to {
364
+ opacity: 1;
365
+ transform: translateY(0) scale(1);
366
+ }
367
+ }
368
+
369
+ [class*="grid"] > [class*="group"] {
370
+ animation: luxury-grid-item 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
371
+ }
372
+
373
+ [class*="grid"] > [class*="group"]:nth-child(2) {
374
+ animation-delay: 0.08s;
375
+ }
376
+
377
+ [class*="grid"] > [class*="group"]:nth-child(3) {
378
+ animation-delay: 0.16s;
379
+ }
380
+
381
+ [class*="grid"] > [class*="group"]:nth-child(4) {
382
+ animation-delay: 0.24s;
383
+ }
384
+
385
+ [class*="grid"] > [class*="group"]:nth-child(5) {
386
+ animation-delay: 0.32s;
387
+ }
388
+
389
+ [class*="grid"] > [class*="group"]:nth-child(6) {
390
+ animation-delay: 0.4s;
391
+ }
392
+
393
+ [class*="grid"] > [class*="group"]:nth-child(7) {
394
+ animation-delay: 0.48s;
395
+ }
396
+
397
+ [class*="grid"] > [class*="group"]:nth-child(8) {
398
+ animation-delay: 0.56s;
399
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "Luxury",
3
+ "description": "Dark, sophisticated design with Cormorant Garamond serif font and warm gold accents",
4
+ "font": {
5
+ "family": "Cormorant Garamond",
6
+ "import": "next/font/google"
7
+ },
8
+ "colors": {
9
+ "background": "20 14% 4%",
10
+ "foreground": "38 25% 86%",
11
+ "primary": "38 60% 55%",
12
+ "primary-foreground": "20 14% 4%",
13
+ "secondary": "20 10% 10%",
14
+ "secondary-foreground": "38 25% 86%",
15
+ "muted": "20 10% 12%",
16
+ "muted-foreground": "30 10% 50%",
17
+ "accent": "38 60% 55%",
18
+ "accent-foreground": "20 14% 4%",
19
+ "destructive": "0 72% 51%",
20
+ "border": "30 10% 16%"
21
+ },
22
+ "radius": "0.25rem"
23
+ }