Browse Source

Initial commit - React project

main
Amol 2 weeks ago
commit
cf900f748e
25 changed files with 3753 additions and 0 deletions
  1. +24
    -0
      .gitignore
  2. +116
    -0
      App.tsx
  3. +101
    -0
      Components/AIAdvisor.tsx
  4. +119
    -0
      Components/QuoteCard.tsx
  5. +58
    -0
      Components/WhyChooseUs.tsx
  6. +20
    -0
      README.md
  7. +34
    -0
      index.html
  8. +38
    -0
      index.tsx
  9. +7
    -0
      metadata.json
  10. +1912
    -0
      package-lock.json
  11. +24
    -0
      package.json
  12. +70
    -0
      src/BasicDetails/BenefitsSidebar.tsx
  13. +171
    -0
      src/BasicDetails/QuoteForm.tsx
  14. +84
    -0
      src/BasicDetails/index.tsx
  15. +8
    -0
      src/DataVariable/dataVariable.tsx
  16. +65
    -0
      src/Header/Header.tsx
  17. +200
    -0
      src/KYCDetails/ProposalPage.tsx
  18. +247
    -0
      src/Quotation/QuoteListPage.tsx
  19. +43
    -0
      src/Routes/index.tsx
  20. +257
    -0
      src/VehicleDetails/CheckoutPage.tsx
  21. +0
    -0
      src/index.tsx
  22. +27
    -0
      src/services/geminiService.ts
  23. +29
    -0
      tsconfig.json
  24. +76
    -0
      types.ts
  25. +23
    -0
      vite.config.ts

+ 24
- 0
.gitignore View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

+ 116
- 0
App.tsx View File

