create-brainerce-store 1.28.13 → 1.28.17

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.
Files changed (24) hide show
  1. package/dist/index.js +1 -1
  2. package/messages/en.json +390 -389
  3. package/messages/he.json +390 -389
  4. package/package.json +46 -46
  5. package/templates/nextjs/base/next.config.ts +47 -47
  6. package/templates/nextjs/base/src/app/api/auth/logout/route.ts +15 -15
  7. package/templates/nextjs/base/src/app/api/auth/oauth-callback/route.ts +66 -66
  8. package/templates/nextjs/base/src/app/api/auth/reset-password/route.ts +76 -76
  9. package/templates/nextjs/base/src/app/api/store/[...path]/route.ts +235 -229
  10. package/templates/nextjs/base/src/app/checkout/page.tsx +975 -975
  11. package/templates/nextjs/base/src/app/products/[slug]/page.tsx +73 -76
  12. package/templates/nextjs/base/src/app/products/[slug]/product-client-section.tsx +529 -501
  13. package/templates/nextjs/base/src/app/products/page.tsx +475 -482
  14. package/templates/nextjs/base/src/app/reset-password/page.tsx +138 -138
  15. package/templates/nextjs/base/src/components/auth/register-form.tsx +245 -245
  16. package/templates/nextjs/base/src/components/checkout/checkout-form.tsx +416 -416
  17. package/templates/nextjs/base/src/components/checkout/payment-step.tsx +656 -656
  18. package/templates/nextjs/base/src/components/seo/product-json-ld.tsx +88 -88
  19. package/templates/nextjs/base/src/lib/brainerce.ts.ejs +6 -2
  20. package/templates/nextjs/base/src/lib/csrf.ts +11 -11
  21. package/templates/nextjs/base/src/lib/nonce.ts +10 -10
  22. package/templates/nextjs/base/src/lib/safe-redirect.ts +45 -45
  23. package/templates/nextjs/base/src/lib/sanitize-html.ts +93 -93
  24. package/templates/nextjs/base/src/lib/validation.ts +37 -37
