feat: implement responsive auth card layout, password reset page, and update navigation and footer styles
All checks were successful
Beta CI/CD Pipeline / build-and-test (push) Successful in 1m21s
Beta CI/CD Pipeline / deploy-beta (push) Successful in 41s

This commit is contained in:
vickytechkey 2026-09-17 19:20:33 +05:30
parent d23ae61c57
commit 208eede1aa
11 changed files with 1039 additions and 157 deletions

BIN
card_inputs_enhanced.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
card_middle_debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
card_step1_debug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View file

@ -12,6 +12,7 @@ import { HowItWorksPage } from './pages/HowItWorksPage';
import { PricingPage } from './pages/PricingPage';
import { GrowBusinessPage } from './pages/GrowBusinessPage';
import { LoginPage } from './pages/LoginPage';
import { ResetPasswordPage } from './pages/ResetPasswordPage';
import { OnboardingPage } from './pages/OnboardingPage';
// Authenticated Seller Central Pages
@ -36,7 +37,7 @@ export const App: React.FC = () => {
<Route path="/pricing" element={<PricingPage />} />
<Route path="/why-sell" element={<GrowBusinessPage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/reset-password" element={<LoginPage initialMode="reset-password" />} />
<Route path="/reset-password" element={<ResetPasswordPage />} />
<Route path="/onboarding" element={<OnboardingPage />} />
</Route>

View file

@ -3,79 +3,79 @@ import { Link } from 'react-router-dom';
export const Footer: React.FC = () => {
return (
<footer className="footer pt-6 pb-6" style={{ backgroundColor: '#791B2C', color: '#ffffff' }}>
<footer className="footer pt-6 pb-6" style={{ backgroundColor: '#671B26', color: '#ffffff' }}>
<div className="container is-widescreen px-4">
<div className="columns is-multiline pb-6" style={{ borderBottom: '1px solid rgba(255, 255, 255, 0.2)' }}>
<div className="columns is-multiline pb-5">
{/* Brand Info */}
<div className="column is-6-desktop is-12-tablet mb-4">
<div className="is-flex is-align-items-center mb-3">
<span className="has-text-weight-black is-size-4 has-text-white" style={{ letterSpacing: '0.05em' }}>
<span className="has-text-weight-black is-size-4 has-text-white" style={{ letterSpacing: '0.02em' }}>
TRADHOX
</span>
</div>
<p className="is-size-7 font-weight-bold has-text-weight-bold has-text-white-ter mb-2" style={{ letterSpacing: '0.12em', textTransform: 'uppercase' }}>
FROM MAKERS TO THE WORLD.
<p className="is-size-7 font-weight-bold has-text-weight-bold mb-2" style={{ letterSpacing: '0.12em', textTransform: 'uppercase', color: '#E2BDC4' }}>
FROM MAKERS TO THE WORLD
</p>
<p className="is-size-6" style={{ color: '#EED5D9', maxWidth: '380px', lineHeight: 1.6 }}>
Empowering verified Indian craftsmanship and creative makers with seamless nationwide commerce, transparent payouts, and cultural storytelling.
<p className="is-size-7" style={{ color: '#E5C3C8', maxWidth: '380px', lineHeight: 1.6 }}>
The sovereign digital trade bridge linking India's verified hereditary master artisans directly to ethical world markets.
</p>
</div>
{/* Company Links */}
<div className="column is-2-desktop is-4-tablet is-6-mobile">
<div className="column is-2-desktop is-4-tablet is-4-mobile">
<h4 className="is-size-7 has-text-weight-bold has-text-white mb-3" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>
COMPANY
</h4>
<ul className="is-size-6" style={{ lineHeight: 2.2 }}>
<ul className="is-size-7" style={{ lineHeight: 2.2 }}>
<li>
<Link to="/how-it-works" style={{ color: '#E5C3C8' }}>About</Link>
<Link to="/how-it-works" style={{ color: '#E5C3C8', textDecoration: 'none' }}>About</Link>
</li>
<li>
<Link to="/pricing" style={{ color: '#E5C3C8' }}>Contact</Link>
<Link to="/pricing" style={{ color: '#E5C3C8', textDecoration: 'none' }}>Contact</Link>
</li>
<li>
<Link to="/why-sell" style={{ color: '#E5C3C8' }}>Grow your business</Link>
<Link to="/why-sell" style={{ color: '#E5C3C8', textDecoration: 'none' }}>Grow your business</Link>
</li>
</ul>
</div>
{/* Policies Links */}
<div className="column is-2-desktop is-4-tablet is-6-mobile">
<div className="column is-2-desktop is-4-tablet is-4-mobile">
<h4 className="is-size-7 has-text-weight-bold has-text-white mb-3" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>
POLICIES
</h4>
<ul className="is-size-6" style={{ lineHeight: 2.2 }}>
<ul className="is-size-7" style={{ lineHeight: 2.2 }}>
<li>
<a href="#privacy" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8' }}>Privacy policy</a>
<a href="#privacy" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8', textDecoration: 'none' }}>Privacy policy</a>
</li>
<li>
<a href="#terms" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8' }}>Terms of service</a>
<a href="#terms" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8', textDecoration: 'none' }}>Terms of service</a>
</li>
<li>
<a href="#seller-agreement" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8' }}>Seller agreement</a>
<a href="#seller-agreement" onClick={(e) => e.preventDefault()} style={{ color: '#E5C3C8', textDecoration: 'none' }}>Seller agreement</a>
</li>
</ul>
</div>
{/* Social Handlers */}
<div className="column is-2-desktop is-4-tablet is-12-mobile">
{/* Social Handlers / Connect */}
<div className="column is-2-desktop is-4-tablet is-4-mobile">
<h4 className="is-size-7 has-text-weight-bold has-text-white mb-3" style={{ letterSpacing: '0.1em', textTransform: 'uppercase' }}>
SOCIAL HANDLERS
CONNECT
</h4>
<ul className="is-size-6" style={{ lineHeight: 2.2 }}>
<ul className="is-size-7" style={{ lineHeight: 2.2 }}>
<li>
<a href="https://instagram.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8' }}>
<i className="fa-brands fa-instagram mr-2"></i> Instagram
<a href="https://instagram.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8', textDecoration: 'none' }}>
Instagram
</a>
</li>
<li>
<a href="https://facebook.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8' }}>
<i className="fa-brands fa-facebook mr-2"></i> Facebook
<a href="https://facebook.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8', textDecoration: 'none' }}>
Facebook
</a>
</li>
<li>
<a href="https://youtube.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8' }}>
<i className="fa-brands fa-youtube mr-2"></i> YouTube
<a href="https://youtube.com" target="_blank" rel="noreferrer" style={{ color: '#E5C3C8', textDecoration: 'none' }}>
YouTube
</a>
</li>
</ul>

View file

@ -49,11 +49,14 @@ export const Navbar: React.FC = () => {
onClick={closeMenu}
style={{ textDecoration: 'none' }}
>
<span className="has-text-weight-bold is-size-4" style={{ color: 'var(--trad-maroon)', letterSpacing: '-0.03em' }}>
<span className="has-text-weight-bold is-size-4" style={{ color: 'var(--trad-maroon)', letterSpacing: '-0.02em', lineHeight: 1 }}>
TRADHOX
</span>
<span className="tag is-trad-wine is-rounded ml-2 is-size-7 is-hidden-touch is-hidden-desktop-only">
SELLER CENTRAL
<span className="is-hidden-touch is-align-items-center ml-2.5" style={{ display: 'inline-flex', gap: '0.45rem' }}>
<span style={{ color: 'var(--trad-maroon)', fontSize: '0.9rem', lineHeight: 1 }}>•</span>
<span style={{ color: '#4A5568', fontSize: '0.75rem', fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
SELLER CENTRAL
</span>
</span>
</Link>
@ -107,9 +110,19 @@ export const Navbar: React.FC = () => {
<Link
to="/onboarding"
className="button is-trad-primary px-4"
className={`button px-4 ${location.pathname === '/onboarding' ? 'is-outlined' : 'is-trad-primary'}`}
onClick={closeMenu}
style={{ whiteSpace: 'nowrap', borderRadius: '9999px', fontSize: '0.875rem', fontWeight: 600 }}
style={{
whiteSpace: 'nowrap',
borderRadius: '9999px',
fontSize: '0.875rem',
fontWeight: 600,
...(location.pathname === '/onboarding' ? {
backgroundColor: '#FFFFFF',
borderColor: 'var(--trad-maroon)',
color: 'var(--trad-maroon)'
} : {})
}}
>
<span>Become a seller</span>
</Link>

View file

@ -138,20 +138,12 @@ export const LoginPage: React.FC<LoginPageProps> = ({ initialMode }) => {
overflow: 'hidden'
}}
>
<div className="is-flex is-flex-direction-column is-flex-direction-row-desktop" style={{ width: '100%' }}>
<div className="auth-card-layout">
{/* ------------------------------------------------------------------ */}
{/* LEFT COLUMN: Artisan Story, Image & Guarantee Highlights */}
{/* ------------------------------------------------------------------ */}
<div
className="is-hidden-touch"
style={{
flex: '0 0 42%',
borderRight: '1px solid #EAE5DD',
backgroundColor: '#FAF9F6',
padding: '2.5rem'
}}
>
<div className="auth-card-left">
{mode === 'login' ? (
<div>
{/* Badge */}
@ -356,14 +348,7 @@ export const LoginPage: React.FC<LoginPageProps> = ({ initialMode }) => {
</div>
{/* ------------------------------------------------------------------ */}
<div
style={{
flex: '1 1 auto',
padding: 'clamp(1.25rem, 4vw, 3rem)',
minWidth: 0,
width: '100%'
}}
>
<div className="auth-card-right">
{/* ================================================================ */}
{/* VIEW A: LOGIN FORM */}

View file

@ -1,5 +1,5 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import React, { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { useSeller } from '../context/SellerContext';
import {
Check,
@ -31,15 +31,37 @@ export const OnboardingPage: React.FC = () => {
const navigate = useNavigate();
const stepLabels = [
'Account',
'GST & Tax',
'Pickup Address',
'Bank Account',
'Mobile',
'Business & GST',
'Pickup address',
'Bank details',
'Categories',
'Sample Craft',
'Store Profile'
'Products',
'Store profile'
];
// Step 1 Specific State matching stitch wireframe
const [step1Phone, setStep1Phone] = useState(onboarding.phone || '9845012345');
const [step1Otp, setStep1Otp] = useState<string[]>(['4', '8', '2', '9', '', '']);
const [termsAgreed, setTermsAgreed] = useState(true);
const [isPhoneFocused, setIsPhoneFocused] = useState(false);
const handleStep1OtpChange = (index: number, val: string) => {
const clean = val.replace(/[^0-9]/g, '').slice(-1);
const newOtp = [...step1Otp];
newOtp[index] = clean;
setStep1Otp(newOtp);
if (clean && index < 5) {
document.getElementById(`step1-otp-${index + 1}`)?.focus();
}
};
const handleStep1OtpKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Backspace' && !step1Otp[index] && index > 0) {
document.getElementById(`step1-otp-${index - 1}`)?.focus();
}
};
const craftCategoryOptions = [
'Handloom & Textiles',
'Handmade Pottery & Ceramics',
@ -340,22 +362,63 @@ export const OnboardingPage: React.FC = () => {
);
}
/* ----------------------------------------------------
Render Onboarding Wizard (Steps 1 to 7)
---------------------------------------------------- */
/* ----------------------------------------------------
Render Onboarding Wizard (Steps 1 to 7)
---------------------------------------------------- */
return (
<div className="section py-4 py-6-tablet">
<div className="section py-5 py-6-tablet" style={{ backgroundColor: '#FCF9F8', minHeight: 'calc(100vh - 80px)' }}>
<div className="container is-widescreen px-3 px-4-tablet">
{/* Wizard Header */}
<div className="has-text-centered mb-4 mb-5-tablet">
<span className="tag is-trad-wine is-rounded mb-2">Seller Registration Wizard</span>
<h1 className="title is-size-4-mobile is-size-3-tablet is-size-2-desktop mb-2">Setup Your Artisan Workshop</h1>
<p className="subtitle is-size-7-mobile is-size-6-tablet has-text-grey">Step {onboardingStep} of 7: {stepLabels[onboardingStep - 1]}</p>
{/* Page Hero Header matching stitch screen */}
<div className="mb-5 mb-6-tablet" style={{ maxWidth: '820px', margin: '0 auto 2.25rem auto' }}>
<h1
className="title mb-2"
style={{
fontSize: 'clamp(1.75rem, 3.5vw, 2.35rem)',
fontWeight: 800,
color: '#111827',
letterSpacing: '-0.025em',
lineHeight: 1.2
}}
>
Join India’s Dedicated Artisan Marketplace
</h1>
<p
className="subtitle mb-3"
style={{
color: '#4B5563',
fontSize: '0.92rem',
lineHeight: 1.55,
maxWidth: '650px'
}}
>
Connect your generational craft with conscious patrons across 15,000+ pin codes. Complete a simple 7–step verification to open your verified digital storefront.
</p>
<div
className="is-flex is-align-items-center is-flex-wrap-wrap"
style={{ gap: '0.45rem', color: '#9C381E', fontSize: '0.85rem', fontWeight: 600 }}
>
<span>no listing fee</span>
<span style={{ color: '#D1D5DB' }}>•</span>
<span>no registration fee</span>
<span style={{ color: '#D1D5DB' }}>•</span>
<span>get live in minutes</span>
</div>
</div>
{/* Responsive Step Progress Bar */}
<div className="card p-3 p-4-desktop mb-5" style={{ borderRadius: '16px', border: '1px solid var(--trad-border)' }}>
<div className="is-flex is-align-items-center is-justify-content-space-between px-1 px-3-desktop">
{/* Responsive 7-Step Stepper matching stitch screen */}
<div className="mb-5 mb-6-tablet" style={{ maxWidth: '820px', margin: '0 auto 2.75rem auto', width: '100%' }}>
<div
className="is-flex is-align-items-flex-start is-justify-content-space-between"
style={{
width: '100%',
overflowX: 'auto',
paddingBottom: '0.5rem'
}}
>
{stepLabels.map((lbl, idx) => {
const stepNum = idx + 1;
const isDone = stepNum < onboardingStep;
@ -363,47 +426,56 @@ export const OnboardingPage: React.FC = () => {
return (
<React.Fragment key={idx}>
{/* Step Node */}
<div
onClick={() => stepNum <= onboardingStep && setOnboardingStep(stepNum)}
className="is-flex is-align-items-center"
style={{ cursor: stepNum <= onboardingStep ? 'pointer' : 'default', flexShrink: 0 }}
title={`Step ${stepNum}: ${lbl}`}
className="is-flex is-flex-direction-column is-align-items-center"
style={{
cursor: stepNum <= onboardingStep ? 'pointer' : 'default',
flex: '0 0 auto',
width: '84px',
textAlign: 'center'
}}
>
<div
className="is-flex is-align-items-center is-justify-content-center"
style={{
width: '32px',
height: '32px',
width: '34px',
height: '34px',
borderRadius: '50%',
fontSize: '0.8rem',
fontWeight: 'bold',
backgroundColor: isActive ? 'var(--trad-maroon)' : isDone ? 'var(--trad-forest)' : '#ffffff',
color: isActive || isDone ? '#ffffff' : 'var(--trad-text-secondary)',
border: isActive ? '2px solid var(--trad-maroon)' : isDone ? '2px solid var(--trad-forest)' : '2px solid var(--trad-border)',
boxShadow: isActive ? '0 0 0 3px rgba(121, 27, 44, 0.18)' : 'none',
fontSize: '0.88rem',
fontWeight: 700,
backgroundColor: (isActive || isDone) ? '#1B2A4A' : '#FFFFFF',
color: (isActive || isDone) ? '#FFFFFF' : '#6B7280',
border: (isActive || isDone) ? '2px solid #1B2A4A' : '1.5px solid #D1D5DB',
boxShadow: isActive ? '0 0 0 3px rgba(27, 42, 74, 0.15)' : 'none',
transition: 'all 0.2s ease'
}}
>
{isDone ? <Check size={16} /> : stepNum}
{stepNum}
</div>
<span
className="is-size-7 is-hidden-touch ml-2"
className="mt-1.5"
style={{
fontWeight: isActive ? 700 : 500,
color: isActive ? 'var(--trad-maroon)' : isDone ? 'var(--trad-text-primary)' : 'var(--trad-text-secondary)',
whiteSpace: 'nowrap'
color: isActive ? '#111827' : '#6B7280',
lineHeight: 1.25,
fontSize: '0.76rem'
}}
>
{lbl}
</span>
</div>
{/* Connecting Line */}
{idx !== stepLabels.length - 1 && (
<div
className="is-flex-grow-1 mx-1 mx-3-desktop"
className="is-flex-grow-1"
style={{
height: '2px',
minWidth: '8px',
backgroundColor: isDone ? 'var(--trad-forest)' : 'var(--trad-border)',
height: '1.5px',
minWidth: '14px',
backgroundColor: isDone ? '#1B2A4A' : '#E5E7EB',
marginTop: '16px',
transition: 'background-color 0.25s ease'
}}
/>
@ -412,81 +484,193 @@ export const OnboardingPage: React.FC = () => {
);
})}
</div>
{/* Mobile Current Step Banner */}
<div className="is-hidden-desktop has-text-centered mt-2 pt-2" style={{ borderTop: '1px dashed var(--trad-border)' }}>
<span className="is-size-7 has-text-weight-bold" style={{ color: 'var(--trad-maroon)' }}>
Step {onboardingStep} of 7: {stepLabels[onboardingStep - 1]}
</span>
</div>
</div>
{/* Step Content Container */}
<div className="columns is-centered">
<div className="column is-8-desktop is-10-tablet is-12-mobile">
<div className="card p-4 p-5-tablet p-6-desktop" style={{ borderRadius: '20px', border: '1.5px solid var(--trad-border)' }}>
{/* =================================================================== */}
{/* STEP 1: Create Your Seller Account (Centered 480px Card) */}
{/* =================================================================== */}
{onboardingStep === 1 && (
<div className="mx-auto mb-6" style={{ maxWidth: '480px', width: '100%' }}>
<div
className="card p-5 p-6-tablet"
style={{
borderRadius: '24px',
border: '1px solid #E5E7EB',
backgroundColor: '#FFFFFF',
boxShadow: '0 8px 30px rgba(0, 0, 0, 0.04)'
}}
>
<div className="has-text-centered mb-5">
<h2
className="title mb-1.5"
style={{
fontSize: 'clamp(1.35rem, 3vw, 1.55rem)',
fontWeight: 800,
color: '#111827',
letterSpacing: '-0.02em'
}}
>
Create your seller account
</h2>
<p className="subtitle is-size-7 mb-0" style={{ color: '#6B7280' }}>
step 1 of 7 · takes about 2 minutes
</p>
</div>
{/* STEP 1: Account Creation */}
{onboardingStep === 1 && (
<div>
<h2 className="title is-size-4 mb-2">Step 1: Create Your Seller Account</h2>
<p className="subtitle is-size-7 has-text-grey mb-5">Basic contact information for order alerts and dashboard login</p>
<div className="field mb-4">
<label className="label is-size-7 has-text-grey">Master Artisan / Contact Person Full Name</label>
<div className="control">
<input
className="input"
type="text"
placeholder="e.g. Raghavendra Sharma"
value={onboarding.fullName}
onChange={(e) => updateOnboarding({ fullName: e.target.value })}
/>
</div>
</div>
<div className="columns mb-2">
<div className="column is-6">
<div className="field">
<label className="label is-size-7 has-text-grey">Official Email Address</label>
<div className="control">
<input
className="input"
type="email"
placeholder="artisan@guild.com"
value={onboarding.email}
onChange={(e) => updateOnboarding({ email: e.target.value })}
/>
</div>
</div>
</div>
<div className="column is-6">
<div className="field">
<label className="label is-size-7 has-text-grey">10-Digit Mobile Number</label>
<div className="field has-addons">
<p className="control"><span className="button is-static">+91</span></p>
<div className="control is-expanded">
<input
className="input"
type="tel"
maxLength={10}
value={onboarding.phone}
onChange={(e) => updateOnboarding({ phone: e.target.value })}
/>
</div>
</div>
</div>
</div>
</div>
<div className="notification is-success is-light p-3 is-size-7 mb-4">
<span className="is-flex is-align-items-center font-weight-bold">
<CheckCircle2 size={16} className="mr-2" />
Mobile number pre-verified via simulated OTP (4829)
</span>
<form onSubmit={(e) => { e.preventDefault(); handleNext(); }}>
{/* Phone Input */}
<div className="field mb-4">
<div
className="is-flex is-align-items-center"
style={{
border: isPhoneFocused ? '1.5px solid #1B2A4A' : '1.5px solid #CBD5E0',
borderRadius: '10px',
backgroundColor: '#FFFFFF',
height: '46px',
padding: '0 12px',
boxShadow: isPhoneFocused ? '0 0 0 3px rgba(27, 42, 74, 0.12)' : 'none',
transition: 'all 0.15s ease'
}}
>
<span style={{ fontWeight: 700, color: '#111827', fontSize: '0.88rem' }}>IN</span>
<span style={{ color: '#4B5563', fontSize: '0.88rem', marginLeft: '4px' }}>+91</span>
<div style={{ width: '1px', height: '20px', backgroundColor: '#E2E8F0', margin: '0 10px' }} />
<input
type="tel"
maxLength={10}
value={step1Phone}
onChange={(e) => {
setStep1Phone(e.target.value);
updateOnboarding({ phone: e.target.value });
}}
onFocus={() => setIsPhoneFocused(true)}
onBlur={() => setIsPhoneFocused(false)}
placeholder="9845012345"
style={{
border: 'none',
outline: 'none',
backgroundColor: 'transparent',
height: '100%',
fontSize: '0.9rem',
color: '#111827',
fontWeight: 600,
flex: 1,
minWidth: 0
}}
/>
</div>
</div>
)}
{/* OTP code prompt and 6 boxes */}
<div className="field mb-4">
<div className="mb-2">
<span style={{ color: '#4B5563', fontSize: '0.85rem' }}>
enter the 6–digit code sent to your phone
</span>
</div>
<div className="is-flex is-align-items-center is-justify-content-space-between mb-2" style={{ gap: '8px' }}>
{step1Otp.map((digit, idx) => (
<input
key={idx}
id={`step1-otp-${idx}`}
type="text"
inputMode="numeric"
maxLength={1}
value={digit}
placeholder="•"
onChange={(e) => handleStep1OtpChange(idx, e.target.value)}
onKeyDown={(e) => handleStep1OtpKeyDown(idx, e)}
className="has-text-centered has-text-weight-bold"
style={{
width: '46px',
height: '46px',
borderRadius: '10px',
border: '1.5px solid #CBD5E0',
fontSize: digit ? '1.15rem' : '1.4rem',
color: '#111827',
outline: 'none',
backgroundColor: '#FFFFFF',
flex: '1 1 0',
minWidth: '36px',
maxWidth: '48px',
padding: 0,
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
}}
onFocus={(e) => {
e.target.style.borderColor = '#1B2A4A';
e.target.style.boxShadow = '0 0 0 3px rgba(27, 42, 74, 0.12)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#CBD5E0';
e.target.style.boxShadow = 'none';
}}
/>
))}
</div>
</div>
{/* Terms & Policies Checkbox */}
<div className="field mb-5">
<label className="checkbox is-flex is-align-items-center" style={{ fontSize: '0.85rem', color: '#4B5563', cursor: 'pointer' }}>
<input
type="checkbox"
checked={termsAgreed}
onChange={(e) => setTermsAgreed(e.target.checked)}
className="mr-2.5"
style={{ accentColor: '#1B2A4A', width: '16px', height: '16px' }}
/>
<span>I agree to the seller terms &amp; policies</span>
</label>
</div>
{/* Continue Button */}
<button
type="submit"
disabled={!termsAgreed}
className="button is-fullwidth"
style={{
backgroundColor: '#1B2A4A',
color: '#FFFFFF',
borderRadius: '9999px',
height: '48px',
fontSize: '0.95rem',
fontWeight: 700,
border: 'none',
boxShadow: '0 4px 14px rgba(27, 42, 74, 0.25)',
transition: 'all 0.2s ease',
cursor: termsAgreed ? 'pointer' : 'not-allowed',
opacity: termsAgreed ? 1 : 0.6
}}
>
<span>Continue</span>
</button>
{/* Already registered? Log in */}
<div className="has-text-centered mt-4 pt-1">
<span style={{ color: '#4B5563', fontSize: '0.85rem' }}>Already registered? </span>
<Link to="/login" style={{ color: '#791B2C', fontWeight: 700, textDecoration: 'none' }}>Log in</Link>
</div>
</form>
</div>
</div>
)}
{/* =================================================================== */}
{/* STEPS 2 TO 7: Business, Pickup, Bank, Categories, Products, Profile */}
{/* =================================================================== */}
{onboardingStep > 1 && (
<div className="columns is-centered">
<div className="column is-8-desktop is-10-tablet is-12-mobile">
<div
className="card p-4 p-5-tablet p-6-desktop mb-6"
style={{
borderRadius: '24px',
border: '1px solid #E5E7EB',
backgroundColor: '#FFFFFF',
boxShadow: '0 8px 30px rgba(0, 0, 0, 0.04)'
}}
>
{/* STEP 2: Business & GST / Enrolment ID */}
{onboardingStep === 2 && (
@ -940,6 +1124,78 @@ export const OnboardingPage: React.FC = () => {
</div>
</div>
</div>
)}
{/* Trust Badges Strip matching stitch screen */}
<div className="mt-6 mb-4">
<div className="container" style={{ maxWidth: '720px' }}>
<div className="columns is-mobile has-text-centered">
<div className="column is-4">
<div className="is-flex is-flex-direction-column is-align-items-center">
<div
className="mb-2 is-flex is-align-items-center is-justify-content-center"
style={{
width: '42px',
height: '42px',
borderRadius: '50%',
backgroundColor: '#FFFFFF',
border: '1px solid #E5E7EB',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.03)'
}}
>
<Lock size={16} color="#4B5563" />
</div>
<span style={{ color: '#374151', fontSize: '0.82rem', fontWeight: 600 }}>
Secure &amp; encrypted
</span>
</div>
</div>
<div className="column is-4">
<div className="is-flex is-flex-direction-column is-align-items-center">
<div
className="mb-2 is-flex is-align-items-center is-justify-content-center"
style={{
width: '42px',
height: '42px',
borderRadius: '50%',
backgroundColor: '#FFFFFF',
border: '1px solid #E5E7EB',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.03)'
}}
>
<span style={{ fontSize: '1.1rem', fontWeight: 700, color: '#4B5563', lineHeight: 1 }}>₹</span>
</div>
<span style={{ color: '#374151', fontSize: '0.82rem', fontWeight: 600 }}>
No registration fee
</span>
</div>
</div>
<div className="column is-4">
<div className="is-flex is-flex-direction-column is-align-items-center">
<div
className="mb-2 is-flex is-align-items-center is-justify-content-center"
style={{
width: '42px',
height: '42px',
borderRadius: '50%',
backgroundColor: '#FFFFFF',
border: '1px solid #E5E7EB',
boxShadow: '0 2px 6px rgba(0, 0, 0, 0.03)'
}}
>
<Headphones size={16} color="#4B5563" />
</div>
<span style={{ color: '#374151', fontSize: '0.82rem', fontWeight: 600 }}>
Dedicated onboarding support
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);

View file

@ -0,0 +1,589 @@
import React, { useState, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useSeller } from '../context/SellerContext';
import {
Star,
Check,
ArrowRight,
ArrowLeft,
Smartphone,
Mail,
Eye,
EyeOff,
PhoneCall
} from 'lucide-react';
const SolidGreenCheck: React.FC<{ size?: number }> = ({ size = 16 }) => (
<svg width={size} height={size} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className="is-flex-shrink-0">
<circle cx="8" cy="8" r="8" fill="#1E8E3E" />
<path d="M4.8 8.2L7 10.4L11.4 5.8" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
export const ResetPasswordPage: React.FC = () => {
const navigate = useNavigate();
const { login } = useSeller();
// Reset tab: 'email' or 'mobile' (matches screenshot: default is 'email')
const [resetTab, setResetTab] = useState<'email' | 'mobile'>('email');
const [resetEmail, setResetEmail] = useState('artisan.weaver@craftguild.in');
const [resetMobile, setResetMobile] = useState('9845012345');
// OTP Digits matching screenshot: 7, 3, 9, 4, and empty for 5 and 6
const [otpDigits, setOtpDigits] = useState<string[]>(['7', '3', '9', '4', '', '']);
const [newPassword, setNewPassword] = useState('craftatelier2026');
const [confirmPassword, setConfirmPassword] = useState('craftatelier2026');
const [showNewPassword, setShowNewPassword] = useState(false);
const [resendTimer, setResendTimer] = useState(28);
const [isEmailFocused, setIsEmailFocused] = useState(false);
const [isNewPassFocused, setIsNewPassFocused] = useState(false);
const [isConfirmPassFocused, setIsConfirmPassFocused] = useState(false);
useEffect(() => {
if (resendTimer > 0) {
const timer = setTimeout(() => setResendTimer(resendTimer - 1), 1000);
return () => clearTimeout(timer);
}
}, [resendTimer]);
const handleOtpDigitChange = (index: number, value: string) => {
// Only accept numeric or clean char
const cleanValue = value.replace(/[^0-9]/g, '').slice(-1);
const newDigits = [...otpDigits];
newDigits[index] = cleanValue;
setOtpDigits(newDigits);
// Auto focus next input
if (cleanValue && index < 5) {
const nextInput = document.getElementById(`otp-digit-${index + 1}`);
nextInput?.focus();
}
};
const handleOtpKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Backspace') {
if (!otpDigits[index] && index > 0) {
const prevInput = document.getElementById(`otp-digit-${index - 1}`);
prevInput?.focus();
}
}
};
const handleResetSubmit = (e: React.FormEvent) => {
e.preventDefault();
login(resetEmail || 'artisan.weaver@craftguild.in');
navigate('/dashboard');
};
return (
<div className="section py-5 py-6-desktop" style={{ minHeight: 'calc(100vh - 80px)', display: 'flex', alignItems: 'center' }}>
<div className="container is-widescreen px-3 px-4-desktop">
<div className="columns is-centered">
<div className="column is-11-widescreen is-12-desktop is-9-tablet is-12-mobile">
{/* 2-Column Authentication Card */}
<div
className="card"
style={{
borderRadius: '24px',
border: '1px solid #EAE5DD',
boxShadow: '0 12px 40px rgba(0, 0, 0, 0.04)',
backgroundColor: '#FFFFFF',
overflow: 'hidden'
}}
>
<div className="auth-card-layout">
{/* ------------------------------------------------------------------ */}
{/* LEFT COLUMN: Artisan Security, Guarantee Highlights & Helpline */}
{/* ------------------------------------------------------------------ */}
<div className="auth-card-left">
<div>
{/* Badge */}
<div className="mb-3">
<span
className="is-inline-flex is-align-items-center px-3 py-1"
style={{
backgroundColor: '#FDF2F4',
border: '1px solid rgba(103, 27, 38, 0.15)',
borderRadius: '9999px',
color: 'var(--trad-maroon)',
fontSize: '0.72rem',
fontWeight: 700,
letterSpacing: '0.08em',
textTransform: 'uppercase'
}}
>
<Star size={11} className="mr-1.5" fill="var(--trad-maroon)" color="var(--trad-maroon)" />
<span>ARTISAN SECURITY</span>
</span>
</div>
{/* Headline */}
<h2
className="title mb-2"
style={{
fontSize: '1.6rem',
fontWeight: 800,
color: '#1A202C',
lineHeight: 1.3,
letterSpacing: '-0.02em'
}}
>
Protecting your craft atelier &amp; earnings.
</h2>
{/* Subtitle */}
<p className="subtitle mb-4 is-size-7" style={{ color: '#4A5568', lineHeight: 1.55 }}>
Ensure seamless recovery to your master workshop ledger, scheduled bank settlements, and global artisan trade orders.
</p>
{/* Artisan Image */}
<div className="mb-5">
<img
src="/images/login-potter.jpg"
alt="Authentic Indian Artisan Potter"
style={{
width: '100%',
height: '215px',
objectFit: 'cover',
borderRadius: '16px',
display: 'block',
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.06)'
}}
/>
</div>
{/* Checklist */}
<div className="is-flex is-flex-direction-column" style={{ gap: '0.85rem' }}>
<div className="is-flex is-align-items-center">
<div
className="mr-2.5 is-flex is-align-items-center is-justify-content-center is-flex-shrink-0"
style={{ width: '20px', height: '20px', borderRadius: '50%', backgroundColor: '#E6F4EA' }}
>
<Check size={13} color="#137333" strokeWidth={3} />
</div>
<span className="is-size-7" style={{ color: '#2D3748', fontWeight: 500 }}>
Quick OTP recovery via registered email or mobile SIM
</span>
</div>
<div className="is-flex is-align-items-center">
<div
className="mr-2.5 is-flex is-align-items-center is-justify-content-center is-flex-shrink-0"
style={{ width: '20px', height: '20px', borderRadius: '50%', backgroundColor: '#E6F4EA' }}
>
<Check size={13} color="#137333" strokeWidth={3} />
</div>
<span className="is-size-7" style={{ color: '#2D3748', fontWeight: 500 }}>
Zero interruption to your ongoing orders and payouts
</span>
</div>
<div className="is-flex is-align-items-center">
<div
className="mr-2.5 is-flex is-align-items-center is-justify-content-center is-flex-shrink-0"
style={{ width: '20px', height: '20px', borderRadius: '50%', backgroundColor: '#E6F4EA' }}
>
<Check size={13} color="#137333" strokeWidth={3} />
</div>
<span className="is-size-7" style={{ color: '#2D3748', fontWeight: 500 }}>
Dedicated 24/7 maker helpline in 7 regional languages
</span>
</div>
</div>
{/* Toll Free Callout */}
<div
className="mt-5 pt-4 is-flex is-align-items-center is-size-7"
style={{ borderTop: '1px solid #EAE5DD', color: '#4A5568' }}
>
<PhoneCall size={15} className="mr-2 has-text-grey" />
<span>
Kala Setu Support:{' '}
<strong style={{ color: '#1A202C' }}>1800–419–8723</strong>{' '}
<span className="has-text-grey">(Toll Free)</span>
</span>
</div>
</div>
</div>
{/* ------------------------------------------------------------------ */}
{/* RIGHT COLUMN: Reset Password Form */}
{/* ------------------------------------------------------------------ */}
<div className="auth-card-right">
<div>
{/* Back to login button */}
<div className="mb-3">
<Link
to="/login"
className="is-inline-flex is-align-items-center"
style={{
color: 'var(--trad-maroon)',
fontSize: '0.85rem',
fontWeight: 600,
textDecoration: 'none'
}}
>
<ArrowLeft size={16} className="mr-1.5" />
<span>Back to login</span>
</Link>
</div>
<h1
className="title mb-1.5"
style={{
fontSize: 'clamp(1.45rem, 4vw, 1.85rem)',
fontWeight: 800,
color: '#1A202C',
letterSpacing: '-0.02em',
lineHeight: 1.25
}}
>
Reset your password
</h1>
<p className="subtitle is-size-7 mb-4" style={{ color: '#718096', lineHeight: 1.5 }}>
Enter your registered email address to receive a verification code and create a new password.
</p>
{/* Segmented Toggle Tabs */}
<div
className="mb-5 p-1 is-flex"
style={{
backgroundColor: '#F1F3F5',
borderRadius: '9999px',
width: '100%',
maxWidth: '280px'
}}
>
<button
type="button"
onClick={() => setResetTab('mobile')}
className="button is-small is-flex-grow-1"
style={{
backgroundColor: resetTab === 'mobile' ? '#FFFFFF' : 'transparent',
color: resetTab === 'mobile' ? '#1A202C' : '#718096',
fontWeight: resetTab === 'mobile' ? 700 : 500,
border: 'none',
borderRadius: '9999px',
boxShadow: resetTab === 'mobile' ? '0 2px 6px rgba(0, 0, 0, 0.08)' : 'none',
height: '34px',
fontSize: '0.8rem',
transition: 'all 0.15s ease'
}}
>
<span>Mobile Number</span>
</button>
<button
type="button"
onClick={() => setResetTab('email')}
className="button is-small is-flex-grow-1"
style={{
backgroundColor: resetTab === 'email' ? '#FFFFFF' : 'transparent',
color: resetTab === 'email' ? '#1A202C' : '#718096',
fontWeight: resetTab === 'email' ? 700 : 500,
border: 'none',
borderRadius: '9999px',
boxShadow: resetTab === 'email' ? '0 2px 6px rgba(0, 0, 0, 0.08)' : 'none',
height: '34px',
fontSize: '0.8rem',
transition: 'all 0.15s ease'
}}
>
<span>Email ID</span>
</button>
</div>
<form onSubmit={handleResetSubmit}>
{/* Registered Email / Mobile Field */}
<div className="field mb-4">
<label
className="label mb-1.5"
style={{
fontSize: '0.68rem',
fontWeight: 700,
color: '#718096',
letterSpacing: '0.06em',
textTransform: 'uppercase'
}}
>
{resetTab === 'email' ? 'REGISTERED EMAIL ADDRESS' : 'REGISTERED MOBILE NUMBER'}
</label>
<div
className="is-flex is-align-items-center"
style={{
border: isEmailFocused ? '1.5px solid var(--trad-maroon)' : '1.5px solid #CBD5E0',
borderRadius: '10px',
backgroundColor: '#FFFFFF',
height: '46px',
padding: '0 12px',
boxShadow: isEmailFocused ? '0 0 0 3px rgba(103, 27, 38, 0.12)' : 'none',
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
}}
>
<span className="mr-2.5 is-flex is-align-items-center is-flex-shrink-0" style={{ color: '#718096' }}>
{resetTab === 'email' ? <Mail size={16} /> : <Smartphone size={16} />}
</span>
<input
type={resetTab === 'email' ? 'email' : 'tel'}
value={resetTab === 'email' ? resetEmail : resetMobile}
onChange={(e) => resetTab === 'email' ? setResetEmail(e.target.value) : setResetMobile(e.target.value)}
onFocus={() => setIsEmailFocused(true)}
onBlur={() => setIsEmailFocused(false)}
style={{
border: 'none',
outline: 'none',
boxShadow: 'none',
backgroundColor: 'transparent',
height: '100%',
fontSize: '0.88rem',
color: '#1A202C',
flex: 1,
minWidth: 0
}}
/>
<SolidGreenCheck size={16} />
</div>
</div>
{/* 6-Digit OTP Verification Field */}
<div className="field mb-4">
<div className="is-flex is-justify-content-space-between is-align-items-center mb-1.5">
<label
className="label mb-0"
style={{
fontSize: '0.68rem',
fontWeight: 700,
color: '#718096',
letterSpacing: '0.06em',
textTransform: 'uppercase'
}}
>
VERIFICATION CODE ({resetTab === 'email' ? 'EMAIL OTP' : 'SMS OTP'})
</label>
<span
className="is-size-7"
style={{
color: 'var(--trad-maroon)',
fontWeight: 600,
fontSize: '0.75rem',
cursor: 'pointer'
}}
>
Resend code in {resendTimer}s
</span>
</div>
{/* 6 Distinct OTP Boxes */}
<div className="is-flex is-align-items-center" style={{ gap: '8px', maxWidth: '340px' }}>
{otpDigits.map((digit, idx) => (
<input
key={idx}
id={`otp-digit-${idx}`}
type="text"
inputMode="numeric"
maxLength={1}
value={digit}
placeholder="•"
onChange={(e) => handleOtpDigitChange(idx, e.target.value)}
onKeyDown={(e) => handleOtpKeyDown(idx, e)}
className="has-text-centered has-text-weight-bold"
style={{
width: '46px',
height: '48px',
borderRadius: '10px',
border: '1.5px solid #CBD5E0',
fontSize: digit ? '1.15rem' : '1.4rem',
color: '#1A202C',
outline: 'none',
backgroundColor: '#FFFFFF',
flex: '1 1 0',
minWidth: '36px',
maxWidth: '48px',
padding: 0,
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
}}
onFocus={(e) => {
e.target.style.borderColor = 'var(--trad-maroon)';
e.target.style.boxShadow = '0 0 0 3px rgba(103, 27, 38, 0.12)';
}}
onBlur={(e) => {
e.target.style.borderColor = '#CBD5E0';
e.target.style.boxShadow = 'none';
}}
/>
))}
</div>
<p className="help mt-1.5 is-flex is-align-items-center" style={{ color: '#1E8E3E', fontSize: '0.75rem' }}>
<SolidGreenCheck size={13} />
<span className="ml-1.5">Code sent to artisa*****@craftguild.in</span>
</p>
</div>
{/* Create New Password */}
<div className="field mb-3">
<label
className="label mb-1.5"
style={{
fontSize: '0.68rem',
fontWeight: 700,
color: '#718096',
letterSpacing: '0.06em',
textTransform: 'uppercase'
}}
>
CREATE NEW PASSWORD
</label>
<div
className="is-flex is-align-items-center"
style={{
border: isNewPassFocused ? '1.5px solid var(--trad-maroon)' : '1.5px solid #CBD5E0',
borderRadius: '10px',
backgroundColor: '#FFFFFF',
height: '46px',
padding: '0 12px',
boxShadow: isNewPassFocused ? '0 0 0 3px rgba(103, 27, 38, 0.12)' : 'none',
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
}}
>
<input
type={showNewPassword ? 'text' : 'password'}
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
onFocus={() => setIsNewPassFocused(true)}
onBlur={() => setIsNewPassFocused(false)}
style={{
border: 'none',
outline: 'none',
boxShadow: 'none',
backgroundColor: 'transparent',
height: '100%',
fontSize: '0.88rem',
color: '#1A202C',
flex: 1,
minWidth: 0,
letterSpacing: showNewPassword ? 'normal' : '0.12em'
}}
/>
<span
className="is-clickable ml-2 is-flex is-align-items-center"
style={{ cursor: 'pointer', color: '#718096' }}
onClick={() => setShowNewPassword(!showNewPassword)}
>
{showNewPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</span>
</div>
{/* Password Strength Meter (3 green segments, 1 gray, Strong (8+ chars)) */}
<div className="is-flex is-align-items-center mt-2">
<div className="is-flex is-flex-grow-1 mr-3" style={{ gap: '4px' }}>
<div style={{ flex: 1, height: '4px', backgroundColor: '#1E6548', borderRadius: '2px' }} />
<div style={{ flex: 1, height: '4px', backgroundColor: '#1E6548', borderRadius: '2px' }} />
<div style={{ flex: 1, height: '4px', backgroundColor: '#1E6548', borderRadius: '2px' }} />
<div style={{ flex: 1, height: '4px', backgroundColor: '#E2E8F0', borderRadius: '2px' }} />
</div>
<span style={{ color: '#1E6548', fontWeight: 600, fontSize: '0.72rem', whiteSpace: 'nowrap' }}>
Strong (8+ chars)
</span>
</div>
</div>
{/* Confirm New Password */}
<div className="field mb-5">
<label
className="label mb-1.5"
style={{
fontSize: '0.68rem',
fontWeight: 700,
color: '#718096',
letterSpacing: '0.06em',
textTransform: 'uppercase'
}}
>
CONFIRM NEW PASSWORD
</label>
<div
className="is-flex is-align-items-center"
style={{
border: isConfirmPassFocused ? '1.5px solid var(--trad-maroon)' : '1.5px solid #CBD5E0',
borderRadius: '10px',
backgroundColor: '#FFFFFF',
height: '46px',
padding: '0 12px',
boxShadow: isConfirmPassFocused ? '0 0 0 3px rgba(103, 27, 38, 0.12)' : 'none',
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
}}
>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
onFocus={() => setIsConfirmPassFocused(true)}
onBlur={() => setIsConfirmPassFocused(false)}
style={{
border: 'none',
outline: 'none',
boxShadow: 'none',
backgroundColor: 'transparent',
height: '100%',
fontSize: '0.88rem',
color: '#1A202C',
flex: 1,
minWidth: 0,
letterSpacing: '0.12em'
}}
/>
<SolidGreenCheck size={16} />
</div>
</div>
{/* Reset Password & Log In Button */}
<button
type="submit"
className="button is-fullwidth"
style={{
backgroundColor: 'var(--trad-maroon)',
color: '#FFFFFF',
borderRadius: '9999px',
height: '48px',
fontSize: '0.95rem',
fontWeight: 700,
border: 'none',
boxShadow: '0 4px 12px rgba(103, 27, 38, 0.25)',
transition: 'all 0.2s ease'
}}
>
<span>Reset Password &amp; Log In</span>
<ArrowRight size={17} className="ml-2" />
</button>
</form>
{/* Footer Support Desk Link */}
<div className="has-text-centered mt-4 pt-2">
<span className="is-size-7" style={{ color: '#718096' }}>
Having trouble?{' '}
<a
href="#support"
onClick={(e) => { e.preventDefault(); alert('Kala Setu Support Helpline: 1800-419-8723'); }}
style={{ color: 'var(--trad-maroon)', fontWeight: 600, textDecoration: 'none' }}
>
Request assistance from our Onboarding Desk
</a>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};

View file

@ -621,3 +621,41 @@ h1, h2, h3, h4, h5, h6, .title {
}
}
/* ==========================================================================
Responsive Auth Two-Column Layout (.auth-card-layout)
========================================================================== */
.auth-card-layout {
display: flex !important;
flex-direction: column !important;
width: 100% !important;
}
@media screen and (min-width: 1024px) {
.auth-card-layout {
flex-direction: row !important;
}
}
.auth-card-left {
flex: 0 0 42% !important;
max-width: 42% !important;
border-right: 1px solid #EAE5DD !important;
background-color: #FAF9F6 !important;
padding: 2.5rem !important;
box-sizing: border-box !important;
}
@media screen and (max-width: 1023px) {
.auth-card-left {
display: none !important;
}
}
.auth-card-right {
flex: 1 1 auto !important;
min-width: 0 !important;
width: 100% !important;
padding: clamp(1.25rem, 4vw, 3rem) !important;
box-sizing: border-box !important;
}