361 lines
18 KiB
TypeScript
361 lines
18 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Image from 'next/image';
|
|
import { useRouter } from 'next/navigation';
|
|
import styles from "./page.module.css";
|
|
import { indianLocations } from "@/data/indianLocations";
|
|
import { businessTypes } from "@/data/businessTypes";
|
|
|
|
type NotificationType = 'success' | 'error' | 'info';
|
|
interface Notification {
|
|
id: number;
|
|
message: string;
|
|
type: NotificationType;
|
|
}
|
|
|
|
export default function Onboarding() {
|
|
const router = useRouter();
|
|
const [view, setView] = useState<'selection' | 'create' | 'join'>('selection');
|
|
|
|
// Create Company State
|
|
const [companyName, setCompanyName] = useState("");
|
|
const [businessType, setBusinessType] = useState("");
|
|
const [industry, setIndustry] = useState("");
|
|
const [size, setSize] = useState("");
|
|
const [city, setCity] = useState("");
|
|
const [state, setState] = useState("");
|
|
|
|
// Compliance State
|
|
const [complianceType, setComplianceType] = useState("");
|
|
const [complianceId, setComplianceId] = useState("");
|
|
|
|
// Derived Data
|
|
// Filter cities based on selected state if available, else show all or empty
|
|
// const availableCities = indianLocations.find(s => s.name === state)?.cities || [];
|
|
|
|
// Join Company State
|
|
const [inviteCode, setInviteCode] = useState("");
|
|
|
|
// Notifications
|
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
|
|
const showNotification = (message: string, type: NotificationType = 'info') => {
|
|
const id = Date.now();
|
|
setNotifications(prev => [...prev, { id, message, type }]);
|
|
setTimeout(() => {
|
|
setNotifications(prev => prev.filter(n => n.id !== id));
|
|
}, 4000);
|
|
};
|
|
|
|
const handleCreateSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
// Simulate API call
|
|
if (!companyName || !businessType || !industry || !size || !state || !city) {
|
|
showNotification("Please fill in all required fields.", 'error');
|
|
return;
|
|
}
|
|
|
|
showNotification("Creating your workspace...", 'success');
|
|
|
|
// Mock success redirect
|
|
setTimeout(() => {
|
|
// Ideally redirect to dashboard, for now just show success
|
|
console.log("Company Created:", {
|
|
companyName,
|
|
businessType,
|
|
industry,
|
|
size,
|
|
city,
|
|
state,
|
|
compliance: complianceType ? { type: complianceType, id: complianceId } : null,
|
|
country: "India",
|
|
currency: "INR"
|
|
});
|
|
// Redirect to dashboard
|
|
router.push('/dashboard');
|
|
}, 1500);
|
|
};
|
|
|
|
const handleJoinSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!inviteCode) {
|
|
showNotification("Please enter an invite code.", 'error');
|
|
return;
|
|
}
|
|
|
|
showNotification("Verifying invitation...", 'info');
|
|
|
|
setTimeout(() => {
|
|
showNotification("Successfully joined company!", 'success');
|
|
}, 1500);
|
|
};
|
|
|
|
return (
|
|
<div className={styles.container}>
|
|
|
|
{/* Notification Toast (Reused logic) */}
|
|
<div style={{ position: 'fixed', top: '20px', right: '20px', zIndex: 1000, display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
|
{notifications.map(n => (
|
|
<div key={n.id} style={{
|
|
background: n.type === 'success' ? 'rgba(0, 200, 100, 0.9)' : n.type === 'error' ? 'rgba(255, 80, 80, 0.9)' : 'rgba(0, 150, 255, 0.9)',
|
|
backdropFilter: 'blur(10px)',
|
|
padding: '12px 20px',
|
|
borderRadius: '8px',
|
|
color: 'white',
|
|
boxShadow: '0 5px 15px rgba(0,0,0,0.2)',
|
|
minWidth: '250px',
|
|
fontSize: '0.9rem',
|
|
animation: 'fadeIn 0.3s ease'
|
|
}}>
|
|
{n.message}
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className={styles.onboardingCard}>
|
|
|
|
{/* Header with Logo */}
|
|
<div className={styles.header}>
|
|
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '1rem' }}>
|
|
<Image
|
|
src="/logo_symbol.png"
|
|
alt="Pennyzo"
|
|
width={80}
|
|
height={80}
|
|
style={{ width: 'auto', height: '64px', objectFit: 'contain' }}
|
|
/>
|
|
</div>
|
|
<h1 className={styles.title}>
|
|
{view === 'selection' && "Welcome, Founder"}
|
|
{view === 'create' && "Establish Entity"}
|
|
{view === 'join' && "Join Structure"}
|
|
</h1>
|
|
<p className={styles.subtitle}>
|
|
{view === 'selection' && "Choose your path to begin using the Business 360 Platform."}
|
|
{view === 'create' && "Register a new business entity to manage operations, finance, and teams."}
|
|
{view === 'join' && "Enter your invitation code provided by your organization administrator."}
|
|
</p>
|
|
</div>
|
|
|
|
{/* VIEW 1: SELECTION */}
|
|
{view === 'selection' && (
|
|
<div className={styles.selectionContainer}>
|
|
|
|
{/* Option: Create Company */}
|
|
<div className={styles.optionCard} onClick={() => setView('create')}>
|
|
<div className={styles.iconWrapper}>
|
|
<span>+</span>
|
|
</div>
|
|
<h3 className={styles.optionTitle}>Create Company</h3>
|
|
<p className={styles.optionDesc}>
|
|
Register a new business organization. You will be assigned the <strong>Founder</strong> role with full administrative controls.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Option: Join Company */}
|
|
<div className={` ${styles.optionCard} ${styles.join} `} onClick={() => setView('join')}>
|
|
<div className={styles.iconWrapper}>
|
|
<span>→</span>
|
|
</div>
|
|
<h3 className={styles.optionTitle}>Join Existing</h3>
|
|
<p className={styles.optionDesc}>
|
|
Join a team using an invitation code or request access. You will be assigned a role based on the invite.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* VIEW 2: CREATE COMPANY FORM */}
|
|
{view === 'create' && (
|
|
<div className={styles.formContainer}>
|
|
<form onSubmit={handleCreateSubmit}>
|
|
<div className={styles.formGrid}>
|
|
{/* Company Name */}
|
|
<div className={styles.formGroup} style={{ gridColumn: 'span 2' }}>
|
|
<input
|
|
type="text"
|
|
className={styles.input}
|
|
placeholder=" "
|
|
value={companyName}
|
|
onChange={e => setCompanyName(e.target.value)}
|
|
required
|
|
/>
|
|
<label className={styles.label}>Company/Trade Name</label>
|
|
</div>
|
|
|
|
{/* Business Type */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={businessType}
|
|
onChange={e => setBusinessType(e.target.value)}
|
|
required
|
|
>
|
|
<option value="" disabled>Select Business Type</option>
|
|
{businessTypes.map(bt => (
|
|
<option key={bt.id} value={bt.id}>{bt.label}</option>
|
|
))}
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>Legal Structure</label>
|
|
</div>
|
|
|
|
{/* Industry */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={industry}
|
|
onChange={e => setIndustry(e.target.value)}
|
|
required
|
|
>
|
|
<option value="" disabled>Select Industry</option>
|
|
<option value="tech">Technology & SaaS</option>
|
|
<option value="finance">Finance & Banking</option>
|
|
<option value="retail">Retail & E-commerce</option>
|
|
<option value="healthcare">Healthcare</option>
|
|
<option value="manufacturing">Manufacturing</option>
|
|
<option value="consulting">Consulting & Services</option>
|
|
<option value="education">Education & EdTech</option>
|
|
<option value="construction">Real Estate & Construction</option>
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>Industry Sector</label>
|
|
</div>
|
|
|
|
{/* Company Size */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={size}
|
|
onChange={e => setSize(e.target.value)}
|
|
required
|
|
>
|
|
<option value="" disabled>Employee Count</option>
|
|
<option value="1-10">1 - 10 Employees</option>
|
|
<option value="11-50">11 - 50 Employees</option>
|
|
<option value="51-200">51 - 200 Employees</option>
|
|
<option value="201-500">201 - 500 Employees</option>
|
|
<option value="500+">500+ Employees</option>
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>Business Size</label>
|
|
</div>
|
|
|
|
{/* State Selection */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={state}
|
|
onChange={e => {
|
|
setState(e.target.value);
|
|
setCity(""); // Reset city on state change
|
|
}}
|
|
required
|
|
>
|
|
<option value="" disabled>Select State</option>
|
|
{indianLocations.map((s, i) => (
|
|
<option key={i} value={s.name}>{s.name}</option>
|
|
))}
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>State</label>
|
|
</div>
|
|
|
|
{/* City Selection (Dependent) */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={city}
|
|
onChange={e => setCity(e.target.value)}
|
|
required
|
|
disabled={!state}
|
|
>
|
|
<option value="" disabled>Select City</option>
|
|
{indianLocations.find(s => s.name === state)?.cities.map((c: string, i: number) => (
|
|
<option key={i} value={c}>{c}</option>
|
|
))}
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>City</label>
|
|
</div>
|
|
|
|
{/* Country (Locked) */}
|
|
<div className={styles.formGroup}>
|
|
<input
|
|
type="text"
|
|
className={styles.input}
|
|
value="India"
|
|
readOnly
|
|
style={{ opacity: 0.7, cursor: 'not-allowed', textAlign: 'center' }}
|
|
/>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>Country (Locked)</label>
|
|
</div>
|
|
|
|
{/* Compliance Section (Optional) */}
|
|
<div className={styles.formGroup}>
|
|
<select
|
|
className={styles.select}
|
|
value={complianceType}
|
|
onChange={e => {
|
|
setComplianceType(e.target.value);
|
|
setComplianceId(""); // Reset ID on type change
|
|
}}
|
|
>
|
|
<option value="">No Compliance (Optional)</option>
|
|
<option value="msme">MSME Udyam</option>
|
|
<option value="gstin">GSTIN</option>
|
|
<option value="cin">CIN</option>
|
|
</select>
|
|
<label className={styles.label} style={{ top: '4px', fontSize: '0.7rem', color: 'var(--color-gold)' }}>Compliance Type</label>
|
|
</div>
|
|
|
|
<div className={styles.formGroup}>
|
|
<input
|
|
type="text"
|
|
className={styles.input}
|
|
placeholder=" "
|
|
value={complianceId}
|
|
onChange={e => setComplianceId(e.target.value)}
|
|
disabled={!complianceType}
|
|
style={{ opacity: !complianceType ? 0.5 : 1, cursor: !complianceType ? 'not-allowed' : 'text' }}
|
|
/>
|
|
<label className={styles.label}>
|
|
{complianceType ? 'Registration Number' : 'ID Number'}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div className={styles.actions}>
|
|
<button type="button" className={styles.backBtn} onClick={() => setView('selection')}>Back</button>
|
|
<button type="submit" className={styles.submitBtn}>Register Entity</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
)}
|
|
|
|
{/* VIEW 3: JOIN COMPANY */}
|
|
{view === 'join' && (
|
|
<div className={styles.formContainer}>
|
|
<form onSubmit={handleJoinSubmit}>
|
|
<div className={styles.formGroup} style={{ marginBottom: '2rem' }}>
|
|
<input
|
|
type="text"
|
|
className={styles.input}
|
|
placeholder=" "
|
|
value={inviteCode}
|
|
onChange={e => setInviteCode(e.target.value)}
|
|
style={{ letterSpacing: '2px', textAlign: 'center', fontSize: '1.2rem' }}
|
|
required
|
|
/>
|
|
<label className={styles.label} style={{ left: '50%', transform: 'translateX(-50%)' }}>Enter Invitation Code</label>
|
|
</div>
|
|
|
|
<div className={styles.actions} style={{ justifyContent: 'center' }}>
|
|
<button type="button" className={styles.backBtn} onClick={() => setView('selection')}>Back</button>
|
|
<button type="submit" className={styles.submitBtn}>Verify & Join</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
)}
|
|
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|