@@ -1,138 +1,138 @@
1
- 'use client';
2
-
3
- import { useState } from 'react';
4
- import { useRouter, Link } from '@/lib/navigation';
5
- import { proxyResetPassword } from '@/lib/auth';
6
- import { LoadingSpinner } from '@/components/shared/loading-spinner';
7
- import { useTranslations } from '@/lib/translations';
8
- import { getPasswordError } from '@/lib/validation';
9
-
10
- export default function ResetPasswordPage() {
11
- const router = useRouter();
12
- const t = useTranslations('auth');
13
-
14
- const [newPassword, setNewPassword] = useState('');
15
- const [confirmPassword, setConfirmPassword] = useState('');
16
- const [loading, setLoading] = useState(false);
17
- const [error, setError] = useState<string | null>(null);
18
- const [success, setSuccess] = useState(false);
19
-
20
- async function handleSubmit(e: React.FormEvent) {
21
- e.preventDefault();
22
- if (loading) return;
23
-
24
- const pwCode = getPasswordError(newPassword);
25
- if (pwCode) {
26
- setError(t(pwCode));
27
- return;
28
- }
29
-
30
- if (newPassword !== confirmPassword) {
31
- setError(t('passwordsMustMatch'));
32
- return;
33
- }
34
-
35
- try {
36
- setLoading(true);
37
- setError(null);
38
- await proxyResetPassword(newPassword);
39
- setSuccess(true);
40
- setTimeout(() => router.push('/login'), 2000);
41
- } catch (err) {
42
- const message =
43
- err instanceof Error ? err.message : 'Something went wrong. Please try again.';
44
- setError(message);
45
- } finally {
46
- setLoading(false);
47
- }
48
- }
49
-
50
- return (
51
- <div className="flex min-h-[60vh] items-center justify-center px-4 py-12">
52
- <div className="w-full max-w-md space-y-6">
53
- <div className="text-center">
54
- <h1 className="text-foreground text-2xl font-bold">{t('resetPasswordTitle')}</h1>
55
- <p className="text-muted-foreground mt-1 text-sm">{t('resetPasswordSubtitle')}</p>
56
- </div>
57
-
58
- {error && (
59
- <div className="bg-destructive/10 border-destructive/20 text-destructive space-y-2 rounded-lg border px-4 py-3 text-sm">
60
- <p>{error}</p>
61
- <Link
62
- href="/forgot-password"
63
- className="text-primary block font-medium hover:underline"
64
- >
65
- {t('sendResetLink')}
66
- </Link>
67
- </div>
68
- )}
69
-
70
- {success ? (
71
- <div className="rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-300">
72
- {t('passwordResetSuccess')}
73
- </div>
74
- ) : (
75
- <form onSubmit={handleSubmit} className="space-y-4">
76
- <div>
77
- <label
78
- htmlFor="new-password"
79
- className="text-foreground mb-1.5 block text-sm font-medium"
80
- >
81
- {t('newPassword')}
82
- </label>
83
- <input
84
- id="new-password"
85
- type="password"
86
- required
87
- minLength={8}
88
- value={newPassword}
89
- onChange={(e) => setNewPassword(e.target.value)}
90
- placeholder={t('newPasswordPlaceholder')}
91
- autoComplete="new-password"
92
- className="border-border bg-background text-foreground placeholder:text-muted-foreground focus:ring-primary/20 focus:border-primary h-10 w-full rounded border px-3 text-sm focus:outline-none focus:ring-2"
93
- />
94
- </div>
95
-
96
- <div>
97
- <label
98
- htmlFor="confirm-password"
99
- className="text-foreground mb-1.5 block text-sm font-medium"
100
- >
101
- {t('confirmPassword')}
102
- </label>
103
- <input
104
- id="confirm-password"
105
- type="password"
106
- required
107
- minLength={8}
108
- value={confirmPassword}
109
- onChange={(e) => setConfirmPassword(e.target.value)}
110
- placeholder={t('confirmPasswordPlaceholder')}
111
- autoComplete="new-password"
112
- className="border-border bg-background text-foreground placeholder:text-muted-foreground focus:ring-primary/20 focus:border-primary h-10 w-full rounded border px-3 text-sm focus:outline-none focus:ring-2"
113
- />
114
- </div>
115
-
116
- <button
117
- type="submit"
118
- disabled={loading}
119
- className="bg-primary text-primary-foreground flex h-10 w-full items-center justify-center gap-2 rounded text-sm font-medium transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
120
- >
121
- {loading ? (
122
- <>
123
- <LoadingSpinner
124
- size="sm"
125
- className="border-primary-foreground/30 border-t-primary-foreground"
126
- />
127
- {t('resettingPassword')}
128
- </>
129
- ) : (
130
- t('resetPassword')
131
- )}
132
- </button>
133
- </form>
134
- )}
135
- </div>
136
- </div>
137
- );
138
- }
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter, Link } from '@/lib/navigation';
5
+ import { proxyResetPassword } from '@/lib/auth';
6
+ import { LoadingSpinner } from '@/components/shared/loading-spinner';
7
+ import { useTranslations } from '@/lib/translations';
8
+ import { getPasswordError } from '@/lib/validation';
9
+
10
+ export default function ResetPasswordPage() {
11
+ const router = useRouter();
12
+ const t = useTranslations('auth');
13
+
14
+ const [newPassword, setNewPassword] = useState('');
15
+ const [confirmPassword, setConfirmPassword] = useState('');
16
+ const [loading, setLoading] = useState(false);
17
+ const [error, setError] = useState<string | null>(null);
18
+ const [success, setSuccess] = useState(false);
19
+
20
+ async function handleSubmit(e: React.FormEvent) {
21
+ e.preventDefault();
22
+ if (loading) return;
23
+
24
+ const pwCode = getPasswordError(newPassword);
25
+ if (pwCode) {
26
+ setError(t(pwCode));
27
+ return;
28
+ }
29
+
30
+ if (newPassword !== confirmPassword) {
31
+ setError(t('passwordsMustMatch'));
32
+ return;
33
+ }
34
+
35
+ try {
36
+ setLoading(true);
37
+ setError(null);
38
+ await proxyResetPassword(newPassword);
39
+ setSuccess(true);
40
+ setTimeout(() => router.push('/login'), 2000);
41
+ } catch (err) {
42
+ const message =
43
+ err instanceof Error ? err.message : 'Something went wrong. Please try again.';
44
+ setError(message);
45
+ } finally {
46
+ setLoading(false);
47
+ }
48
+ }
49
+
50
+ return (
51
+ <div className="flex min-h-[60vh] items-center justify-center px-4 py-12">
52
+ <div className="w-full max-w-md space-y-6">
53
+ <div className="text-center">
54
+ <h1 className="text-foreground text-2xl font-bold">{t('resetPasswordTitle')}</h1>
55
+ <p className="text-muted-foreground mt-1 text-sm">{t('resetPasswordSubtitle')}</p>
56
+ </div>
57
+
58
+ {error && (
59
+ <div className="bg-destructive/10 border-destructive/20 text-destructive space-y-2 rounded-lg border px-4 py-3 text-sm">
60
+ <p>{error}</p>
61
+ <Link
62
+ href="/forgot-password"
63
+ className="text-primary block font-medium hover:underline"
64
+ >
65
+ {t('sendResetLink')}
66
+ </Link>
67
+ </div>
68
+ )}
69
+
70
+ {success ? (
71
+ <div className="rounded-lg border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-300">
72
+ {t('passwordResetSuccess')}
73
+ </div>
74
+ ) : (
75
+ <form onSubmit={handleSubmit} className="space-y-4">
76
+ <div>
77
+ <label
78
+ htmlFor="new-password"
79
+ className="text-foreground mb-1.5 block text-sm font-medium"
80
+ >
81
+ {t('newPassword')}
82
+ </label>
83
+ <input
84
+ id="new-password"
85
+ type="password"
86
+ required
87
+ minLength={8}
88
+ value={newPassword}
89
+ onChange={(e) => setNewPassword(e.target.value)}
90
+ placeholder={t('newPasswordPlaceholder')}
91
+ autoComplete="new-password"
92
+ className="border-border bg-background text-foreground placeholder:text-muted-foreground focus:ring-primary/20 focus:border-primary h-10 w-full rounded border px-3 text-sm focus:outline-none focus:ring-2"
93
+ />
94
+ </div>
95
+
96
+ <div>
97
+ <label
98
+ htmlFor="confirm-password"
99
+ className="text-foreground mb-1.5 block text-sm font-medium"
100
+ >
101
+ {t('confirmPassword')}
102
+ </label>
103
+ <input
104
+ id="confirm-password"
105
+ type="password"
106
+ required
107
+ minLength={8}
108
+ value={confirmPassword}
109
+ onChange={(e) => setConfirmPassword(e.target.value)}
110
+ placeholder={t('confirmPasswordPlaceholder')}
111
+ autoComplete="new-password"
112
+ className="border-border bg-background text-foreground placeholder:text-muted-foreground focus:ring-primary/20 focus:border-primary h-10 w-full rounded border px-3 text-sm focus:outline-none focus:ring-2"
113
+ />
114
+ </div>
115
+
116
+ <button
117
+ type="submit"
118
+ disabled={loading}
119
+ className="bg-primary text-primary-foreground flex h-10 w-full items-center justify-center gap-2 rounded text-sm font-medium transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
120
+ >
121
+ {loading ? (
122
+ <>
123
+ <LoadingSpinner
124
+ size="sm"
125
+ className="border-primary-foreground/30 border-t-primary-foreground"
126
+ />
127
+ {t('resettingPassword')}
128
+ </>
129
+ ) : (
130
+ t('resetPassword')
131
+ )}
132
+ </button>
133
+ </form>
134
+ )}
135
+ </div>
136
+ </div>
137
+ );
138
+ }