feat: extend onboarding process to 7 steps with new business details and store configuration fields
Some checks failed
Beta CI/CD Pipeline / build-and-test (push) Failing after 41s
Beta CI/CD Pipeline / deploy-beta (push) Has been skipped

This commit is contained in:
vickytechkey 2026-08-18 11:30:45 +05:30
parent d9f4b380c1
commit 3748de8674
2 changed files with 457 additions and 242 deletions

5
prompt
View file

@ -0,0 +1,5 @@
django server code : /home/vignesh/github/seller_central_backend
Don't push code without my command
beta site : https://betasuppliers.tipro.in/
in betasite only you need test with playwright
Read this repo and understand this project , once you ready let me know i will provide you instruction

View file

@ -68,6 +68,13 @@ export default function App() {
const [aadharS3Key, setAadharS3Key] = useState<string | null>(null) const [aadharS3Key, setAadharS3Key] = useState<string | null>(null)
const [panS3Key, setPanS3Key] = useState<string | null>(null) const [panS3Key, setPanS3Key] = useState<string | null>(null)
// New onboarding customization states
const [businessType, setBusinessType] = useState<'registered_company' | 'self_help_group' | 'individual_maker'>('registered_company')
const [storeSlug, setStoreSlug] = useState('')
const [supportEmail, setSupportEmail] = useState('')
const [supportPhone, setSupportPhone] = useState('')
const [selectedCategories, setSelectedCategories] = useState<string[]>([])
// Step 3: Store and Location details // Step 3: Store and Location details
const [storeName, setStoreName] = useState('My Artisan Handloom') const [storeName, setStoreName] = useState('My Artisan Handloom')
const [storeLogo, setStoreLogo] = useState<string | null>(null) const [storeLogo, setStoreLogo] = useState<string | null>(null)
@ -158,6 +165,13 @@ export default function App() {
setPanFile(profile.pan_file || null); setPanFile(profile.pan_file || null);
setAadharS3Key(profile.aadhar_s3_key || null); setAadharS3Key(profile.aadhar_s3_key || null);
setPanS3Key(profile.pan_s3_key || null); setPanS3Key(profile.pan_s3_key || null);
setBusinessType(profile.business_type || 'registered_company');
setStoreSlug(profile.store_slug || '');
setSupportEmail(profile.support_email || '');
setSupportPhone(profile.support_phone || '');
setSelectedCategories(profile.categories || []);
setStoreName(profile.store_name || 'My Artisan Handloom'); setStoreName(profile.store_name || 'My Artisan Handloom');
setStoreLogo(profile.store_logo || null); setStoreLogo(profile.store_logo || null);
setLogoS3Key(profile.logo_s3_key || null); setLogoS3Key(profile.logo_s3_key || null);
@ -173,7 +187,7 @@ export default function App() {
} }
const step = profile.onboarding_step; const step = profile.onboarding_step;
if (step >= 4) { if (step >= 7) {
setIsProfileComplete(true); setIsProfileComplete(true);
setCurrentPage('dashboard'); setCurrentPage('dashboard');
} else { } else {
@ -529,6 +543,11 @@ export default function App() {
email_verified: emailVerified, email_verified: emailVerified,
gstin: gstin, gstin: gstin,
is_gstin_verified: isGstinVerified, is_gstin_verified: isGstinVerified,
business_type: businessType,
store_slug: storeSlug,
support_email: supportEmail,
support_phone: supportPhone,
categories: selectedCategories,
aadhar_file: aadharFile, aadhar_file: aadharFile,
pan_file: panFile, pan_file: panFile,
aadhar_s3_key: aadharS3Key || 'suppliers/default/aadhar.pdf', aadhar_s3_key: aadharS3Key || 'suppliers/default/aadhar.pdf',
@ -557,14 +576,13 @@ export default function App() {
if (profileStep === 2) { if (profileStep === 2) {
setIsGstinVerified(true) setIsGstinVerified(true)
} }
const isLastStep = profileStep === 3; const isLastStep = profileStep === 6;
saveProfileBackend(isLastStep) saveProfileBackend(isLastStep)
.then(() => { .then(() => {
if (!isLastStep) { if (!isLastStep) {
setProfileStep(prev => prev + 1) setProfileStep(prev => prev + 1)
} else { } else {
setIsProfileComplete(true) setProfileStep(7);
navigateTo('welcome-tour', true)
} }
}) })
.catch(err => { .catch(err => {
@ -1256,13 +1274,17 @@ export default function App() {
{currentPage === 'profile-completion' && ( {currentPage === 'profile-completion' && (
<div className="form-card" style={{ maxWidth: '640px' }}> <div className="form-card" style={{ maxWidth: '640px' }}>
<div className="step-progress-wrapper"> <div className="step-progress-wrapper">
<span className="step-label">Step {profileStep} of 3: { <span className="step-label">Step {profileStep} of 7: {
profileStep === 1 ? 'Contact Verification' : profileStep === 1 ? 'Welcome' :
profileStep === 2 ? 'Tax & Identity Verification' : profileStep === 2 ? 'Business Details' :
'Store & Pickup Location' profileStep === 3 ? 'Identity Verification' :
profileStep === 4 ? 'Contact Verification' :
profileStep === 5 ? 'Choose Categories' :
profileStep === 6 ? 'Store Setup' :
'Verification'
}</span> }</span>
<div className="step-bar-container"> <div className="step-bar-container">
<div className="step-bar-fill" style={{ width: `${(profileStep / 3) * 100}%` }}></div> <div className="step-bar-fill" style={{ width: `${(profileStep / 7) * 100}%` }}></div>
</div> </div>
</div> </div>
@ -1270,8 +1292,177 @@ export default function App() {
<form onSubmit={handleProfileSubmit}> <form onSubmit={handleProfileSubmit}>
{profileStep === 1 && ( {profileStep === 1 && (
<div style={{ textAlign: 'center', padding: '1rem' }}>
<div style={{ fontSize: '3rem', marginBottom: '1.5rem' }}>👋</div>
<h3 style={{ fontSize: '1.25rem', marginBottom: '1rem', color: 'var(--primary)' }}>Welcome to Tradhox Onboarding</h3>
<p style={{ color: 'var(--text-muted)', marginBottom: '2rem', fontSize: '0.95rem', lineHeight: '1.6' }}>
We bridge the gap between traditional Indian artistry and a global marketplace.
Let's set up your supplier profile in a few simple steps.
</p>
<button
type="button"
className="btn btn-primary"
style={{ width: '100%', padding: '0.8rem', fontWeight: 'bold' }}
onClick={() => setProfileStep(2)}
>
Begin Setup
</button>
</div>
)}
{profileStep === 2 && (
<div> <div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 1: Verify Contacts</h3> <h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 2: Business Details</h3>
<div className="form-group">
<label>Business Type *</label>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', marginBottom: '1.5rem' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', padding: '0.75rem', border: '1px solid var(--border)', borderRadius: '6px' }}>
<input type="radio" name="business_type" value="registered_company" checked={businessType === 'registered_company'} onChange={() => setBusinessType('registered_company')} />
Registered Company
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', padding: '0.75rem', border: '1px solid var(--border)', borderRadius: '6px' }}>
<input type="radio" name="business_type" value="self_help_group" checked={businessType === 'self_help_group'} onChange={() => setBusinessType('self_help_group')} />
Self Help Group
</label>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', padding: '0.75rem', border: '1px solid var(--border)', borderRadius: '6px' }}>
<input type="radio" name="business_type" value="individual_maker" checked={businessType === 'individual_maker'} onChange={() => setBusinessType('individual_maker')} />
Individual Maker
</label>
</div>
</div>
<div className="form-group">
<label htmlFor="verify-gst">GSTIN Number *</label>
<div style={{ display: 'flex', gap: '0.75rem' }}>
<input
id="verify-gst"
type="text"
className="form-control"
placeholder="Enter 15-digit GSTIN"
value={gstin}
onChange={(e) => setGstin(e.target.value)}
required
/>
<button
type="button"
className={`btn ${isGstinVerified ? 'btn-secondary' : 'btn-primary'}`}
onClick={handleVerifyGstin}
disabled={isGstinVerified}
style={{ minWidth: '120px' }}
>
{isGstinVerified ? 'Submitted ✓' : 'Submit GSTIN'}
</button>
</div>
{isGstinVerified && (
<p style={{ color: 'var(--success)', fontSize: '0.85rem', marginTop: '0.5rem', fontWeight: 600 }}>
GSTIN verification will be completed within the next 24 hrs
</p>
)}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem', marginTop: '2rem' }}>
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(1)}>
Back
</button>
<button
type="submit"
className="btn btn-primary"
style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={!gstin || !isGstinVerified}
>
Next Step
</button>
</div>
</div>
)}
{profileStep === 3 && (
<div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 3: Identity Verification</h3>
{/* Aadhaar Upload */}
<div className="form-group" style={{ marginBottom: '1.25rem' }}>
<label htmlFor="aadhar-upload">Aadhaar Card Upload *</label>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<input
id="aadhar-upload"
type="file"
accept=".pdf,image/*"
onChange={async (e) => {
if (e.target.files?.[0]) {
const file = e.target.files[0];
setAadharFile(file.name);
alert(`Aadhaar card selected: ${file.name}. Uploading...`);
const s3Key = await uploadDocument(file, 'aadhar');
setAadharS3Key(s3Key);
alert(`Aadhaar card uploaded successfully!`);
}
}}
style={{ display: 'none' }}
/>
<button
type="button"
className="btn btn-outline-dark"
onClick={() => document.getElementById('aadhar-upload')?.click()}
>
📁 Select Aadhaar File
</button>
<span style={{ fontSize: '0.9rem', color: 'var(--text-muted)' }}>
{aadharFile ? `Selected: ${aadharFile}` : 'No file uploaded yet'}
</span>
</div>
</div>
{/* PAN Upload */}
<div className="form-group" style={{ marginBottom: '2rem' }}>
<label htmlFor="pan-upload">PAN Card Upload *</label>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<input
id="pan-upload"
type="file"
accept=".pdf,image/*"
onChange={async (e) => {
if (e.target.files?.[0]) {
const file = e.target.files[0];
setPanFile(file.name);
alert(`PAN card selected: ${file.name}. Uploading...`);
const s3Key = await uploadDocument(file, 'pan');
setPanS3Key(s3Key);
alert(`PAN card uploaded successfully!`);
}
}}
style={{ display: 'none' }}
/>
<button
type="button"
className="btn btn-outline-dark"
onClick={() => document.getElementById('pan-upload')?.click()}
>
📁 Select PAN File
</button>
<span style={{ fontSize: '0.9rem', color: 'var(--text-muted)' }}>
{panFile ? `Selected: ${panFile}` : 'No file uploaded yet'}
</span>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}>
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(2)}>
Back
</button>
<button
type="submit"
className="btn btn-primary"
style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={!aadharFile || !panFile}
>
Next Step
</button>
</div>
</div>
)}
{profileStep === 4 && (
<div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 4: Verify Contacts</h3>
{/* Phone/WhatsApp Verification */} {/* Phone/WhatsApp Verification */}
<div className="form-group" style={{ padding: '1rem', backgroundColor: 'rgba(233, 196, 110, 0.08)', borderRadius: '8px', border: '1px solid var(--border)', marginBottom: '1rem' }}> <div className="form-group" style={{ padding: '1rem', backgroundColor: 'rgba(233, 196, 110, 0.08)', borderRadius: '8px', border: '1px solid var(--border)', marginBottom: '1rem' }}>
@ -1417,169 +1608,151 @@ export default function App() {
)} )}
</div> </div>
<button
type="submit"
className="btn btn-primary"
style={{ width: '100%', padding: '0.8rem', backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={!phoneVerified || !emailVerified}
>
Next Step: Identity Verification
</button>
</div>
)}
{profileStep === 2 && (
<div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 2: GSTIN, PAN & Aadhaar</h3>
{/* GSTIN verification */}
<div className="form-group">
<label htmlFor="verify-gst">GSTIN Number *</label>
<div style={{ display: 'flex', gap: '0.75rem' }}>
<input
id="verify-gst"
type="text"
className="form-control"
placeholder="Enter 15-digit GSTIN"
value={gstin}
onChange={(e) => setGstin(e.target.value)}
required
/>
<button
type="button"
className={`btn ${isGstinVerified ? 'btn-secondary' : 'btn-primary'}`}
onClick={handleVerifyGstin}
disabled={isGstinVerified}
style={{ minWidth: '120px' }}
>
{isGstinVerified ? 'Submitted ✓' : 'Submit GSTIN'}
</button>
</div>
{isGstinVerified && (
<p style={{ color: 'var(--success)', fontSize: '0.85rem', marginTop: '0.5rem', fontWeight: 600 }}>
verification will be completed within next 24 hrs
</p>
)}
</div>
{/* Aadhaar Upload */}
<div className="form-group" style={{ marginBottom: '1.25rem' }}>
<label htmlFor="aadhar-upload">Aadhaar Card Upload *</label>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<input
id="aadhar-upload"
type="file"
accept=".pdf,image/*"
onChange={async (e) => {
if (e.target.files?.[0]) {
const file = e.target.files[0];
setAadharFile(file.name);
alert(`Aadhaar card selected: ${file.name}. Uploading...`);
const s3Key = await uploadDocument(file, 'aadhar');
setAadharS3Key(s3Key);
alert(`Aadhaar card uploaded successfully!`);
}
}}
style={{ display: 'none' }}
/>
<button
type="button"
className="btn btn-outline-dark"
onClick={() => document.getElementById('aadhar-upload')?.click()}
>
📁 Select Aadhaar File
</button>
<span style={{ fontSize: '0.9rem', color: 'var(--text-muted)' }}>
{aadharFile ? `Selected: ${aadharFile}` : 'No file uploaded yet'}
</span>
</div>
</div>
{/* PAN Upload */}
<div className="form-group" style={{ marginBottom: '2rem' }}>
<label htmlFor="pan-upload">PAN Card Upload *</label>
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
<input
id="pan-upload"
type="file"
accept=".pdf,image/*"
onChange={async (e) => {
if (e.target.files?.[0]) {
const file = e.target.files[0];
setPanFile(file.name);
alert(`PAN card selected: ${file.name}. Uploading...`);
const s3Key = await uploadDocument(file, 'pan');
setPanS3Key(s3Key);
alert(`PAN card uploaded successfully!`);
}
}}
style={{ display: 'none' }}
/>
<button
type="button"
className="btn btn-outline-dark"
onClick={() => document.getElementById('pan-upload')?.click()}
>
📁 Select PAN File
</button>
<span style={{ fontSize: '0.9rem', color: 'var(--text-muted)' }}>
{panFile ? `Selected: ${panFile}` : 'No file uploaded yet'}
</span>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}> <div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}>
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(1)}> <button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(3)}>
Back Back
</button> </button>
<button <button
type="submit" type="submit"
className="btn btn-primary" className="btn btn-primary"
style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }} style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={!gstin || !aadharFile || !panFile} disabled={!phoneVerified || !emailVerified}
> >
Next Step: Store & Location Next Step
</button> </button>
</div> </div>
</div> </div>
)} )}
{profileStep === 3 && ( {profileStep === 5 && (
<div> <div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 3: Store Details & Location Map</h3> <h3 style={{ fontSize: '1.1rem', marginBottom: '0.5rem', color: 'var(--primary)' }}>Step 5: Choose Product Categories</h3>
<p style={{ color: 'var(--text-muted)', marginBottom: '1.5rem', fontSize: '0.9rem' }}>
Select the categories that apply to your artisanal goods.
</p>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem', marginBottom: '2rem' }}>
{['Sustainable Products', 'Home Decor', 'Eco-Friendly', 'OPOD Products', 'GI Tagged', 'Textiles & Apparel'].map(cat => {
const isSelected = selectedCategories.includes(cat);
return (
<div
key={cat}
onClick={() => {
if (isSelected) {
setSelectedCategories(selectedCategories.filter(c => c !== cat));
} else {
setSelectedCategories([...selectedCategories, cat]);
}
}}
style={{
padding: '1rem',
border: isSelected ? '2px solid var(--primary)' : '1px solid var(--border)',
backgroundColor: isSelected ? 'rgba(107, 26, 44, 0.05)' : 'transparent',
borderRadius: '8px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontWeight: isSelected ? '600' : 'normal',
transition: 'all 0.2s ease',
textAlign: 'center'
}}
>
{cat}
</div>
);
})}
</div>
<div className="form-group"> <div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem' }}>
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(4)}>
Back
</button>
<button
type="submit"
className="btn btn-primary"
style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={selectedCategories.length === 0}
>
Next Step
</button>
</div>
</div>
)}
{profileStep === 6 && (
<div>
<h3 style={{ fontSize: '1.1rem', marginBottom: '1rem', color: 'var(--primary)' }}>Step 6: Store Details & Pickup Location</h3>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }} className="form-group">
<div>
<label htmlFor="store-name">Store Display Name *</label> <label htmlFor="store-name">Store Display Name *</label>
<input <input
id="store-name" id="store-name"
type="text" type="text"
className="form-control" className="form-control"
placeholder="Enter store name"
value={storeName} value={storeName}
onChange={(e) => setStoreName(e.target.value)} onChange={(e) => setStoreName(e.target.value)}
required required
/> />
</div> </div>
<div>
<label htmlFor="store-slug">Store Slug / Custom URL *</label>
<input
id="store-slug"
type="text"
className="form-control"
placeholder="e.g. my-artisan-shop"
value={storeSlug}
onChange={(e) => setStoreSlug(e.target.value.toLowerCase().replace(/[^a-z0-9\-]/g, ''))}
required
/>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }} className="form-group">
<div>
<label htmlFor="support-email">Support Email *</label>
<input
id="support-email"
type="email"
className="form-control"
placeholder="hello@shop.com"
value={supportEmail}
onChange={(e) => setSupportEmail(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="support-phone">Support Phone *</label>
<input
id="support-phone"
type="tel"
className="form-control"
placeholder="+91..."
value={supportPhone}
onChange={(e) => setSupportPhone(e.target.value)}
required
/>
</div>
</div>
<div className="form-group"> <div className="form-group">
<label htmlFor="store-bio">About Your Craft / Business *</label> <label htmlFor="store-bio">Store Description / Bio *</label>
<textarea <textarea
id="store-bio" id="store-bio"
className="form-control" className="form-control"
rows={2} rows={2}
placeholder="Describe your craft, materials, and history..."
value={businessBio} value={businessBio}
onChange={(e) => setBusinessBio(e.target.value)} onChange={(e) => setBusinessBio(e.target.value)}
required required
></textarea> ></textarea>
</div> </div>
{/* Interactive Location Map Picker */}
<div className="form-group"> <div className="form-group">
<label>Store Location on Map *</label> <label>Store Location on Map *</label>
<div <div
style={{ style={{
height: '180px', height: '150px',
backgroundColor: '#cbd5e1', backgroundColor: '#cbd5e1',
borderRadius: '8px', borderRadius: '8px',
position: 'relative', position: 'relative',
@ -1601,10 +1774,8 @@ export default function App() {
...prev, ...prev,
street: `Plot ${x}, Sector ${Math.round(y/10)}, Handicraft Park` street: `Plot ${x}, Sector ${Math.round(y/10)}, Handicraft Park`
})); }));
alert(`Location Pinned: Lat: ${(12.9 + y * 0.001).toFixed(4)}, Lng: ${(77.5 + x * 0.001).toFixed(4)}`);
}} }}
> >
{/* Map Marker Pin */}
<div <div
style={{ style={{
position: 'absolute', position: 'absolute',
@ -1613,43 +1784,28 @@ export default function App() {
transform: 'translate(-50%, -100%)', transform: 'translate(-50%, -100%)',
fontSize: '2rem', fontSize: '2rem',
color: 'var(--error)', color: 'var(--error)',
pointerEvents: 'none', pointerEvents: 'none'
textShadow: '0 2px 4px rgba(0,0,0,0.3)'
}} }}
> >
📍 📍
</div> </div>
<div style={{ <span style={{ fontSize: '0.85rem', color: '#475569', pointerEvents: 'none', fontWeight: 600 }}>Click map to pin location</span>
position: 'absolute',
bottom: '8px',
left: '8px',
backgroundColor: 'rgba(26, 43, 69, 0.85)',
color: 'white',
padding: '0.3rem 0.6rem',
borderRadius: '4px',
fontSize: '0.75rem',
pointerEvents: 'none'
}}>
Coordinates: Lat {mapCoordinates.lat}, Lng {mapCoordinates.lng} (Click map to change pin)
</div>
<span style={{ fontSize: '0.85rem', color: '#475569', pointerEvents: 'none', fontWeight: 600 }}>Click anywhere on the map grid to pin location</span>
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group">
<label htmlFor="location-text">Location in Text (Address) *</label> <label htmlFor="location-text">Pickup Address *</label>
<input <input
id="location-text" id="location-text"
type="text" type="text"
className="form-control" className="form-control"
placeholder="Street, Landmark, District"
value={address.street} value={address.street}
onChange={(e) => setAddress({ ...address, street: e.target.value })} onChange={(e) => setAddress({ ...address, street: e.target.value })}
required required
/> />
</div> </div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }} className="form-group"> <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: '0.5rem' }} className="form-group">
<div> <div>
<label htmlFor="loc-city">City *</label> <label htmlFor="loc-city">City *</label>
<input <input
@ -1661,13 +1817,23 @@ export default function App() {
required required
/> />
</div> </div>
<div>
<label htmlFor="loc-state">State *</label>
<input
id="loc-state"
type="text"
className="form-control"
value={address.state}
onChange={(e) => setAddress({ ...address, state: e.target.value })}
required
/>
</div>
<div> <div>
<label htmlFor="loc-pincode">Pincode *</label> <label htmlFor="loc-pincode">Pincode *</label>
<input <input
id="loc-pincode" id="loc-pincode"
type="text" type="text"
className="form-control" className="form-control"
placeholder="6-digit Pincode"
value={address.pincode} value={address.pincode}
onChange={(e) => setAddress({ ...address, pincode: e.target.value })} onChange={(e) => setAddress({ ...address, pincode: e.target.value })}
required required
@ -1676,76 +1842,42 @@ export default function App() {
</div> </div>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem', marginTop: '2rem' }}> <div style={{ display: 'flex', justifyContent: 'space-between', gap: '1rem', marginTop: '2rem' }}>
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(2)}> <button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(5)}>
Back Back
</button> </button>
<button <button
type="submit" type="submit"
className="btn btn-primary" className="btn btn-primary"
style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }} style={{ flex: 1, backgroundColor: 'var(--accent)', color: 'var(--primary)', fontWeight: 'bold' }}
disabled={!storeName || !storeSlug || !supportEmail || !supportPhone || !address.street || !address.city || !address.state || !address.pincode}
> >
Submit Supplier Profile Submit Supplier Profile
</button> </button>
</div> </div>
</div> </div>
)} )}
</form>
{profileStep === 7 && (
<div style={{ textAlign: 'center', padding: '1rem' }}>
<div style={{ fontSize: '4rem', marginBottom: '1.5rem' }}>🎉</div>
<h3 style={{ fontSize: '1.25rem', marginBottom: '1rem', color: 'var(--primary)' }}>Setup Complete!</h3>
<p style={{ color: 'var(--text-muted)', marginBottom: '2rem', fontSize: '0.95rem' }}>
Your supplier profile has been successfully submitted and is under review. You can now access your preview dashboard.
</p>
<button
type="button"
className="btn btn-dark"
style={{ width: '100%', padding: '0.8rem', fontWeight: 'bold' }}
onClick={() => {
setIsProfileComplete(true);
navigateTo('welcome-tour', true);
}}
>
Go to Dashboard
</button>
</div> </div>
)} )}
</form>
{/* ONBOARDING CONFIRMATION / REVIEW STATUS VIEW (Step 3 of 3) */}
{currentPage === 'confirmation' && (
<div className="form-card" style={{ maxWidth: '640px', textAlign: 'center' }}>
<div className="step-progress-wrapper">
<span className="step-label">Step 3 of 3: Verification</span>
<div className="step-bar-container">
<div className="step-bar-fill" style={{ width: '100%' }}></div>
</div>
</div>
<div style={{ fontSize: '4rem', marginBottom: '1.5rem' }}>🎉</div>
<h2 className="form-card-title">Welcome to {CONFIG.companyName}!</h2>
<p style={{ color: 'var(--text-muted)', marginBottom: '2rem', fontSize: '1.05rem' }}>
Your registration is under review. We will verify your GSTIN credentials and activate your dashboard access within 24-48 hours.
</p>
<div className="conf-dashboard-preview">
<div className="mock-dash-header">
<div className="mock-dash-dot"></div>
<div className="mock-dash-dot"></div>
<div className="mock-dash-dot"></div>
</div>
<div className="mock-dash-body">
<div className="mock-dash-card">
<div className="mock-dash-card-title">Total Sales</div>
<div className="mock-dash-card-val">$0.00</div>
</div>
<div className="mock-dash-card">
<div className="mock-dash-card-title">Active Orders</div>
<div className="mock-dash-card-val">0</div>
</div>
<div className="mock-dash-card">
<div className="mock-dash-card-title">Balance</div>
<div className="mock-dash-card-val">$0.00</div>
</div>
<div className="mock-dash-chart">
<div className="mock-chart-bar" style={{ height: '40px' }}></div>
<div className="mock-chart-bar" style={{ height: '60px' }}></div>
<div className="mock-chart-bar" style={{ height: '50px' }}></div>
<div className="mock-chart-bar active" style={{ height: '80px' }}></div>
<div className="mock-chart-bar" style={{ height: '30px' }}></div>
</div>
</div>
</div>
<button
className="btn btn-dark"
style={{ marginTop: '2.5rem', width: '100%', padding: '0.8rem' }}
onClick={() => navigateTo('dashboard')}
>
Go to Dashboard (Preview Only)
</button>
</div> </div>
)} )}
@ -2494,8 +2626,14 @@ export default function App() {
<div className="form-card" style={{ margin: 0, maxWidth: '640px' }}> <div className="form-card" style={{ margin: 0, maxWidth: '640px' }}>
<h2 className="form-card-title" style={{ textAlign: 'left', marginBottom: '2rem' }}>Store Configurations</h2> <h2 className="form-card-title" style={{ textAlign: 'left', marginBottom: '2rem' }}>Store Configurations</h2>
<form onSubmit={e => { e.preventDefault(); alert('Store configurations updated successfully!'); }}> <form onSubmit={e => {
<div className="form-group"> e.preventDefault();
saveProfileBackend(true)
.then(() => alert('Store configurations updated successfully!'))
.catch(err => alert(err.message));
}}>
<div className="form-group" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
<div>
<label htmlFor="set-store">Store Display Name *</label> <label htmlFor="set-store">Store Display Name *</label>
<input <input
id="set-store" id="set-store"
@ -2506,6 +2644,18 @@ export default function App() {
required required
/> />
</div> </div>
<div>
<label htmlFor="set-slug">Store Slug / Custom URL *</label>
<input
id="set-slug"
type="text"
className="form-control"
value={storeSlug}
onChange={e => setStoreSlug(e.target.value.toLowerCase().replace(/[^a-z0-9\-]/g, ''))}
required
/>
</div>
</div>
<div className="form-group"> <div className="form-group">
<label htmlFor="set-logo">Store Logo</label> <label htmlFor="set-logo">Store Logo</label>
@ -2526,6 +2676,31 @@ export default function App() {
</div> </div>
</div> </div>
<div className="form-group" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
<div>
<label htmlFor="set-email">Support Email *</label>
<input
id="set-email"
type="email"
className="form-control"
value={supportEmail}
onChange={e => setSupportEmail(e.target.value)}
required
/>
</div>
<div>
<label htmlFor="set-phone">Support Phone *</label>
<input
id="set-phone"
type="tel"
className="form-control"
value={supportPhone}
onChange={e => setSupportPhone(e.target.value)}
required
/>
</div>
</div>
<div className="form-group"> <div className="form-group">
<label htmlFor="set-bio">About Your Craft / Business</label> <label htmlFor="set-bio">About Your Craft / Business</label>
<textarea <textarea
@ -2537,6 +2712,31 @@ export default function App() {
></textarea> ></textarea>
</div> </div>
<div className="form-group">
<label>Product Categories</label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.5rem', marginTop: '0.5rem' }}>
{['Sustainable Products', 'Home Decor', 'Eco-Friendly', 'OPOD Products', 'GI Tagged', 'Textiles & Apparel'].map(cat => {
const isSelected = selectedCategories.includes(cat);
return (
<label key={cat} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', padding: '0.5rem', border: '1px solid var(--border)', borderRadius: '4px' }}>
<input
type="checkbox"
checked={isSelected}
onChange={() => {
if (isSelected) {
setSelectedCategories(selectedCategories.filter(c => c !== cat));
} else {
setSelectedCategories([...selectedCategories, cat]);
}
}}
/>
{cat}
</label>
);
})}
</div>
</div>
<div className="form-group"> <div className="form-group">
<label>Pickup Location Address *</label> <label>Pickup Location Address *</label>
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '0.75rem', marginBottom: '0.75rem' }}> <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '0.75rem', marginBottom: '0.75rem' }}>
@ -2577,17 +2777,27 @@ export default function App() {
</div> </div>
</div> </div>
<div className="form-group"> <div className="form-group" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
<div>
<label htmlFor="set-business-type">Business Type</label>
<input
id="set-business-type"
type="text"
className="form-control"
value={businessType.replace('_', ' ').toUpperCase()}
disabled
/>
</div>
<div>
<label htmlFor="set-gstin">GSTIN Number</label> <label htmlFor="set-gstin">GSTIN Number</label>
<input <input
id="set-gstin" id="set-gstin"
type="text" type="text"
className="form-control" className="form-control"
value={gstin} value={gstin}
onChange={e => setGstin(e.target.value)}
disabled disabled
/> />
<p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', marginTop: '0.25rem' }}>GSTIN cannot be modified after verification.</p> </div>
</div> </div>
<button type="submit" className="btn btn-dark" style={{ width: '100%', marginTop: '2rem' }}> <button type="submit" className="btn btn-dark" style={{ width: '100%', marginTop: '2rem' }}>