@ -0,0 +1,116 @@
import React, { useState } from 'react';
import Header from './src/Header/Header';
import BenefitsSidebar from './src/BasicDetails/BenefitsSidebar';
import QuoteForm from './src/BasicDetails/QuoteForm';
import AIAdvisor from './Components/AIAdvisor';
import WhyChooseUs from './Components/WhyChooseUs';
import { FormData, InsuranceQuote, VehicleInfo } from './types';
import QuoteListPage from './src/Quotation/QuoteListPage';
import ProposalPage from './src/KYCDetails/ProposalPage';
import CheckoutPage from './src/VehicleDetails/CheckoutPage';
import BasicDetails from './src/BasicDetails';
const App: React.FC = () => {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState<FormData>({
registrationNumber: '',
previousPolicyNumber: '',
expiryDate: '',
coverType: 'comprehensive',
claimStatus: 'no',
ncb: 0,
});
const [selectedQuote, setSelectedQuote] = useState<InsuranceQuote | null>(null);
const vehicleInfo: VehicleInfo = {
model: 'H NESS CB350 DLX2 DUALTONE',
make: 'HONDA MOTORCYCLE AND SCOOTER INDIA (P) LTD',
fuelType: 'PETROL',
variant: 'HIGHNESS CB 350 DLX 2',
registeredCity: 'R.T.O.BORIVALI, Maharashtra',
registeredDate: '01-Feb-2021',
chassisNumber: 'ME4NC586AMA005171',
engineNumber: 'NC58EA1008396',
previousPolicyNumber: '2546754321',
previousInsurer: 'TATA General Insurance',
previousPolicyEndDate: '2025-07-31',
regNo: formData.registrationNumber || 'MH47AX9310'
};
const handleFormChange = (updates: Partial<FormData>) => {
setFormData((prev) => ({ ...prev, ...updates }));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setStep(2);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleBuyNow = (quote: InsuranceQuote) => {
setSelectedQuote(quote);
setStep(3);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleKycComplete = () => {
setStep(4);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const userProfile = {
name: 'Akanksha Sahai Srivastava',
email: 'akshri.inv@gmail.com',
avatarLetter: 'A'
};
return (
<div className="min-h-screen flex flex-col bg-[#f8fafc]">
<Header user={step >= 2 ? userProfile : undefined} />
{step === 1 && (
<BasicDetails
formData={formData}
onChange={handleFormChange}
onSubmit={handleSubmit}
/>
)}
{step === 2 && (
<QuoteListPage onBuyNow={handleBuyNow} />
)}
{step === 3 && selectedQuote && (
<ProposalPage quote={selectedQuote} vehicle={vehicleInfo} onNext={handleKycComplete} />
)}
{step === 4 && selectedQuote && (
<CheckoutPage quote={selectedQuote} vehicle={vehicleInfo} />
)}
<footer className="bg-white py-12 border-t border-slate-100 mt-auto">
<div className="container mx-auto px-4 text-center">
<div className="flex justify-center mb-6 opacity-30 grayscale">
<div className="flex flex-col leading-none text-left">
<span className="text-xl font-bold" style={{ color: '#1a2b4b', fontFamily: 'serif' }}>nivesh</span>
<span className="text-[8px] font-bold tracking-[0.2em] uppercase" style={{ color: '#1a2b4b' }}>Insurance</span>
</div>
</div>
<div className="max-w-3xl mx-auto space-y-3">
<p className="text-slate-500 text-[10px] font-medium leading-relaxed">
A wholly owned subsidiary of Providential Platforms Private Limited (<a href="https://www.nivesh.com" target="_blank" rel="noopener noreferrer" className="text-[#e31e24] hover:underline">https://www.nivesh.com</a>)
</p>
<p className="text-slate-400 text-[10px] font-medium leading-relaxed">
Nivesh Insurance BROKERS PRIVATE LIMITED is a Direct Broker (Life & General) registered by IRDAI vide Registration Code IRDA/DB856/21, Certificate of Registration No. 769 valid upto 02-09-2027.
</p>
</div>
</div>
</footer>
</div>
);
};
export default App;

+ 101
- 0
Components/AIAdvisor.tsx View File

@ -0,0 +1,101 @@
import React, { useEffect, useState, useRef } from 'react';
import { GoogleGenAI } from "@google/genai";
import { FormData } from '../types';
interface AIAdvisorProps {
formData: FormData;
}
const AIAdvisor: React.FC<AIAdvisorProps> = ({ formData }) => {
// New pitchable initial line
const [advice, setAdvice] = useState<string>("Smart recommendations tailored to your vehicle's profile.");
const [isLoading, setIsLoading] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
const getExpertAdvice = async (data: FormData) => {
if (!data.registrationNumber && data.ncb === 0 && data.claimStatus === 'no') return;
setIsLoading(true);
if (abortControllerRef.current) abortControllerRef.current.abort();
abortControllerRef.current = new AbortController();
try {
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY || '' });
const prompt = `
You are a Senior Insurance Underwriter at Nivesh Insurance.
Analyze this 2-wheeler insurance profile and provide ONE professional, concise guidance tip (max 15 words).
Profile Details:
- Vehicle Reg: ${data.registrationNumber || 'Not provided'}
- Previous Claim: ${data.claimStatus}
- Current No Claim Bonus: ${data.ncb}%
- Chosen Cover: ${data.coverType}
Focus on technical insurance advice like NCB protection, Zero-Dep value, or IDV optimization.
Do NOT mention "AI", "Algorithms", or "Premium Discounts". Use an authoritative, professional tone.
`;
const response = await ai.models.generateContent({
model: 'gemini-3-flash-preview',
contents: prompt,
config: {
temperature: 0.6,
maxOutputTokens: 60,
}
});
const text = response.text;
if (text) {
setAdvice(text.trim());
}
} catch (error: any) {
if (error.name !== 'AbortError') {
setAdvice("Maintain a consistent insurance history for seamless future claim processing.");
}
} finally {
setIsLoading(false);
}
};
useEffect(() => {
const handler = setTimeout(() => {
getExpertAdvice(formData);
}, 1200);
return () => clearTimeout(handler);
}, [formData.registrationNumber, formData.claimStatus, formData.ncb, formData.coverType]);
return (
<div className="bg-white border-2 border-slate-100 rounded-3xl p-6 flex gap-5 items-center relative overflow-hidden group hover:border-[#1a2b4b]/20 transition-all duration-500 shadow-sm">
<div className="absolute top-0 right-0 -mr-4 -mt-4 w-24 h-24 bg-[#1a2b4b]/5 rounded-full blur-2xl group-hover:bg-[#e31e24]/5 transition-colors duration-500"></div>
<div className="flex-shrink-0 relative">
<div className={`w-12 h-12 bg-slate-50 border border-slate-100 rounded-2xl flex items-center justify-center shadow-inner transition-transform duration-500 ${isLoading ? 'scale-95' : 'group-hover:scale-105'}`}>
<svg className={`w-6 h-6 ${isLoading ? 'text-[#e31e24] animate-pulse' : 'text-[#1a2b4b]'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
</div>
</div>
<div className="flex-grow">
<div className="flex items-center gap-2 mb-1">
<h4 className="text-[10px] font-black text-[#e31e24] uppercase tracking-[0.2em]">Smart Insight</h4>
{isLoading && <span className="text-[8px] font-bold text-slate-400 animate-pulse italic">Optimizing...</span>}
</div>
<p className={`text-sm font-semibold text-[#1a2b4b] leading-snug transition-all duration-500 ${isLoading ? 'opacity-40' : 'opacity-100'}`}>
{advice}
</p>
</div>
<div className="hidden md:block flex-shrink-0 ml-2">
<div className="px-3 py-1 bg-[#1a2b4b]/5 rounded-full border border-[#1a2b4b]/10">
<span className="text-[9px] font-black text-[#1a2b4b]">PROTECT</span>
</div>
</div>
</div>
);
};
export default AIAdvisor;

+ 119
- 0
Components/QuoteCard.tsx View File

@ -0,0 +1,119 @@
import React, { useState } from 'react';
import { InsuranceQuote } from '../types';
interface QuoteCardProps {
quote: InsuranceQuote;
onBuy?: () => void;
}
const QuoteCard: React.FC<QuoteCardProps> = ({ quote, onBuy }) => {
const [isExpanded, setIsExpanded] = useState(false);
// Specific features to highlight on the front line
const highlightFeatures = quote.features.filter(f =>
['PERSONAL_ACCIDENT_COVER', 'ROAD_SIDE_ASSISTANCE', 'ZERO_DEPRECIATION', 'ENGINE_COVER', 'Tax'].includes(f.label)
).slice(0, 4); // Keep only one line worth of features
return (
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow overflow-hidden mb-4">
{/* Top Row: Insurer & Pricing */}
<div className="p-5 flex flex-col md:flex-row items-center gap-6">
{/* Insurer Logo & Name */}
<div className="flex items-center gap-4 w-full md:w-1/3">
<div className="w-14 h-14 bg-white rounded-lg p-1 flex items-center justify-center border border-slate-100 flex-shrink-0">
<img src={quote.insurerLogo} alt={quote.insurerName} className="max-h-full max-w-full object-contain" />
</div>
<div>
<h3 className="text-[#1a2b4b] font-bold text-sm leading-tight line-clamp-2">{quote.insurerName}</h3>
<div className="flex items-center gap-2 mt-1">
<span className="bg-emerald-50 text-emerald-600 text-[9px] font-black px-1.5 py-0.5 rounded uppercase border border-emerald-100">Quick Approval</span>
</div>
</div>
</div>
{/* IDV Value Display */}
<div className="flex-1 flex items-center justify-center md:border-x border-slate-100">
<div className="text-center md:px-4">
<p className="text-[10px] text-slate-400 uppercase font-black tracking-widest mb-0.5">IDV Value</p>
<p className="text-sm font-bold text-[#1a2b4b]"> {quote.idvValue.toLocaleString()}</p>
</div>
</div>
{/* Action & Price */}
<div className="flex items-center gap-4 w-full md:w-auto justify-between md:justify-end">
<div className="text-right">
<p className="text-[10px] text-slate-400 font-bold line-through"> {(quote.premium + 150).toLocaleString()}</p>
<p className="text-2xl font-black text-[#1a2b4b] leading-none">
<span className="text-xs font-bold mr-0.5"></span>
{quote.premium.toLocaleString()}
</p>
</div>
<button
onClick={onBuy}
className="bg-[#2b458c] hover:bg-[#1a2b4b] text-white px-8 py-3 rounded-xl font-black text-sm transition-all shadow-lg shadow-[#2b458c]/10"
>
BUY NOW
</button>
</div>
</div>
{/* NEW SECTION: Key features in one line below Buy Now */}
<div className="px-5 pb-4">
<div className="flex flex-wrap gap-4 items-center">
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest mr-2">Key Benefits:</span>
{highlightFeatures.map((f, i) => (
<div key={i} className="flex items-center gap-1.5">
<svg className="w-3 h-3 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7" /></svg>
<span className="text-[10px] text-[#1a2b4b] font-bold">
{f.label.replace(/_/g, ' ')}: <span className="text-slate-500 font-black">{typeof f.value === 'number' && f.value > 1000 ? `${(f.value/100000).toFixed(1)}L` : f.value}</span>
</span>
</div>
))}
</div>
</div>
{/* Expand/Collapse Toggle Bar */}
<button
onClick={() => setIsExpanded(!isExpanded)}
className="w-full py-2 bg-slate-50/50 border-t border-slate-50 flex items-center justify-center gap-2 hover:bg-slate-50 transition-colors"
>
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">
{isExpanded ? 'Hide Details' : 'View All Policy Features'}
</span>
<svg
className={`w-3 h-3 text-slate-400 transition-transform duration-300 ${isExpanded ? 'rotate-180' : ''}`}
fill="none" stroke="currentColor" viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Expanded Content */}
{isExpanded && (
<div className="p-6 bg-white border-t border-slate-100 animate-in fade-in slide-in-from-top-2 duration-300">
<h4 className="text-[#1a2b4b] text-[10px] font-black uppercase tracking-[0.2em] mb-4 opacity-50">Detailed Coverage & Breakdown</h4>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-2">
{quote.features.map((feature, idx) => (
<div key={idx} className="flex items-center justify-between gap-2 text-[10px] py-1 border-b border-slate-50 last:border-0">
<div className="flex items-center gap-2 overflow-hidden">
<svg className="w-3 h-3 text-emerald-500 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7" />
</svg>
<span className="text-slate-500 font-bold uppercase tracking-tight truncate">{feature.label.replace(/_/g, ' ')}</span>
</div>
<span className="text-[#1a2b4b] font-black flex-shrink-0">
{feature.value === true || feature.value === 'True' || feature.value === 'Included' || feature.value === 'Yes' ? 'Yes' :
feature.value === false || feature.value === 'False' || feature.value === 'No' ? 'No' :
feature.value}
</span>
</div>
))}
</div>
</div>
)}
</div>
);
};
export default QuoteCard;

+ 58
- 0
Components/WhyChooseUs.tsx View File

@ -0,0 +1,58 @@
import React, { useState, useEffect } from 'react';
const WhyChooseUs: React.FC = () => {
const points = [
{ title: "99.2% Settlement", subtitle: "Industry leading claim record", icon: "🏆" },
{ title: "24/7 Support", subtitle: "Round the clock assistance", icon: "📞" },
{ title: "Instant Policy", subtitle: "Buy and download in 2 minutes", icon: "⚡" },
{ title: "Paperless Claims", subtitle: "Fully digital hassle-free process", icon: "📲" },
{ title: "Lowest Prices", subtitle: "Best quotes guaranteed", icon: "💰" }
];
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % points.length);
}, 3000);
return () => clearInterval(timer);
}, []);
return (
<div className="bg-[#f1f5f9] rounded-3xl p-6 border border-slate-200 overflow-hidden relative">
<div className="flex items-center justify-between mb-4">
<h3 className="text-[#1a2b4b] text-xs font-black uppercase tracking-widest">Why Choose Us?</h3>
<div className="flex gap-1">
{points.map((_, i) => (
<div
key={i}
className={`w-1.5 h-1.5 rounded-full transition-all duration-300 ${i === currentIndex ? 'bg-[#e31e24] w-4' : 'bg-slate-300'}`}
/>
))}
</div>
</div>
<div className="relative h-16">
{points.map((point, idx) => (
<div
key={idx}
className={`absolute inset-0 flex items-center gap-4 transition-all duration-700 ease-in-out ${
idx === currentIndex ? 'opacity-100 translate-x-0' : 'opacity-0 translate-x-8 pointer-events-none'
}`}
>
<div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center text-2xl shadow-sm border border-slate-100">
{point.icon}
</div>
<div>
<h4 className="text-[#1a2b4b] font-bold text-sm leading-none mb-1">{point.title}</h4>
<p className="text-slate-500 text-[11px] font-medium">{point.subtitle}</p>
</div>
</div>
))}
</div>
</div>
);
};
export default WhyChooseUs;

