feat: remove backend link and support passwordless login and registration
This commit is contained in:
parent
b3557d0698
commit
2a8f575e1c
6 changed files with 59 additions and 37 deletions
|
|
@ -267,5 +267,34 @@ describe('Supplier Portal Onboarding & Active Dashboard Tests', () => {
|
|||
|
||||
globalThis.fetch = originalFetch
|
||||
})
|
||||
|
||||
it('allows login directly without entering any password', async () => {
|
||||
render(<App />)
|
||||
|
||||
const loginBtn = screen.getByText(/^Login$/i)
|
||||
fireEvent.click(loginBtn)
|
||||
|
||||
const submitBtn = screen.getByRole('button', { name: /Sign In/i })
|
||||
fireEvent.click(submitBtn)
|
||||
|
||||
expect(await screen.findByText(/Total Sales/i)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('allows signup without entering any password', async () => {
|
||||
render(<App />)
|
||||
|
||||
const getStartedBtn = screen.getByText(/^Get started$/i)
|
||||
fireEvent.click(getStartedBtn)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText(/Enter your email/i), { target: { value: 'artisan_nopass@tradhox.com' } })
|
||||
fireEvent.change(screen.getByPlaceholderText(/9876543210/i), { target: { value: '9876543210' } })
|
||||
fireEvent.click(screen.getByLabelText(/I agree to the/i))
|
||||
|
||||
const alertMock = vi.spyOn(window, 'alert').mockImplementation(() => {})
|
||||
fireEvent.click(screen.getByRole('button', { name: /Create Account/i }))
|
||||
|
||||
expect(await screen.findByText(/Welcome to Tradhox/i)).toBeInTheDocument()
|
||||
alertMock.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -150,10 +150,9 @@ export default function AuthForms() {
|
|||
<input
|
||||
id="r-pass"
|
||||
type={showSignupPass ? 'text' : 'password'}
|
||||
placeholder="Create a password"
|
||||
placeholder="Create a password (optional)"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
required
|
||||
style={{ width: '100%', border: '1px solid #D1CAC7', borderRadius: '8px', height: '42px', padding: '0 40px 0 14px', fontSize: '0.9rem', outline: 'none' }}
|
||||
/>
|
||||
<button
|
||||
|
|
@ -174,10 +173,9 @@ export default function AuthForms() {
|
|||
<input
|
||||
id="r-confirm"
|
||||
type="password"
|
||||
placeholder="Repeat your password"
|
||||
placeholder="Repeat your password (optional)"
|
||||
value={confirmPassword}
|
||||
onChange={e => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
style={{ width: '100%', border: '1px solid #D1CAC7', borderRadius: '8px', height: '42px', padding: '0 14px', fontSize: '0.9rem', outline: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -211,7 +211,6 @@ export default function LoginPage() {
|
|||
placeholder="you@example.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
style={{ borderColor: '#D1CAC7', borderRadius: '8px', height: '42px', fontSize: '0.9rem' }}
|
||||
/>
|
||||
<span className="icon is-left has-text-grey">
|
||||
|
|
@ -237,7 +236,6 @@ export default function LoginPage() {
|
|||
placeholder="••••••••"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
style={{ borderColor: '#D1CAC7', borderRadius: '8px', height: '42px', fontSize: '0.9rem' }}
|
||||
/>
|
||||
<span className="icon is-left has-text-grey">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
const getApiBaseUrl = () => {
|
||||
if (import.meta.env.DEV) {
|
||||
return 'http://127.0.0.1:8000'; // Or your local backend port
|
||||
}
|
||||
// Use relative path in production.
|
||||
// CloudFront will proxy /api/* requests to the EC2 backend automatically!
|
||||
return '';
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -479,26 +479,22 @@ export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children
|
|||
const handleRegisterSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
|
||||
// Email Validation
|
||||
// Email Validation (if provided)
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
if (email.trim() && !emailRegex.test(email)) {
|
||||
alert('Please enter a valid email address.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Phone Validation
|
||||
// Phone Validation (if provided)
|
||||
const phoneRegex = /^[6-9]\d{9}$/;
|
||||
if (!phoneRegex.test(phone)) {
|
||||
if (phone.trim() && !phoneRegex.test(phone)) {
|
||||
alert('Please enter a valid 10-digit mobile number starting with 6, 7, 8, or 9.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Password Validation
|
||||
if (password.length < 8) {
|
||||
alert('Password must be at least 8 characters long.');
|
||||
return;
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
// Password Validation: If password is provided and confirm password doesn't match
|
||||
if (password && confirmPassword && password !== confirmPassword) {
|
||||
alert('Passwords do not match.');
|
||||
return;
|
||||
}
|
||||
|
|
@ -509,15 +505,26 @@ export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children
|
|||
return;
|
||||
}
|
||||
|
||||
const completeRegistration = () => {
|
||||
localStorage.setItem('access_token', 'demo_access_token');
|
||||
navigateTo('profile-completion');
|
||||
};
|
||||
|
||||
// If no password is provided, proceed directly to onboarding
|
||||
if (!password) {
|
||||
completeRegistration();
|
||||
return;
|
||||
}
|
||||
|
||||
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/register/`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
username: email,
|
||||
email: email,
|
||||
phone: phone,
|
||||
username: email || 'demo_artisan@tradhox.com',
|
||||
email: email || 'demo_artisan@tradhox.com',
|
||||
phone: phone || '9876543210',
|
||||
password: password,
|
||||
confirm_password: confirmPassword,
|
||||
confirm_password: confirmPassword || password,
|
||||
policy_accepted: policyAccepted
|
||||
})
|
||||
})
|
||||
|
|
@ -530,7 +537,7 @@ export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children
|
|||
apiFetch(`${CONFIG.apiBaseUrl}/api/auth/login/`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: email, password: password })
|
||||
body: JSON.stringify({ username: email || 'demo_artisan@tradhox.com', password: password })
|
||||
})
|
||||
.then(res => {
|
||||
if (!res.ok) throw new Error('Auto-login failed.');
|
||||
|
|
@ -541,11 +548,13 @@ export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children
|
|||
if (data.refresh_token) localStorage.setItem('refresh_token', data.refresh_token);
|
||||
navigateTo('profile-completion')
|
||||
})
|
||||
.catch(() => {
|
||||
completeRegistration();
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// Offline / demo fallback
|
||||
localStorage.setItem('access_token', 'demo_access_token');
|
||||
navigateTo('profile-completion');
|
||||
completeRegistration();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -562,7 +571,8 @@ export const SellerProvider: React.FC<{children: React.ReactNode}> = ({ children
|
|||
navigateTo('dashboard', true);
|
||||
};
|
||||
|
||||
if (!email.trim()) {
|
||||
// Allow login without password directly
|
||||
if (!password) {
|
||||
fallbackDemoLogin();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,4 @@ export default defineConfig({
|
|||
plugins: [
|
||||
react(),
|
||||
],
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue