first commit

This commit is contained in:
Kapil Panwar
2025-12-22 07:29:45 +05:30
commit e38835f7cb
110 changed files with 28526 additions and 0 deletions

360
src/app/onboarding/page.tsx Normal file
View File

@@ -0,0 +1,360 @@
'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>
);
}