feat: extend onboarding process to 7 steps with new business details and store configuration fields
This commit is contained in:
parent
d9f4b380c1
commit
3748de8674
2 changed files with 457 additions and 242 deletions
5
prompt
5
prompt
|
|
@ -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
|
||||
640
src/App.tsx
640
src/App.tsx
|
|
@ -68,6 +68,13 @@ export default function App() {
|
|||
const [aadharS3Key, setAadharS3Key] = 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
|
||||
const [storeName, setStoreName] = useState('My Artisan Handloom')
|
||||
const [storeLogo, setStoreLogo] = useState<string | null>(null)
|
||||
|
|
@ -158,6 +165,13 @@ export default function App() {
|
|||
setPanFile(profile.pan_file || null);
|
||||
setAadharS3Key(profile.aadhar_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');
|
||||
setStoreLogo(profile.store_logo || null);
|
||||
setLogoS3Key(profile.logo_s3_key || null);
|
||||
|
|
@ -173,7 +187,7 @@ export default function App() {
|
|||
}
|
||||
|
||||
const step = profile.onboarding_step;
|
||||
if (step >= 4) {
|
||||
if (step >= 7) {
|
||||
setIsProfileComplete(true);
|
||||
setCurrentPage('dashboard');
|
||||
} else {
|
||||
|
|
@ -529,6 +543,11 @@ export default function App() {
|
|||
email_verified: emailVerified,
|
||||
gstin: gstin,
|
||||
is_gstin_verified: isGstinVerified,
|
||||
business_type: businessType,
|
||||
store_slug: storeSlug,
|
||||
support_email: supportEmail,
|
||||
support_phone: supportPhone,
|
||||
categories: selectedCategories,
|
||||
aadhar_file: aadharFile,
|
||||
pan_file: panFile,
|
||||
aadhar_s3_key: aadharS3Key || 'suppliers/default/aadhar.pdf',
|
||||
|
|
@ -557,14 +576,13 @@ export default function App() {
|
|||
if (profileStep === 2) {
|
||||
setIsGstinVerified(true)
|
||||
}
|
||||
const isLastStep = profileStep === 3;
|
||||
const isLastStep = profileStep === 6;
|
||||
saveProfileBackend(isLastStep)
|
||||
.then(() => {
|
||||
if (!isLastStep) {
|
||||
setProfileStep(prev => prev + 1)
|
||||
} else {
|
||||
setIsProfileComplete(true)
|
||||
navigateTo('welcome-tour', true)
|
||||
setProfileStep(7);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
|
|
@ -1256,13 +1274,17 @@ export default function App() {
|
|||
{currentPage === 'profile-completion' && (
|
||||
<div className="form-card" style={{ maxWidth: '640px' }}>
|
||||
<div className="step-progress-wrapper">
|
||||
<span className="step-label">Step {profileStep} of 3: {
|
||||
profileStep === 1 ? 'Contact Verification' :
|
||||
profileStep === 2 ? 'Tax & Identity Verification' :
|
||||
'Store & Pickup Location'
|
||||
<span className="step-label">Step {profileStep} of 7: {
|
||||
profileStep === 1 ? 'Welcome' :
|
||||
profileStep === 2 ? 'Business Details' :
|
||||
profileStep === 3 ? 'Identity Verification' :
|
||||
profileStep === 4 ? 'Contact Verification' :
|
||||
profileStep === 5 ? 'Choose Categories' :
|
||||
profileStep === 6 ? 'Store Setup' :
|
||||
'Verification'
|
||||
}</span>
|
||||
<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>
|
||||
|
||||
|
|
@ -1270,8 +1292,177 @@ export default function App() {
|
|||
|
||||
<form onSubmit={handleProfileSubmit}>
|
||||
{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>
|
||||
<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 */}
|
||||
<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>
|
||||
|
||||
<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' }}>
|
||||
<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
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary"
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{profileStep === 3 && (
|
||||
{profileStep === 5 && (
|
||||
<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>
|
||||
<input
|
||||
id="store-name"
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Enter store name"
|
||||
value={storeName}
|
||||
onChange={(e) => setStoreName(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</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">
|
||||
<label htmlFor="store-bio">About Your Craft / Business *</label>
|
||||
<label htmlFor="store-bio">Store Description / Bio *</label>
|
||||
<textarea
|
||||
id="store-bio"
|
||||
className="form-control"
|
||||
rows={2}
|
||||
placeholder="Describe your craft, materials, and history..."
|
||||
value={businessBio}
|
||||
onChange={(e) => setBusinessBio(e.target.value)}
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
{/* Interactive Location Map Picker */}
|
||||
<div className="form-group">
|
||||
<label>Store Location on Map *</label>
|
||||
<div
|
||||
style={{
|
||||
height: '180px',
|
||||
height: '150px',
|
||||
backgroundColor: '#cbd5e1',
|
||||
borderRadius: '8px',
|
||||
position: 'relative',
|
||||
|
|
@ -1601,10 +1774,8 @@ export default function App() {
|
|||
...prev,
|
||||
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
|
||||
style={{
|
||||
position: 'absolute',
|
||||
|
|
@ -1613,43 +1784,28 @@ export default function App() {
|
|||
transform: 'translate(-50%, -100%)',
|
||||
fontSize: '2rem',
|
||||
color: 'var(--error)',
|
||||
pointerEvents: 'none',
|
||||
textShadow: '0 2px 4px rgba(0,0,0,0.3)'
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
>
|
||||
📍
|
||||
</div>
|
||||
<div style={{
|
||||
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>
|
||||
<span style={{ fontSize: '0.85rem', color: '#475569', pointerEvents: 'none', fontWeight: 600 }}>Click map to pin location</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="location-text">Location in Text (Address) *</label>
|
||||
<label htmlFor="location-text">Pickup Address *</label>
|
||||
<input
|
||||
id="location-text"
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Street, Landmark, District"
|
||||
value={address.street}
|
||||
onChange={(e) => setAddress({ ...address, street: e.target.value })}
|
||||
required
|
||||
/>
|
||||
</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>
|
||||
<label htmlFor="loc-city">City *</label>
|
||||
<input
|
||||
|
|
@ -1661,13 +1817,23 @@ export default function App() {
|
|||
required
|
||||
/>
|
||||
</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>
|
||||
<label htmlFor="loc-pincode">Pincode *</label>
|
||||
<input
|
||||
id="loc-pincode"
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="6-digit Pincode"
|
||||
value={address.pincode}
|
||||
onChange={(e) => setAddress({ ...address, pincode: e.target.value })}
|
||||
required
|
||||
|
|
@ -1676,76 +1842,42 @@ export default function App() {
|
|||
</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(2)}>
|
||||
<button type="button" className="btn btn-outline-dark" style={{ flex: 1 }} onClick={() => setProfileStep(5)}>
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary"
|
||||
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
|
||||
</button>
|
||||
</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>
|
||||
)}
|
||||
|
||||
{/* 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>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -2494,8 +2626,14 @@ export default function App() {
|
|||
<div className="form-card" style={{ margin: 0, maxWidth: '640px' }}>
|
||||
<h2 className="form-card-title" style={{ textAlign: 'left', marginBottom: '2rem' }}>Store Configurations</h2>
|
||||
|
||||
<form onSubmit={e => { e.preventDefault(); alert('Store configurations updated successfully!'); }}>
|
||||
<div className="form-group">
|
||||
<form onSubmit={e => {
|
||||
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>
|
||||
<input
|
||||
id="set-store"
|
||||
|
|
@ -2506,6 +2644,18 @@ export default function App() {
|
|||
required
|
||||
/>
|
||||
</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">
|
||||
<label htmlFor="set-logo">Store Logo</label>
|
||||
|
|
@ -2526,6 +2676,31 @@ export default function App() {
|
|||
</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">
|
||||
<label htmlFor="set-bio">About Your Craft / Business</label>
|
||||
<textarea
|
||||
|
|
@ -2537,6 +2712,31 @@ export default function App() {
|
|||
></textarea>
|
||||
</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">
|
||||
<label>Pickup Location Address *</label>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '0.75rem', marginBottom: '0.75rem' }}>
|
||||
|
|
@ -2577,17 +2777,27 @@ export default function App() {
|
|||
</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>
|
||||
<input
|
||||
id="set-gstin"
|
||||
type="text"
|
||||
className="form-control"
|
||||
value={gstin}
|
||||
onChange={e => setGstin(e.target.value)}
|
||||
disabled
|
||||
/>
|
||||
<p style={{ fontSize: '0.85rem', color: 'var(--text-muted)', marginTop: '0.25rem' }}>GSTIN cannot be modified after verification.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" className="btn btn-dark" style={{ width: '100%', marginTop: '2rem' }}>
|
||||
|
|
|
|||
Loading…
Reference in a new issue