+ 20
- 0
README.md View File

@ -0,0 +1,20 @@
<div align="center">
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
</div>
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
View your app in AI Studio: https://ai.studio/apps/drive/13U7FfFtZi6YY5N1GH86-GpTd7TFEKQyc
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

+ 34
- 0
index.html View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nivesh Insurance | 2-Wheeler Quote</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc;
}
</style>
<script type="importmap">
{
"imports": {
"@google/genai": "https://esm.sh/@google/genai@^1.34.0",
"react/": "https://esm.sh/react@^19.2.3/",
"react": "https://esm.sh/react@^19.2.3",
"react-dom/": "https://esm.sh/react-dom@^19.2.3/"
}
}
</script>
<link rel="stylesheet" href="/index.css">
</head>
<body>
<div id="root"></div>
<script type="module" src="/index.tsx"></script>
</body>
</html>

+ 38
- 0
index.tsx View File

@ -0,0 +1,38 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import PageRoutes from './src/Routes';
import Header from './src/Header/Header';
const rootElement = document.getElementById('root');
if (!rootElement) {
throw new Error("Could not find root element to mount to");
}
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
{/* <App /> */}
<Header />
<PageRoutes />
<footer className="bg-white py-12 border-t border-slate-100 mt-auto">
<div className="container mx-auto px-4 text-center">
<div className="flex justify-center mb-6 opacity-30 grayscale">
<div className="flex flex-col leading-none text-left">
<span className="text-xl font-bold" style={{ color: '#1a2b4b', fontFamily: 'serif' }}>nivesh</span>
<span className="text-[8px] font-bold tracking-[0.2em] uppercase" style={{ color: '#1a2b4b' }}>Insurance</span>
</div>
</div>
<div className="max-w-3xl mx-auto space-y-3">
<p className="text-slate-500 text-[10px] font-medium leading-relaxed">
A wholly owned subsidiary of Providential Platforms Private Limited (<a href="https://www.nivesh.com" target="_blank" rel="noopener noreferrer" className="text-[#e31e24] hover:underline">https://www.nivesh.com</a>)
</p>
<p className="text-slate-400 text-[10px] font-medium leading-relaxed">
Nivesh Insurance BROKERS PRIVATE LIMITED is a Direct Broker (Life & General) registered by IRDAI vide Registration Code IRDA/DB856/21, Certificate of Registration No. 769 valid upto 02-09-2027.
</p>
</div>
</div>
</footer>
</React.StrictMode>
);

+ 7
- 0
metadata.json View File

@ -0,0 +1,7 @@
{
"name": "Nivesh Insurance - 2-Wheeler full journey",
"description": "A modern, high-conversion insurance quote generation landing page for two-wheeler insurance, featuring AI-assisted guidance and a clean, user-friendly interface.",
"requestFramePermissions": [
"camera"
]
}

+ 1912
- 0
package-lock.json
File diff suppressed because it is too large
View File


+ 24
- 0
package.json View File

@ -0,0 +1,24 @@
{
"name": "nivesh-insurance---2-wheeler-full-journey",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"start": "react-scripts start"
},
"dependencies": {
"@google/genai": "^1.34.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"react-router-dom": "^7.12.0"
},
"devDependencies": {
"@types/node": "^22.14.0",
"@vitejs/plugin-react": "^5.0.0",
"typescript": "~5.8.2",
"vite": "^6.2.0"
}
}

+ 70
- 0
src/BasicDetails/BenefitsSidebar.tsx View File

@ -0,0 +1,70 @@
import React from 'react';
const BenefitsSidebar: React.FC = () => {
const coveragePoints = [
"Damage due to accident of carrying truck / conveyance",
"Fire, lightening or explosion",
"Theft / pilferage",
"Loading and unloading damage",
"Malicious damage",
"Overturning or Derailment",
"Breakage of bridge",
"Collision",
"Damage to the goods / cargo due to sea / river / lake water entering inside the vehicle"
];
return (
<div className="space-y-4">
<div className="bg-[#f8f9fb] border border-slate-200 rounded-3xl p-6 shadow-sm">
<h3 className="text-[#1a2b4b] text-lg font-bold mb-6 flex items-center gap-2">
<span className="w-1.5 h-6 bg-[#e31e24] rounded-full"></span>
Standard Risks Covered
</h3>
<div className="space-y-4">
{coveragePoints.map((point, idx) => (
<div key={idx} className="flex gap-3 items-start group">
<div className="flex-shrink-0 mt-0.5">
<div className="w-5 h-5 rounded-full bg-emerald-50 flex items-center justify-center border border-emerald-100 group-hover:bg-emerald-500 group-hover:border-emerald-500 transition-colors duration-300">
<svg
className="w-2.5 h-2.5 text-emerald-600 group-hover:text-white transition-colors duration-300"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="4" d="M5 13l4 4L19 7" />
</svg>
</div>
</div>
<p className="text-[12px] font-medium text-[#1a2b4b]/80 leading-snug">
{point}
</p>
</div>
))}
</div>
<div className="mt-8 pt-6 border-t border-slate-200">
<div className="flex items-center justify-between p-3 bg-white rounded-2xl border border-slate-100">
<div className="flex items-center gap-2">
<div className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse"></div>
<span className="text-[10px] font-bold text-slate-500 uppercase">Settlement Ratio</span>
</div>
<span className="text-sm font-black text-[#1a2b4b]">99.2%</span>
</div>
</div>
</div>
<div className="p-6 rounded-3xl bg-gradient-to-br from-[#1a2b4b] to-[#253b66] text-white relative overflow-hidden group">
<div className="absolute top-[-10px] right-[-10px] w-24 h-24 bg-white/5 rounded-full blur-2xl group-hover:bg-[#e31e24]/10 transition-all duration-500"></div>
<h4 className="text-sm font-bold mb-2 text-white/90 uppercase tracking-widest">Instant Support</h4>
<p className="text-xs text-white/70 mb-4">Questions about coverage? Our experts are here to help you choose.</p>
<button className="w-full py-3 bg-[#e31e24] hover:bg-[#c41a1f] text-white rounded-xl text-xs font-bold transition-all shadow-lg shadow-black/20">
Talk to an Expert
</button>
</div>
</div>
);
};
export default BenefitsSidebar;

+ 171
- 0
src/BasicDetails/QuoteForm.tsx View File

