@pradip1995/commerce-auth 1.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/README.md +9 -0
- package/package.json +54 -0
- package/src/components/forgot-password/index.tsx +179 -0
- package/src/components/login/index.tsx +606 -0
- package/src/components/register/index.tsx +581 -0
- package/src/data/customer-registration.ts +365 -0
- package/src/data/customer.ts +638 -0
- package/src/data/guest.ts +357 -0
- package/src/index.ts +12 -0
- package/src/templates/login-template.tsx +44 -0
- package/src/types/account.ts +21 -0
- package/src/util-google-oauth.ts +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# @pradip1995/commerce-auth
|
|
2
|
+
|
|
3
|
+
Authentication UI and server actions: login, register, OTP, Google OAuth.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Login, LOGIN_VIEW } from "@pradip1995/commerce-auth"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Consuming apps must provide `@modules/*` shims for icons and modals, or replace theme slots to wrap headless exports.
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pradip1995/commerce-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Medusa storefront authentication — login, register, OTP, Google OAuth",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public",
|
|
8
|
+
"registry": "https://registry.npmjs.org/"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/SmartByteLabs/medusa-storefront-kit.git",
|
|
13
|
+
"directory": "packages/commerce-auth"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"files": [
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.ts",
|
|
21
|
+
"./components/login": "./src/components/login/index.tsx",
|
|
22
|
+
"./components/register": "./src/components/register/index.tsx",
|
|
23
|
+
"./components/forgot-password": "./src/components/forgot-password/index.tsx",
|
|
24
|
+
"./templates/login-template": "./src/templates/login-template.tsx",
|
|
25
|
+
"./types/account": "./src/types/account.ts",
|
|
26
|
+
"./data/customer": "./src/data/customer.ts",
|
|
27
|
+
"./data/customer-registration": "./src/data/customer-registration.ts",
|
|
28
|
+
"./data/guest": "./src/data/guest.ts"
|
|
29
|
+
},
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@medusajs/ui": ">=4",
|
|
32
|
+
"next": ">=15",
|
|
33
|
+
"react": ">=19",
|
|
34
|
+
"react-dom": ">=19",
|
|
35
|
+
"@pradip1995/commerce-core": "1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@medusajs/types": "latest",
|
|
39
|
+
"@medusajs/ui": "latest",
|
|
40
|
+
"@types/react": "^19",
|
|
41
|
+
"@types/react-dom": "^19",
|
|
42
|
+
"eslint": "^8.57.0",
|
|
43
|
+
"next": "15.3.8",
|
|
44
|
+
"react": "19.0.3",
|
|
45
|
+
"react-dom": "19.0.3",
|
|
46
|
+
"typescript": "^5.7.2",
|
|
47
|
+
"@pradip1995/commerce-core": "1.0.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"typecheck:full": "tsc --noEmit -p tsconfig.full.json",
|
|
52
|
+
"lint": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState } from "react"
|
|
4
|
+
import { LOGIN_VIEW } from "@core/types/account"
|
|
5
|
+
import ErrorMessage from "@modules/checkout/components/error-message"
|
|
6
|
+
import Envelope from "@modules/common/icons/envelope"
|
|
7
|
+
import ArrowLeft from "@modules/common/icons/arrow-left"
|
|
8
|
+
import {
|
|
9
|
+
requestPasswordReset,
|
|
10
|
+
checkEmailRegistered,
|
|
11
|
+
} from "@core/data/customer-registration"
|
|
12
|
+
|
|
13
|
+
type Props = {
|
|
14
|
+
setCurrentView: (view: LOGIN_VIEW) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const ForgotPassword = ({ setCurrentView }: Props) => {
|
|
18
|
+
const [email, setEmail] = useState("")
|
|
19
|
+
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
20
|
+
const [error, setError] = useState<string | null>(null)
|
|
21
|
+
const [success, setSuccess] = useState(false)
|
|
22
|
+
|
|
23
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
24
|
+
e.preventDefault()
|
|
25
|
+
if (!email) return
|
|
26
|
+
|
|
27
|
+
setIsSubmitting(true)
|
|
28
|
+
setError(null)
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// First check if the email is registered
|
|
32
|
+
const checkResult = await checkEmailRegistered(email)
|
|
33
|
+
if (!checkResult.exists) {
|
|
34
|
+
setError(
|
|
35
|
+
"This email address is not registered with us. Please sign up to create a new account."
|
|
36
|
+
)
|
|
37
|
+
setIsSubmitting(false)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const result = await requestPasswordReset(email)
|
|
42
|
+
|
|
43
|
+
if (result.success) {
|
|
44
|
+
setSuccess(true)
|
|
45
|
+
} else {
|
|
46
|
+
setError(result.error || "Failed to send reset link")
|
|
47
|
+
}
|
|
48
|
+
} catch (err: any) {
|
|
49
|
+
setError(err.message || "An error occurred")
|
|
50
|
+
} finally {
|
|
51
|
+
setIsSubmitting(false)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (success) {
|
|
56
|
+
return (
|
|
57
|
+
<div className="w-full flex flex-col" data-testid="forgot-password-success">
|
|
58
|
+
{/* Success Icon */}
|
|
59
|
+
<div className="flex justify-center mb-4">
|
|
60
|
+
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
|
61
|
+
<svg
|
|
62
|
+
className="w-8 h-8 text-green-500"
|
|
63
|
+
fill="none"
|
|
64
|
+
stroke="currentColor"
|
|
65
|
+
viewBox="0 0 24 24"
|
|
66
|
+
>
|
|
67
|
+
<path
|
|
68
|
+
strokeLinecap="round"
|
|
69
|
+
strokeLinejoin="round"
|
|
70
|
+
strokeWidth={2}
|
|
71
|
+
d="M5 13l4 4L19 7"
|
|
72
|
+
/>
|
|
73
|
+
</svg>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<h1 className="text-xl min-[340px]:text-2xl min-[550px]:text-3xl font-bold text-heading mb-2 text-center">
|
|
78
|
+
Check Your Email
|
|
79
|
+
</h1>
|
|
80
|
+
<p className="text-sm min-[340px]:text-base text-gray-500 mb-6 text-center">
|
|
81
|
+
We've sent a password reset link to <strong>{email}</strong>. Please check
|
|
82
|
+
your email and click the link to reset your password.
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<button
|
|
86
|
+
onClick={() => setCurrentView(LOGIN_VIEW.SIGN_IN)}
|
|
87
|
+
className="w-full py-2.5 min-[340px]:py-3 px-3 min-[340px]:px-4 bg-brand-accent text-sm min-[340px]:text-base text-inverse font-bold hover:bg-brand-accent-hover transition-colors"
|
|
88
|
+
style={{ borderRadius: "30px" }}
|
|
89
|
+
>
|
|
90
|
+
Back to Login
|
|
91
|
+
</button>
|
|
92
|
+
|
|
93
|
+
<p className="text-xs text-gray-500 mt-4 text-center">
|
|
94
|
+
Didn't receive the email?{" "}
|
|
95
|
+
<button
|
|
96
|
+
onClick={() => setSuccess(false)}
|
|
97
|
+
className="text-brand-accent font-semibold hover:underline"
|
|
98
|
+
>
|
|
99
|
+
Try again
|
|
100
|
+
</button>
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="w-full flex flex-col" data-testid="forgot-password-page">
|
|
108
|
+
{/* Back Button */}
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
onClick={() => setCurrentView(LOGIN_VIEW.SIGN_IN)}
|
|
112
|
+
className="flex items-center gap-2 text-gray-600 hover:text-heading mb-4"
|
|
113
|
+
>
|
|
114
|
+
<ArrowLeft size={20} />
|
|
115
|
+
<span className="text-sm">Back to Login</span>
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
{/* Title */}
|
|
119
|
+
<h1 className="text-xl min-[340px]:text-2xl min-[550px]:text-3xl font-bold text-heading mb-1 min-[340px]:mb-2">
|
|
120
|
+
Forgot Password
|
|
121
|
+
</h1>
|
|
122
|
+
<p className="text-sm min-[340px]:text-base text-gray-500 mb-4 min-[340px]:mb-5 min-[550px]:mb-6">
|
|
123
|
+
Enter your email address and we'll send you a link to reset your password.
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
<form className="w-full" onSubmit={handleSubmit}>
|
|
127
|
+
{/* Email Input */}
|
|
128
|
+
<div className="mb-4 min-[340px]:mb-5 min-[550px]:mb-6">
|
|
129
|
+
<div className="relative">
|
|
130
|
+
<div className="absolute inset-y-0 left-0 pl-3 min-[340px]:pl-4 flex items-center pointer-events-none">
|
|
131
|
+
<Envelope size="20" color="#9CA3AF" />
|
|
132
|
+
</div>
|
|
133
|
+
<input
|
|
134
|
+
type="email"
|
|
135
|
+
name="email"
|
|
136
|
+
placeholder="Email"
|
|
137
|
+
autoComplete="email"
|
|
138
|
+
required
|
|
139
|
+
value={email}
|
|
140
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
141
|
+
className="w-full pl-10 min-[340px]:pl-12 pr-3 min-[340px]:pr-4 py-2 min-[340px]:py-2.5 min-[550px]:py-3 text-sm min-[340px]:text-base border border-gray-300 focus:outline-none focus:ring-2 focus:ring-brand-accent focus:border-transparent"
|
|
142
|
+
style={{ borderRadius: "30px" }}
|
|
143
|
+
data-testid="forgot-password-email-input"
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{/* Error Message */}
|
|
149
|
+
<ErrorMessage error={error} data-testid="forgot-password-error-message" />
|
|
150
|
+
|
|
151
|
+
{/* Submit Button */}
|
|
152
|
+
<button
|
|
153
|
+
type="submit"
|
|
154
|
+
disabled={isSubmitting || !email}
|
|
155
|
+
className="w-full py-2.5 min-[340px]:py-3 px-3 min-[340px]:px-4 bg-brand-accent text-sm min-[340px]:text-base text-inverse font-bold hover:bg-brand-accent-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
156
|
+
style={{ borderRadius: "30px" }}
|
|
157
|
+
data-testid="send-reset-link-button"
|
|
158
|
+
>
|
|
159
|
+
{isSubmitting ? "Sending..." : "Send Reset Link"}
|
|
160
|
+
</button>
|
|
161
|
+
</form>
|
|
162
|
+
|
|
163
|
+
{/* Login Link */}
|
|
164
|
+
<div className="text-center mt-6">
|
|
165
|
+
<span className="text-gray-700 text-xs min-[340px]:text-sm">
|
|
166
|
+
Remember your password?{" "}
|
|
167
|
+
<button
|
|
168
|
+
onClick={() => setCurrentView(LOGIN_VIEW.SIGN_IN)}
|
|
169
|
+
className="text-brand-accent font-semibold hover:underline"
|
|
170
|
+
>
|
|
171
|
+
Login
|
|
172
|
+
</button>
|
|
173
|
+
</span>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export default ForgotPassword
|