@ -0,0 +1,171 @@
import React from 'react';
import { FormData } from '@/types';
import { useNavigate } from 'react-router-dom';
interface QuoteFormProps {
data: FormData;
onChange: (updates: Partial<FormData>) => void;
onSubmit: (e: React.FormEvent) => void;
}
const QuoteForm: React.FC<QuoteFormProps> = ({ data, onChange, onSubmit }) => {
console.log("SKJDHJSHDSJD", data);
const navigate = useNavigate()
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit(e);
// navigate("/quote-list");
};
const handleRedirection = () => {
if (data.registrationNumber || data.expiryDate) {
navigate("/quote-list");
}
};
return (
<form onSubmit={handleSubmit} className="space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6">
{/* Registration Number */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400">
Vehicle Registration Number <span className="text-[#e31e24]">*</span>
</label>
<input
type="text"
placeholder="MH 12 AB 1234"
className="w-full px-5 py-4 rounded-2xl border-2 border-slate-100 bg-white focus:border-[#e31e24] focus:ring-4 focus:ring-[#e31e24]/5 outline-none transition-all placeholder:text-slate-300 uppercase font-bold text-[#1a2b4b]"
value={data.registrationNumber}
onChange={(e) => onChange({ registrationNumber: e.target.value })}
required
/>
</div>
{/* Previous Policy Number */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400">
Previous Policy Number
</label>
<input
type="text"
placeholder="Optional"
className="w-full px-5 py-4 rounded-2xl border-2 border-slate-100 bg-white focus:border-[#1a2b4b] focus:ring-4 focus:ring-[#1a2b4b]/5 outline-none transition-all placeholder:text-slate-300 text-[#1a2b4b]"
value={data.previousPolicyNumber}
onChange={(e) => onChange({ previousPolicyNumber: e.target.value })}
/>
</div>
{/* Expiry Date */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400">
Expiry Date <span className="text-[#e31e24]">*</span>
</label>
<input
type="date"
className="w-full px-5 py-4 rounded-2xl border-2 border-slate-100 bg-white focus:border-[#1a2b4b] focus:ring-4 focus:ring-[#1a2b4b]/5 outline-none transition-all font-medium text-[#1a2b4b]"
value={data.expiryDate}
onChange={(e) => onChange({ expiryDate: e.target.value })}
required
/>
</div>
{/* Cover Type */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400">
Cover Type
</label>
<div className="relative">
<select
className="w-full px-5 py-4 rounded-2xl border-2 border-slate-100 focus:border-[#1a2b4b] focus:ring-4 focus:ring-[#1a2b4b]/5 outline-none transition-all bg-white appearance-none cursor-pointer font-medium text-[#1a2b4b]"
value={data.coverType}
onChange={(e) => onChange({ coverType: e.target.value as any })}
>
<option value="comprehensive">Comprehensive Cover</option>
<option value="third-party">Third Party Only</option>
<option value="own-damage">Standalone Own Damage</option>
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none text-slate-400">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
</div>
</div>
</div>
{/* Claim Status */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400">
Claimed Last Year?
</label>
<div className="flex gap-3 p-1.5 bg-slate-50 rounded-2xl border border-slate-100">
<button
type="button"
className={`flex-1 py-3 rounded-xl font-bold text-sm transition-all ${data.claimStatus === 'no'
? 'bg-white text-[#e31e24] shadow-sm'
: 'text-slate-400 hover:text-slate-600'
}`}
onClick={() => onChange({ claimStatus: 'no' })}
>
No
</button>
<button
type="button"
className={`flex-1 py-3 rounded-xl font-bold text-sm transition-all ${data.claimStatus === 'yes'
? 'bg-white text-[#e31e24] shadow-sm'
: 'text-slate-400 hover:text-slate-600'
}`}
onClick={() => onChange({ claimStatus: 'yes' })}
>
Yes
</button>
</div>
</div>
{/* NCB Percentage */}
<div className="space-y-2">
<label className="text-xs font-black uppercase tracking-widest text-slate-400 flex justify-between">
No Claim Bonus %
<span className="text-[#e31e24] font-black">SAVE UP TO 50%</span>
</label>
<div className="relative">
<select
className="w-full px-5 py-4 rounded-2xl border-2 border-slate-100 focus:border-[#1a2b4b] focus:ring-4 focus:ring-[#1a2b4b]/5 outline-none transition-all bg-white appearance-none cursor-pointer font-bold text-[#1a2b4b]"
value={data.ncb}
onChange={(e) => onChange({ ncb: parseInt(e.target.value) })}
disabled={data.claimStatus === 'yes'}
>
<option value="0">0% (New Policy)</option>
<option value="20">20%</option>
<option value="25">25%</option>
<option value="35">35%</option>
<option value="45">45%</option>
<option value="50">50%</option>
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none text-slate-400">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
</div>
</div>
</div>
</div>
<div className="pt-6 border-t border-slate-50">
<button
type="submit"
className="w-full bg-[#e31e24] hover:bg-[#c41a1f] text-white font-black py-5 px-8 rounded-2xl shadow-xl shadow-[#e31e24]/20 hover:shadow-[#e31e24]/30 hover:-translate-y-0.5 transition-all flex items-center justify-center gap-3 text-lg"
// onClick={() => navigate("/quote-list")}
onClick={() => handleRedirection()}
>
Check Live Prices
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
</button>
</div>
</form>
);
};
export default QuoteForm;

+ 84
- 0
src/BasicDetails/index.tsx View File

@ -0,0 +1,84 @@
import AIAdvisor from '@/Components/AIAdvisor';
import WhyChooseUs from '@/Components/WhyChooseUs';
import React, { useState } from 'react';
import QuoteForm from './QuoteForm';
import BenefitsSidebar from './BenefitsSidebar';
import { FormData } from '@/types';
const BasicDetails = (props) => {
const { handleSubmit } = props;
const [formData, setFormData] = useState<FormData>({
registrationNumber: '',
previousPolicyNumber: '',
expiryDate: '',
coverType: 'comprehensive',
claimStatus: 'no',
ncb: 0,
});
const handleFormChange = (updates: Partial<FormData>) => {
setFormData((prev) => ({ ...prev, ...updates }));
};
return (
<main className="flex-grow container mx-auto px-4 py-8 lg:py-16">
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-12 gap-8 items-start">
<aside className="lg:col-span-4 space-y-6 lg:sticky lg:top-28">
<div className="mb-6">
<h1 className="text-3xl font-bold text-[#1a2b4b] leading-tight mb-2">
Protect your <br /><span className="text-[#e31e24]">2-Wheeler</span> today
</h1>
<p className="text-slate-500 text-sm">
Join 2M+ riders who trust Nivesh for their journeys.
</p>
</div>
<BenefitsSidebar />
</aside>
<div className="lg:col-span-8 space-y-8">
<div className="bg-white rounded-3xl shadow-[0_10px_40px_-15px_rgba(0,0,0,0.08)] border border-slate-100 overflow-hidden">
<div className="p-8 lg:p-10">
<div className="flex items-center justify-between mb-8">
<h2 className="text-2xl font-bold text-[#1a2b4b]">
Get a Quick Quote
</h2>
<div className="text-[10px] font-black text-emerald-600 bg-emerald-50 px-3 py-1 rounded-full uppercase tracking-widest border border-emerald-100">
Live Prices
</div>
</div>
<QuoteForm
data={formData}
onChange={handleFormChange}
onSubmit={handleSubmit}
/>
</div>
<div className="bg-[#1a2b4b] p-5 text-center">
<p className="text-[11px] text-white/80 font-bold uppercase tracking-widest flex items-center justify-center gap-2">
<svg className="w-4 h-4 text-emerald-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
IRDAI Certified Insurance Broker
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<AIAdvisor
formData={formData}
/>
<WhyChooseUs />
</div>
</div>
</div>
</div>
</main>
);
};
export default BasicDetails;

+ 8
- 0
src/DataVariable/dataVariable.tsx View File

@ -0,0 +1,8 @@
export const formFileds = {
registrationNumber: '',
previousPolicyNumber: '',
expiryDate: '',
coverType: 'comprehensive',
claimStatus: 'no',
ncb: 0,
}

+ 65
- 0
src/Header/Header.tsx View File

@ -0,0 +1,65 @@
import React from 'react';
interface HeaderProps {
user?: {
name: string;
email: string;
avatarLetter: string;
};
}
const Header: React.FC<HeaderProps> = ({ user }) => {
return (
<header className="bg-white border-b border-slate-100 sticky top-0 z-50">
<div className="container mx-auto px-4 h-20 flex items-center justify-between">
<div className="flex items-center gap-2">
{/* Brand Logo Recreation */}
<div className="flex flex-col leading-none">
<span className="text-3xl font-bold tracking-tight" style={{ color: '#e31e24', fontFamily: 'serif' }}>
nivesh
</span>
<span className="text-[11px] font-bold tracking-[0.2em] uppercase mt-[-2px]" style={{ color: '#1a2b4b' }}>
Insurance
</span>
</div>
</div>
<nav className="hidden md:flex items-center gap-10">
<div className="relative group">
<a href="#" className="text-[#1a2b4b] font-medium transition-colors flex items-center gap-1">
Insurance
</a>
</div>
<a href="#" className="text-slate-600 hover:text-[#e31e24] font-medium transition-colors">Insurance Advisors</a>
<div className="relative group">
<a href="#" className="text-slate-600 hover:text-[#e31e24] font-medium transition-colors flex items-center gap-1">
Support
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
</a>
</div>
</nav>
<div className="flex items-center gap-4">
{user ? (
<div className="flex items-center gap-4 bg-slate-50 border border-slate-100 rounded-xl px-4 py-2">
<div className="text-right hidden sm:block">
<p className="text-xs font-bold text-[#1a2b4b]">{user.name}</p>
<p className="text-[10px] text-slate-400">{user.email}</p>
</div>
<div className="w-10 h-10 rounded-full bg-indigo-600 flex items-center justify-center text-white font-bold text-lg shadow-sm border-2 border-white">
{user.avatarLetter}
</div>
</div>
) : (
<button className="bg-[#1a2b4b] text-white px-5 py-2 rounded-lg font-semibold text-sm hover:bg-[#253b66] transition-all">
My Account
</button>
)}
</div>
</div>
</header>
);
};
export default Header;

+ 200
- 0
src/KYCDetails/ProposalPage.tsx View File

@ -0,0 +1,200 @@
import React, { useState } from 'react';
import { InsuranceQuote, VehicleInfo, ProposalData } from '@/types';
const ProposalPage: React.FC = () => {
const [proposal, setProposal] = useState<ProposalData>({
pan: 'AATPU7693N',
name: 'Suketu Upadhyay',
dob: '1984-07-03',
phone: '9833604121',
email: 'Suketu123_Upadhyay@hotmail.com'
});
const quote: InsuranceQuote = {
id: 'QUOTE123',
insurerName: 'TATA General Insurance',
insurerLogo: 'https://via.placeholder.com/100x40.png?text=TATA',
premium: 5000,
idvValue: 120000,
features: ['24x7 Support', 'Roadside Assistance']
};
const vehicle: VehicleInfo = {
model: 'H NESS CB350 DLX2 DUALTONE',
make: 'HONDA MOTORCYCLE AND SCOOTER INDIA (P) LTD',
fuelType: 'PETROL',
variant: 'HIGHNESS CB 350 DLX 2',
registeredCity: 'R.T.O.BORIVALI, Maharashtra',
registeredDate: '01-Feb-2021',
chassisNumber: 'ME4NC586AMA005171',
engineNumber: 'NC58EA1008396',
previousPolicyNumber: '2546754321',
previousInsurer: 'TATA General Insurance',
previousPolicyEndDate: '2025-07-31',
regNo: 'MH47AX9310'
};
const tax = Math.round(quote.premium * 0.18);
const basePrice = quote.premium;
const total = basePrice + tax;
return (
<div className="container mx-auto px-4 py-8 space-y-6 animate-in fade-in duration-500">
{/* 1. Vehicle Details Header */}
<div className="bg-[#1a2b4b] text-white rounded-2xl shadow-xl overflow-hidden relative border border-white/10">
<div className="p-6 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-x-8 gap-y-6">
<HeaderDetail label="MODEL:" value={vehicle.model} />
<HeaderDetail label="MAKE:" value={vehicle.make} />
<HeaderDetail label="FUEL_TYPE:" value={vehicle.fuelType} />
<HeaderDetail label="VARIANT:" value={vehicle.variant} />
<HeaderDetail label="REGISTERED_CITY:" value={vehicle.registeredCity} />
<HeaderDetail label="REGISTERED_DATE:" value={vehicle.registeredDate} />
<HeaderDetail label="CHASSIS_NUMBER:" value={vehicle.chassisNumber} />
<HeaderDetail label="ENGINE_NUMBER:" value={vehicle.engineNumber} />
<HeaderDetail label="PREVIOUS_INSURER:" value={vehicle.previousInsurer} />
<HeaderDetail label="PREVIOUS_POLICY_NUMBER:" value={vehicle.previousPolicyNumber} />
<HeaderDetail label="PREVIOUS_POLICY_END_DATE:" value={vehicle.previousPolicyEndDate} />
<HeaderDetail label="REGISTRATION_NO:" value={vehicle.regNo} />
</div>
</div>
<div className="flex flex-col lg:flex-row gap-8">
{/* 2. KYC Information Form (Left) */}
<div className="lg:w-2/3 bg-white rounded-3xl p-8 border border-slate-200 shadow-sm self-start">
<h3 className="text-lg font-black text-[#1a2b4b] mb-6 flex items-center gap-2">
Owner Information
<span className="w-1.5 h-1.5 rounded-full bg-[#2b458c]"></span>
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6">
<div className="space-y-2">
<label className="text-xs font-bold text-slate-400">Pan *</label>
<input
type="text"
value={proposal.pan}
onChange={e => setProposal({ ...proposal, pan: e.target.value })}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
<div className="space-y-2">
<label className="text-xs font-bold text-slate-400">Name *</label>
<input
type="text"
value={proposal.name}
onChange={e => setProposal({ ...proposal, name: e.target.value })}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
<div className="space-y-2">
<label className="text-xs font-bold text-slate-400">Date of Birth *</label>
<input
type="date"
value={proposal.dob}
onChange={e => setProposal({ ...proposal, dob: e.target.value })}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
<div className="space-y-2">
<label className="text-xs font-bold text-slate-400">Phone Number *</label>
<input
type="tel"
value={proposal.phone}
onChange={e => setProposal({ ...proposal, phone: e.target.value })}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
<div className="md:col-span-2 space-y-2">
<label className="text-xs font-bold text-slate-400">Email *</label>
<input
type="email"
value={proposal.email}
onChange={e => setProposal({ ...proposal, email: e.target.value })}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
</div>
<div className="mt-12 flex justify-end">
<button
// onClick={onNext}
className="bg-[#e31e24] hover:bg-[#c41a1f] text-white px-12 py-4 rounded-lg font-bold transition-all shadow-lg hover:-translate-y-0.5"
>
Proceed to KYC
</button>
</div>
</div>
{/* 3. Premium Breakup (Right) */}
<div className="lg:w-1/3">
<div className="bg-white rounded-3xl p-7 border border-slate-200 shadow-sm space-y-8 lg:sticky lg:top-28">
<div className="flex items-center justify-between py-2 border-b border-slate-50">
<div className="flex flex-col">
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest mb-1">Insurance Partner</span>
<p className="text-[11px] font-bold text-[#1a2b4b] max-w-[180px] leading-tight">{quote.insurerName}</p>
</div>
<img src={quote.insurerLogo} alt={quote.insurerName} className="h-10 w-auto object-contain" />
</div>
<div className="space-y-4">
<PriceRow label="BASE PRICE" value={basePrice} />
<PriceRow label="CONVIENCE FEE" value={0} />
<PriceRow label="TAX" value={tax} />
<PriceRow label="PROCESSING FEE" value={0} />
<div className="pt-6 border-t-2 border-slate-100 flex justify-between items-center">
<span className="text-sm font-black text-[#1a2b4b] uppercase tracking-wider">Total</span>
<span className="text-2xl font-black text-[#1a2b4b]">{total.toLocaleString()}</span>
</div>
</div>
<div className="pt-4 text-center">
<p className="text-[10px] text-slate-400 leading-relaxed font-medium">
By clicking on 'Proceed', I agree to the <a href="#" className="text-[#e31e24] font-bold hover:underline">terms & conditions</a>
</p>
</div>
</div>
</div>
</div>
</div>
);
};
// Reusable Components
const HeaderDetail = ({ label, value }: { label: string; value: string }) => (
<div className="flex flex-col gap-0.5">
<span className="text-[9px] font-black text-slate-400 uppercase tracking-widest">{label}</span>
<span className="text-[11px] font-black text-white uppercase break-words leading-tight">{value || '-'}</span>
</div>
);
const PriceRow = ({ label, value }: { label: string; value: number }) => (
<div className="flex justify-between items-center">
<span className="text-[11px] font-bold text-slate-400 uppercase tracking-wide">{label}</span>
<span className="text-sm font-black text-[#1a2b4b]">{value.toLocaleString()}</span>
</div>
);
const FormInput = ({
label,
value,
onChange,
type = 'text',
colSpan = 1
}: {
label: string;
value: string;
onChange: (val: string) => void;
type?: string;
colSpan?: number;
}) => (
<div className={colSpan > 1 ? 'md:col-span-2 space-y-2' : 'space-y-2'}>
<label className="text-xs font-bold text-slate-400">{label}</label>
<input
type={type}
value={value}
onChange={e => onChange(e.target.value)}
className="w-full px-4 py-3.5 bg-white rounded-lg border border-slate-200 focus:ring-2 focus:ring-[#2b458c] font-medium text-[#1a2b4b] outline-none transition-all"
/>
</div>
);
export default ProposalPage;

+ 247
- 0
src/Quotation/QuoteListPage.tsx View File

@ -0,0 +1,247 @@
import React, { useState } from 'react';
import QuoteCard from '@/Components/QuoteCard';
import { InsuranceQuote } from '@/types';
interface QuoteListPageProps {
onBuyNow: (quote: InsuranceQuote) => void;
}
const QuoteListPage: React.FC<QuoteListPageProps> = ({ onBuyNow }) => {
const [isEditingIDV, setIsEditingIDV] = useState(false);
const [idvType, setIdvType] = useState<'lowest' | 'recommended' | 'custom'>('recommended');
const [customIDV, setCustomIDV] = useState(9507);
// Tenure State
const [isEditingTenure, setIsEditingTenure] = useState(false);
const [tenure, setTenure] = useState<'1 Year' | '2 Year' | '3 Year'>('1 Year');
const minIDV = 6914;
const maxIDV = 9507;
const currentIDV = idvType === 'custom' ? customIDV : (idvType === 'lowest' ? minIDV : maxIDV);
const dummyQuotes: InsuranceQuote[] = [
{
id: '1',
insurerName: 'Universal Sompo General Insurance Ltd',
insurerLogo: 'https://companieslogo.com/img/orig/UNIVERSAL_SOMPO.NS-4b7b3d9c.png?t=1659939525',
idvValue: currentIDV,
premium: tenure === '1 Year' ? 2117 : tenure === '2 Year' ? 3850 : 5200,
features: [
{ label: 'PERSONAL_ACCIDENT_COVER', value: 1500000 },
{ label: 'ROAD_SIDE_ASSISTANCE', value: 'Included' },
{ label: 'ELECTRICAL_ACCESSORIES_COVER', value: 0 },
{ label: 'ENGINE_COVER', value: 0 },
{ label: 'KEY_COVER', value: 'No' },
{ label: 'TYRE_COVER', value: 0 },
{ label: 'PER_DAY_CASH', value: 0 },
{ label: 'Convience Fee', value: 0 },
{ label: 'Tax', value: 381 },
{ label: 'Manual Review', value: 'False' },
{ label: 'DEPRECIATION_COVER', value: 'No' },
{ label: 'CONSUMABLES_COVER', value: 'No' },
{ label: 'NON_ELECTRICAL_ACCESSORIES_COVER', value: 0 },
{ label: 'EXTERNAL_CNG_OR_LPG_COVER', value: 'No' },
{ label: 'PERSONAL_BAGGAGE_COVER', value: 0 },
{ label: 'RETURN_TO_INVOICE', value: 0 },
{ label: 'Base Price', value: 2117 },
{ label: 'Processing Fee', value: 0 },
{ label: 'Offer validity', value: 'P15D' }
]
},
{
id: '2',
insurerName: 'Digit Two Wheeler Insurance',
insurerLogo: 'https://cdn.freelogovectors.net/wp-content/uploads/2023/11/go-digit-logo-freelogovectors.net_.png',
idvValue: 9100.00,
premium: 942,
features: [
{ label: 'PERSONAL_ACCIDENT_COVER', value: 1500000 },
{ label: 'ZERO_DEPRECIATION', value: 'Yes' },
{ label: 'Tax', value: 142 },
{ label: 'Base Price', value: 800 }
]
}
];
return (
<div className="container mx-auto px-4 py-6 md:py-10">
<div className="flex flex-col lg:flex-row gap-8">
{/* Left Sidebar Filters */}
<aside className="lg:w-1/3 xl:w-1/4">
<div className="sticky top-28 space-y-6">
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-sm">
<h3 className="text-[#1a2b4b] text-xs font-black uppercase tracking-widest mb-6 flex items-center justify-between">
Modify Quote
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500"></span>
</h3>
<div className="space-y-6">
{/* Tenure */}
<div className="p-3 bg-slate-50 rounded-xl border border-slate-100">
<div className="flex items-center justify-between mb-1">
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Policy Tenure</h4>
{!isEditingTenure && (
<button
onClick={() => setIsEditingTenure(true)}
className="text-[#e31e24] hover:scale-110 transition-transform"
>
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" /></svg>
</button>
)}
</div>
{!isEditingTenure ? (
<p className="text-sm font-bold text-[#1a2b4b]">{tenure}</p>
) : (
<div className="flex flex-col gap-2 mt-2 animate-in fade-in slide-in-from-top-1">
{(['1 Year', '2 Year', '3 Year'] as const).map((t) => (
<button
key={t}
onClick={() => {
setTenure(t);
setIsEditingTenure(false);
}}
className={`w-full text-left px-3 py-2 rounded-lg text-xs font-bold transition-all border ${
tenure === t
? 'bg-white border-[#e31e24] text-[#e31e24] shadow-sm'
: 'bg-slate-100 border-slate-200 text-[#1a2b4b] hover:bg-white'
}`}
>
{t}
</button>
))}
</div>
)}
</div>
{/* IDV Section */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Cover Value (IDV):</h4>
{!isEditingIDV && (
<button
onClick={() => setIsEditingIDV(true)}
className="text-[#e31e24] text-[10px] font-black uppercase hover:underline"
>
Edit
</button>
)}
</div>
<p className="text-[10px] text-slate-400 leading-snug">
The maximum value you will receive in case of total damage or theft.
</p>
<div className="pt-2">
<input
type="range"
min={minIDV}
max={maxIDV}
value={currentIDV}
onChange={(e) => { setCustomIDV(parseInt(e.target.value)); setIdvType('custom'); }}
className="w-full h-1.5 bg-slate-100 rounded-full appearance-none cursor-pointer accent-[#1a2b4b]"
/>
<div className="flex justify-between text-[10px] font-bold text-slate-400 mt-1">
<span> {minIDV.toLocaleString()}</span>
<span> {maxIDV.toLocaleString()}</span>
</div>
</div>
{!isEditingIDV ? (
<div className="bg-indigo-50 border border-indigo-100 rounded-xl p-3 text-center">
<p className="text-[10px] font-black text-indigo-700 uppercase">
{idvType === 'lowest' ? 'Cheapest' : idvType === 'recommended' ? 'Recommended' : 'Custom'} IDV
</p>
<p className="text-sm font-black text-indigo-900"> {currentIDV.toLocaleString()}</p>
</div>
) : (
<div className="space-y-2 animate-in fade-in slide-in-from-top-1">
<button
onClick={() => { setIdvType('lowest'); setIsEditingIDV(false); }}
className={`w-full text-left p-3 rounded-xl border transition-all ${idvType === 'lowest' ? 'border-[#e31e24] bg-red-50' : 'border-slate-100 bg-white hover:bg-slate-50'}`}
>
<p className="text-xs font-bold text-[#1a2b4b]">Lowest {minIDV.toLocaleString()}</p>
<p className="text-[9px] text-slate-500 font-medium">Cheapest premium</p>
</button>
<button
onClick={() => { setIdvType('recommended'); setIsEditingIDV(false); }}
className={`w-full text-left p-3 rounded-xl border transition-all ${idvType === 'recommended' ? 'border-[#e31e24] bg-red-50' : 'border-slate-100 bg-white hover:bg-slate-50'}`}
>
<p className="text-xs font-bold text-[#1a2b4b]">Recommended {maxIDV.toLocaleString()}</p>
<p className="text-[9px] text-slate-500 font-medium">Based on your bike age</p>
</button>
<div className={`p-3 rounded-xl border transition-all ${idvType === 'custom' ? 'border-[#e31e24] bg-red-50' : 'border-slate-100 bg-white'}`}>
<div className="flex items-center justify-between">
<p className="text-xs font-bold text-[#1a2b4b]">Set your own</p>
<input
type="number"
min={minIDV}
max={maxIDV}
value={customIDV}
onChange={(e) => {
const val = parseInt(e.target.value);
if (!isNaN(val)) { setCustomIDV(val); setIdvType('custom'); }
}}
className="w-20 px-2 py-1 text-xs font-bold border rounded bg-white outline-none focus:border-[#e31e24]"
/>
</div>
<button
onClick={() => setIsEditingIDV(false)}
className="w-full mt-2 py-1 text-[9px] font-black text-[#e31e24] uppercase border border-[#e31e24]/20 rounded"
>
Apply Custom
</button>
</div>
</div>
)}
</div>
</div>
</div>
<div className="bg-white rounded-2xl border border-slate-200 p-4 shadow-sm text-center">
<p className="text-[10px] text-slate-400 font-medium">Looking for specific add-ons?</p>
<button className="text-[10px] font-black text-[#e31e24] uppercase mt-1 hover:underline">Customize Coverages</button>
</div>
</div>
</aside>
{/* Main Quotes List */}
<main className="lg:w-2/3 xl:w-3/4">
<div className="mb-6 flex items-center justify-between">
<h2 className="text-[#1a2b4b] font-bold">
Found below <span className="text-[#e31e24]">Quotes</span> for your ride
</h2>
<div className="flex items-center gap-2">
<span className="text-[10px] font-black text-slate-400 uppercase">Sort by:</span>
<select className="text-[11px] font-bold bg-transparent border-none focus:ring-0 cursor-pointer text-[#1a2b4b]">
<option>Lowest Premium</option>
<option>Highest IDV</option>
</select>
</div>
</div>
<div className="space-y-4">
{dummyQuotes.map(q => <QuoteCard key={q.id} quote={q} onBuy={() => onBuyNow(q)} />)}
</div>
<div className="mt-12 text-center p-8 border-2 border-dashed border-slate-200 rounded-3xl">
<div className="inline-flex items-center gap-3 text-slate-400 text-sm font-medium">
<div className="flex gap-1">
<div className="w-1.5 h-1.5 rounded-full bg-slate-300 animate-bounce"></div>
<div className="w-1.5 h-1.5 rounded-full bg-slate-300 animate-bounce [animation-delay:0.2s]"></div>
<div className="w-1.5 h-1.5 rounded-full bg-slate-300 animate-bounce [animation-delay:0.4s]"></div>
</div>
Updating latest rates from insurers...
</div>
</div>
</main>
</div>
</div>
);
};
export default QuoteListPage;

+ 43
- 0
src/Routes/index.tsx View File

@ -0,0 +1,43 @@
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import BasicDetails from "../BasicDetails";
import QuoteListPage from "../Quotation/QuoteListPage";
import ProposalPage from "../KYCDetails/ProposalPage";
// import {
// BasicDetails
// } from "../Pages";
// Routes config
const routes = [
{
path: "/basic-details",
element: <BasicDetails />,
},
{
path: "/quote-list",
element: <QuoteListPage />,
},
{
path: "/kyc-details",
element: <ProposalPage />,
},
];
const PageRoutes = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Navigate to="/basic-details" replace />} />
{routes?.map(({ path, element }, index) => (
<Route
// key={index}
path={path} element={element} />
))}
</Routes>
</BrowserRouter>
);
};
export default PageRoutes;

+ 257
- 0
src/VehicleDetails/CheckoutPage.tsx View File

@ -0,0 +1,257 @@
import React, { useState } from 'react';
import { InsuranceQuote, ProposalData, VehicleInfo } from '@/types';
interface CheckoutPageProps {
quote: InsuranceQuote;
}
const CheckoutPage: React.FC<CheckoutPageProps & { vehicle: VehicleInfo }> = ({ quote, vehicle }) => {
const [isSubmitted, setIsSubmitted] = useState(false);
const [isPaid, setIsPaid] = useState(false);
const [proposal, setProposal] = useState<ProposalData>({
pan: 'AATPU7693N',
name: 'Suketu Upadhyay',
dob: '1984-07-03',
phone: '9833604121',
email: 'Suketu123_Upadhyay@hotmail.com',
nomineeName: 'Vasudha Upadhyay',
nomineeDob: '1986-06-03',
nomineeRelationship: 'Spouse',
previousTPPolicyNo: '',
previousTPPolicyIssuer: '',
appointeeName: '',
appointeeRelationship: '',
isHypothecated: 'No',
bankName: ''
});
const tax = Math.round(quote.premium * 0.18);
const basePrice = quote.premium;
const total = basePrice + tax;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitted(true);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handlePay = () => {
// Simulating redirect to payment gateway and back
setIsPaid(true);
};
return (
<div className="relative min-h-screen">
<div className={`container mx-auto px-4 py-8 space-y-6 transition-all duration-500 ${isPaid ? 'blur-sm pointer-events-none' : ''} animate-in fade-in duration-700`}>
{/* 1. High-Density Vehicle Header (Top) */}
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div className="p-6 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 gap-x-12 gap-y-4">
<HeaderItem label="MODEL:" value={vehicle.model} />
<HeaderItem label="MAKE:" value={vehicle.make} />
<HeaderItem label="FUEL_TYPE:" value={vehicle.fuelType} />
<HeaderItem label="VARIANT:" value={vehicle.variant} />
<HeaderItem label="REGISTERED_CITY:" value={vehicle.registeredCity} />
<HeaderItem label="REGISTERED_DATE:" value={vehicle.registeredDate} />
<HeaderItem label="CHASSIS_NUMBER:" value={vehicle.chassisNumber} />
<HeaderItem label="ENGINE_NUMBER:" value={vehicle.engineNumber} />
<HeaderItem label="PREVIOUS_POLICY_NUMBER:" value={vehicle.previousPolicyNumber} />
<HeaderItem label="PREVIOUS_INSURER:" value={vehicle.previousInsurer} />
<HeaderItem label="PREVIOUS_POLICY_END_DATE:" value={vehicle.previousPolicyEndDate} />
<HeaderItem label="VEHICLE REG NO:" value={vehicle.regNo} />
</div>
</div>
<div className="flex flex-col lg:flex-row gap-8">
{/* 2. Full Proposal Form (Left) */}
<div className={`lg:w-2/3 bg-white rounded-3xl p-8 border border-slate-200 shadow-sm transition-opacity duration-500 ${isSubmitted ? 'opacity-80' : ''}`}>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-5">
<FormField label="Vehicle Reg No. *" value={vehicle.regNo} readOnly={true} />
<FormField label="Chassis number *" value={vehicle.chassisNumber} readOnly={true} />
<FormField label="Engine number *" value={vehicle.engineNumber} readOnly={true} />
<FormField label="Previous policy No. *" value={vehicle.previousPolicyNumber} readOnly={isSubmitted} />
<FormField label="Previous policy Issuer *" value={vehicle.previousInsurer} readOnly={isSubmitted} />
<FormField label="Previous TP policy Issuer" value={proposal.previousTPPolicyIssuer || 'Previous TP policy Issuer'} placeholder readOnly={isSubmitted} />
<FormField label="Previous TP policy No." value={proposal.previousTPPolicyNo || 'Previous TP policy No.'} placeholder readOnly={isSubmitted} />
<FormField label="Nominee Name *" value={proposal.nomineeName} readOnly={isSubmitted} />
<FormField label="Nominee DOB *" value={proposal.nomineeDob} type="date" readOnly={isSubmitted} />
<FormField label="Nominee Relationship *" value={proposal.nomineeRelationship} isSelect readOnly={isSubmitted} />
<FormField label="Appointee name" value={proposal.appointeeName || 'Appointee name'} placeholder readOnly={isSubmitted} />
<FormField label="Appointee relationship with nominee" value={proposal.appointeeRelationship || 'Appointee relationship with nominee'} placeholder readOnly={isSubmitted} />
<FormField label="Hypothecation/Loan flag" value={proposal.isHypothecated} isSelect readOnly={isSubmitted} />
<FormField label="Bank name if hypothecated" value={proposal.bankName || 'Bank name if hypothecated'} placeholder readOnly={isSubmitted} />
</div>
<div className="mt-12 flex justify-center">
<button
onClick={handleSubmit}
disabled={isSubmitted}
className={`px-16 py-3 rounded-lg font-bold transition-all shadow-lg uppercase tracking-wide ${isSubmitted
? 'bg-slate-400 cursor-not-allowed opacity-60 text-white'
: 'bg-[#2b458c]/80 hover:bg-[#1e3a8a] text-white'
}`}
>
{isSubmitted ? 'Submitted' : 'Submit'}
</button>
</div>
</div>
{/* 3. Summary Card (Right) */}
<div className="lg:w-1/3">
<div className="bg-white rounded-3xl p-8 border border-slate-200 shadow-sm space-y-8 lg:sticky lg:top-28">
<div className="flex items-center gap-4 py-2 border-b border-slate-50">
<img src={quote.insurerLogo} alt={quote.insurerName} className="h-12 w-12 object-contain" />
<div className="flex flex-col">
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-0.5">Insurance Partner</span>
<p className="text-[11px] font-bold text-[#1a2b4b] leading-tight">{quote.insurerName}</p>
</div>
</div>
<div className="space-y-4">
<PriceDetail label="BASE PRICE" value={basePrice} />
<PriceDetail label="CONVIENCE FEE" value={0} />
<PriceDetail label="TAX" value={tax} />
<PriceDetail label="PROCESSING FEE" value={0} />
<div className="pt-6 border-t border-slate-100 flex justify-between items-center">
<span className="text-[13px] font-bold text-[#1a2b4b]">Total</span>
<span className="text-xl font-black text-[#1a2b4b]">{total.toLocaleString()}</span>
</div>
</div>
{isSubmitted && (
<div className="pt-4 animate-in slide-in-from-bottom-4 duration-500">
<button
onClick={handlePay}
className="w-full bg-[#1e3a8a] hover:bg-[#162a63] text-white py-4 rounded-xl font-black text-sm shadow-xl transition-all hover:-translate-y-1"
>
Pay securely
</button>
</div>
)}
<div className="pt-2 text-center">
<p className="text-[10px] text-slate-400 leading-relaxed font-medium">
By clicking on '{isSubmitted ? 'Pay now' : 'Submit'}', I agree to the <a href="#" className="text-blue-500 font-bold hover:underline">terms & conditions</a>
</p>
</div>
</div>
</div>
</div>
</div>
{/* 4. Success Modal Overlay */}
{isPaid && (
<div className="fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-[2px] animate-in fade-in duration-300">
<div className="bg-white rounded-[2rem] shadow-2xl w-full max-w-lg p-10 text-center space-y-8 animate-in zoom-in-95 duration-500">
<div className="space-y-2">
<h2 className="text-xl md:text-2xl font-bold text-[#1a2b4b]">
Your policy purchased successfully !
</h2>
</div>
<div className="flex justify-center">
<div className="relative">
<div className="w-24 h-24 rounded-full bg-emerald-100 flex items-center justify-center animate-pulse">
<div className="w-20 h-20 rounded-full bg-[#22c55e] flex items-center justify-center shadow-lg shadow-emerald-200">
<svg className="w-10 h-10 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="4" d="M5 13l4 4L19 7" />
</svg>
</div>
</div>
{/* Decorative particles */}
<div className="absolute -top-2 -right-2 w-3 h-3 bg-emerald-400 rounded-full animate-ping"></div>
<div className="absolute top-1/2 -left-6 w-2 h-2 bg-emerald-300 rounded-full animate-ping delay-75"></div>
</div>
</div>
<div className="space-y-6">
<p className="text-[#1a2b4b] font-bold text-sm">
Policy No: <span className="text-slate-500">USGI/WEBAG/1126181/00/000</span>
</p>
<div className="space-y-4">
<button className="w-full bg-[#2b458c] hover:bg-[#1e3a8a] text-white py-3.5 rounded-xl font-bold text-sm shadow-xl transition-all hover:scale-[1.02]">
Download Policy
</button>
<button
onClick={() => window.location.reload()}
className="block w-full text-blue-500 font-bold text-sm hover:underline"
>
Back to Homepage
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
};
const HeaderItem = ({ label, value }: { label: string; value: string }) => (
<div className="flex flex-col gap-0.5">
<span className="text-[10px] font-medium text-slate-400">{label}</span>
<span className="text-[11px] font-bold text-[#1a2b4b] uppercase break-words">
{value || '-'}
</span>
</div>
);
const FormField = ({
label,
value,
readOnly,
placeholder,
type = "text",
isSelect = false
}: {
label: string;
value?: string;
readOnly?: boolean;
placeholder?: boolean;
type?: string;
isSelect?: boolean;
}) => (
<div className="space-y-1.5">
<label className={`text-[11px] font-medium transition-colors ${readOnly ? 'text-slate-300' : 'text-[#1a2b4b]/60'}`}>
{label}
</label>
<div className="relative">
<input
type={type}
defaultValue={value}
readOnly={readOnly}
className={`w-full px-4 py-2.5 rounded-lg border outline-none text-[12px] font-medium transition-all ${readOnly
? 'bg-slate-50 border-slate-100 text-slate-400 cursor-not-allowed'
: 'bg-white border-slate-200 text-[#1a2b4b] focus:border-[#2b458c]'
} ${placeholder && !readOnly ? 'text-slate-300 italic' : ''} ${isSelect && !readOnly ? 'cursor-pointer' : ''
}`}
/>
{isSelect && !readOnly && (
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none opacity-20">
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" /></svg>
</div>
)}
{type === 'date' && !readOnly && (
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none opacity-20">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /></svg>
</div>
)}
</div>
</div>
);
const PriceDetail = ({ label, value }: { label: string; value: number }) => (
<div className="flex justify-between items-center">
<span className="text-[11px] font-bold text-slate-400 tracking-tight">{label}</span>
<span className="text-[12px] font-bold text-[#1a2b4b]">{value.toLocaleString()}</span>
</div>
);
export default CheckoutPage;

+ 0
- 0
src/index.tsx View File


+ 27
- 0
src/services/geminiService.ts View File

@ -0,0 +1,27 @@
import { GoogleGenAI } from "@google/genai";
// Standard service pattern to keep AI logic separate
export const getInsuranceInsight = async (formData: any) => {
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY || '' });
const prompt = `
Based on the following two-wheeler insurance details, provide a short 1-sentence helpful advice to the user.
Details:
- Cover: ${formData.coverType}
- Claims made: ${formData.claimStatus}
- NCB: ${formData.ncb}%
Keep it professional, helpful, and encouraging.
`;
try {
const response = await ai.models.generateContent({
model: 'gemini-3-flash-preview',
contents: prompt,
});
return response.text;
} catch (error) {
console.error("Gemini Error:", error);
return null;
}
};

+ 29
- 0
tsconfig.json View File

@ -0,0 +1,29 @@
{
"compilerOptions": {
"target": "ES2022",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"skipLibCheck": true,
"types": [
"node"
],
"moduleResolution": "bundler",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"jsx": "react-jsx",
"paths": {
"@/*": [
"./*"
]
},
"allowImportingTsExtensions": true,
"noEmit": true
}
}

+ 76
- 0
types.ts View File

@ -0,0 +1,76 @@
export interface FormData {
registrationNumber: string;
previousPolicyNumber: string;
expiryDate: string;
coverType: 'comprehensive' | 'third-party' | 'own-damage';
claimStatus: 'yes' | 'no';
ncb: number;
}
export interface Benefit {
title: string;
description: string;
icon: string;
}
export interface QuoteFeature {
label: string;
value: string | number | boolean;
}
// export interface InsuranceQuote {
// id?: string;
// insurerName: string;
// insurerLogo: string;
// premium: number;
// idvValue?: number;
// features?: string[];
// }
export interface Feature {
label: string;
value: string | number;
}
export interface InsuranceQuote {
id: string;
insurerName: string;
insurerLogo: string;
idvValue: number;
premium: number;
features: Feature[] | string[];
}
export interface VehicleInfo {
model: string;
make: string;
fuelType: string;
variant: string;
registeredCity: string;
registeredDate: string;
chassisNumber: string;
engineNumber: string;
previousPolicyNumber: string;
previousInsurer: string;
previousPolicyEndDate: string;
regNo: string;
}
export interface ProposalData {
pan: string;
name: string;
dob: string;
phone: string;
email: string;
// Final Review Fields
nomineeName?: string;
nomineeDob?: string;
nomineeRelationship?: string;
appointeeName?: string;
appointeeRelationship?: string;
isHypothecated?: string;
bankName?: string;
previousTPPolicyIssuer?: string;
previousTPPolicyNo?: string;
}

+ 23
- 0
vite.config.ts View File

@ -0,0 +1,23 @@
import path from 'path';
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, '.', '');
return {
server: {
port: 3000,
host: '0.0.0.0',
},
plugins: [react()],
define: {
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
}
}
};
});

Loading…
Cancel
Save

Powered by TurnKey Linux.