import React, { useState } from 'react';
import { Calculator, Receipt, IndianRupee, CheckSquare, Square, Users, TableProperties, AlertCircle, Printer } from 'lucide-react';
const feeData = {
'GEN/OBC': {
title: '9th - GEN/OBC',
color: 'blue',
items: [
{ id: 'gen_sci', name: 'Sci', amount: 5.00 },
{ id: 'gen_art', name: 'Art', amount: 2.00 },
{ id: 'gen_fan', name: 'Fan', amount: 20.00 },
{ id: 'gen_da', name: 'DA', amount: 1.00 },
{ id: 'gen_df', name: 'DF', amount: 10.00 },
{ id: 'gen_rr', name: 'RR', amount: 12.00 },
{ id: 'gen_game', name: 'Game', amount: 5.00 },
{ id: 'gen_scout', name: 'Scout', amount: 2.00 },
{ id: 'gen_rc', name: 'RC', amount: 1.00 },
{ id: 'gen_pbf', name: 'PBF', amount: 10.00 },
{ id: 'gen_av', name: 'AV', amount: 2.00 },
{ id: 'gen_mag', name: 'Mag', amount: 20.00 },
{ id: 'gen_exam', name: 'Exam', amount: 50.00 },
{ id: 'gen_jalpan', name: 'Jalpan', amount: 10.00 },
{ id: 'gen_icard', name: 'I.card', amount: 10.00 },
{ id: 'gen_pragti', name: 'Pragti Patra', amount: 5.00 },
{ id: 'gen_regi', name: 'Regi', amount: 50.00 },
]
},
'SC': {
title: '9th - SC',
color: 'purple',
items: [
{ id: 'sc_sci', name: 'Sci', amount: 5.00 },
{ id: 'sc_art', name: 'Art', amount: 2.00 },
{ id: 'sc_fan', name: 'Fan', amount: 20.00 },
{ id: 'sc_scout', name: 'Scout', amount: 2.00 },
{ id: 'sc_rc', name: 'RC', amount: 1.00 },
{ id: 'sc_pbf', name: 'PBF', amount: 10.00 },
{ id: 'sc_mag', name: 'Mag', amount: 20.00 },
{ id: 'sc_exam', name: 'Exam', amount: 50.00 },
{ id: 'sc_jalpan', name: 'Jalpan', amount: 10.00 },
{ id: 'sc_icard', name: 'I.card', amount: 10.00 },
{ id: 'sc_pragti', name: 'Pragti Patra', amount: 5.00 },
{ id: 'sc_regi', name: 'Regi', amount: 50.00 },
]
}
};
// Safe Tailwind Color Classes for dynamic rendering
const colors = {
'GEN/OBC': { bg: 'bg-blue-50', border: 'border-blue-200', text: 'text-blue-900', check: 'text-blue-600' },
'SC': { bg: 'bg-purple-50', border: 'border-purple-200', text: 'text-purple-900', check: 'text-purple-600' }
};
// Initialize all items as selected
const initialSelectedItems = {
'GEN/OBC': new Set(feeData['GEN/OBC'].items.map(i => i.id)),
'SC': new Set(feeData['SC'].items.map(i => i.id))
};
// All unique item names for the summary table
const allUniqueItemNames = [...new Set([
...feeData['GEN/OBC'].items.map(i => i.name),
...feeData['SC'].items.map(i => i.name),
'Fine (Manual)'
])];
export default function App() {
const [studentCounts, setStudentCounts] = useState({ 'GEN/OBC': '', 'SC': '' });
const [selectedItems, setSelectedItems] = useState(initialSelectedItems);
const [fines, setFines] = useState({ 'GEN/OBC': '', 'SC': '' });
const [printHint, setPrintHint] = useState(false);
const handlePrint = () => {
try {
window.print();
} catch (e) {
console.log("Print blocked by environment", e);
}
// Agar environment print block karta hai, toh user ko alternative rasta dikhayein
setPrintHint(true);
setTimeout(() => setPrintHint(false), 6000);
};
const handleCountChange = (category, value) => {
const num = value === '' ? '' : parseInt(value, 10);
setStudentCounts(prev => ({ ...prev, [category]: isNaN(num) && value !== '' ? prev[category] : num }));
};
const handleFineChange = (category, value) => {
setFines(prev => ({ ...prev, [category]: value }));
};
const toggleItem = (category, id) => {
const newSelected = new Set(selectedItems[category]);
if (newSelected.has(id)) {
newSelected.delete(id);
} else {
newSelected.add(id);
}
setSelectedItems(prev => ({ ...prev, [category]: newSelected }));
};
const calculatePerStudent = (category) => {
let total = 0;
feeData[category].items.forEach(item => {
if (selectedItems[category].has(item.id)) {
total += item.amount;
}
});
const fineAmount = parseFloat(fines[category]) || 0;
return total + fineAmount;
};
const perStudentGen = calculatePerStudent('GEN/OBC');
const perStudentSc = calculatePerStudent('SC');
const countGen = studentCounts['GEN/OBC'] || 0;
const countSc = studentCounts['SC'] || 0;
const totalGenFee = perStudentGen * countGen;
const totalScFee = perStudentSc * countSc;
const grandTotal = totalGenFee + totalScFee;
// Generate data for Combined Summary Table
const getSummaryData = () => {
const rows = [];
allUniqueItemNames.forEach(name => {
let genAmount = 0;
let scAmount = 0;
if (name === 'Fine (Manual)') {
const genFine = parseFloat(fines['GEN/OBC']) || 0;
const scFine = parseFloat(fines['SC']) || 0;
genAmount = genFine * countGen;
scAmount = scFine * countSc;
} else {
const genItem = feeData['GEN/OBC'].items.find(i => i.name === name);
if (genItem && selectedItems['GEN/OBC'].has(genItem.id)) {
genAmount = genItem.amount * countGen;
}
const scItem = feeData['SC'].items.find(i => i.name === name);
if (scItem && selectedItems['SC'].has(scItem.id)) {
scAmount = scItem.amount * countSc;
}
}
if (genAmount > 0 || scAmount > 0) {
rows.push({ name, genAmount, scAmount, total: genAmount + scAmount });
}
});
return rows;
};
const summaryRows = getSummaryData();
const FeeItem = ({ category, item, count }) => {
const isSelected = selectedItems[category].has(item.id);
const theme = colors[category];
return (
<div
className={`flex items-center justify-between p-2 rounded border cursor-pointer transition-colors ${
isSelected
? `${theme.bg} ${theme.border} ${theme.text}`
: 'bg-gray-50 border-gray-200 text-gray-400 hover:bg-gray-100'
}`}
onClick={() => toggleItem(category, item.id)}
>
<div className="flex items-center space-x-2">
{isSelected ? (
<CheckSquare className={`w-4 h-4 ${theme.check}`} />
) : (
<Square className="w-4 h-4" />
)}
<span className="font-medium text-sm">{item.name}</span>
</div>
<div className="text-right">
{count > 0 ? (
<div className="flex flex-col items-end">
<span className="font-semibold text-sm">₹{(item.amount * count).toFixed(2)}</span>
<span className={`text-[10px] ${isSelected ? `${theme.check} opacity-80` : 'text-gray-400'}`}>
₹{item.amount.toFixed(2)} × {count}
</span>
</div>
) : (
<span className="font-semibold text-sm">₹{item.amount.toFixed(2)}</span>
)}
</div>
</div>
);
};
const FineItem = ({ category, count }) => {
const fineVal = parseFloat(fines[category]) || 0;
return (
<div className="flex items-center justify-between p-2 rounded border bg-amber-50 border-amber-200 mt-2">
<div className="flex items-center space-x-2 text-amber-800">
<AlertCircle className="w-4 h-4" />
<span className="font-medium text-sm">Fine (Manual)</span>
</div>
<div className="flex flex-col items-end">
<div className="flex items-center space-x-1">
<span className="text-amber-700 text-sm font-medium">₹</span>
<input
type="number"
min="0"
placeholder="0"
value={fines[category]}
onChange={(e) => handleFineChange(category, e.target.value)}
className="w-16 p-1 border border-amber-300 rounded text-right text-sm font-semibold outline-none focus:border-amber-500 bg-white print:border-none print:bg-transparent print:p-0"
/>
</div>
{count > 0 && fineVal > 0 && (
<span className="text-[10px] text-amber-700 mt-1">
₹{fineVal.toFixed(2)} × {count} = ₹{(fineVal * count).toFixed(2)}
</span>
)}
</div>
</div>
);
};
return (
<div className="min-h-screen bg-gray-100 p-2 sm:p-4 md:p-8 font-sans pb-32 print:bg-white print:p-0 print:pb-0">
<div className="max-w-6xl mx-auto space-y-6 print:space-y-4">
{/* Header */}
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 p-6 text-center relative print:border-none print:shadow-none print:p-0 print:mb-8">
<div className="absolute top-4 right-4 sm:top-6 sm:right-6 print:hidden flex flex-col items-end z-10">
<button
onClick={handlePrint}
className="flex items-center bg-gray-900 text-white px-4 py-2 rounded-xl text-sm font-semibold hover:bg-gray-800 transition-colors shadow-md"
>
<Printer className="w-4 h-4 mr-2" />
Print PDF
</button>
{printHint && (
<div className="mt-2 text-xs text-red-600 bg-red-50 p-2 rounded-lg border border-red-200 w-44 text-right shadow-sm">
Agar button kaam na kare, toh kripya browser ke menu se <b>Print</b> chunein ya <b>Ctrl+P</b> dabayein.
</div>
)}
</div>
<div className="flex justify-center mb-2 text-blue-600 print:hidden mt-10 sm:mt-0">
<Calculator className="w-10 h-10" />
</div>
<h1 className="text-2xl md:text-3xl font-bold text-gray-800 tracking-wide">Fee Collection Calculator</h1>
<p className="text-gray-500 mt-1">Class 9th (2021-22) - GEN/OBC & SC</p>
</div>
{/* Grid for Both Categories */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* GEN/OBC Card */}
<div className="bg-white rounded-2xl shadow-sm border-t-4 border-blue-500 flex flex-col print:border-t-2 print:border-gray-800 print:shadow-none">
<div className="p-5 flex-grow">
<h2 className="text-xl font-bold text-gray-800 mb-4 flex items-center">
<Users className="w-6 h-6 mr-2 text-blue-500 print:text-gray-800" />
GEN / OBC
</h2>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-600 mb-1 print:hidden">Student ki Sankhya (Count)</label>
<div className="hidden print:block text-sm font-medium text-gray-600 mb-1">Total Students:</div>
<input
type="number"
min="0"
value={studentCounts['GEN/OBC']}
onChange={(e) => handleCountChange('GEN/OBC', e.target.value)}
placeholder="0"
className="w-full text-xl p-3 border-2 border-gray-200 rounded-xl focus:border-blue-500 focus:ring-0 outline-none transition-colors print:border-none print:p-0 print:text-2xl print:font-bold print:text-gray-900"
/>
</div>
<div className="bg-blue-50 rounded-xl p-4 flex justify-between items-center mb-6 print:bg-white print:border print:border-gray-300">
<div>
<p className="text-xs text-blue-600 font-bold uppercase tracking-wide print:text-gray-600">Prati Student</p>
<p className="text-xl font-bold text-blue-900 print:text-gray-900">₹{perStudentGen.toFixed(2)}</p>
</div>
<div className="text-right">
<p className="text-xs text-blue-600 font-bold uppercase tracking-wide print:text-gray-600">Kul (Total)</p>
<p className="text-2xl font-black text-blue-700 print:text-gray-900">₹{totalGenFee.toFixed(2)}</p>
</div>
</div>
<div className="border-t pt-4">
<h3 className="text-sm font-bold text-gray-500 uppercase mb-3">Sabhi Mad (Fee Items)</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{feeData['GEN/OBC'].items.map(item => (
<FeeItem key={item.id} category="GEN/OBC" item={item} count={countGen} />
))}
</div>
<FineItem category="GEN/OBC" count={countGen} />
</div>
</div>
</div>
{/* SC Card */}
<div className="bg-white rounded-2xl shadow-sm border-t-4 border-purple-500 flex flex-col print:border-t-2 print:border-gray-800 print:shadow-none">
<div className="p-5 flex-grow">
<h2 className="text-xl font-bold text-gray-800 mb-4 flex items-center">
<Users className="w-6 h-6 mr-2 text-purple-500 print:text-gray-800" />
SC
</h2>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-600 mb-1 print:hidden">Student ki Sankhya (Count)</label>
<div className="hidden print:block text-sm font-medium text-gray-600 mb-1">Total Students:</div>
<input
type="number"
min="0"
value={studentCounts['SC']}
onChange={(e) => handleCountChange('SC', e.target.value)}
placeholder="0"
className="w-full text-xl p-3 border-2 border-gray-200 rounded-xl focus:border-purple-500 focus:ring-0 outline-none transition-colors print:border-none print:p-0 print:text-2xl print:font-bold print:text-gray-900"
/>
</div>
<div className="bg-purple-50 rounded-xl p-4 flex justify-between items-center mb-6 print:bg-white print:border print:border-gray-300">
<div>
<p className="text-xs text-purple-600 font-bold uppercase tracking-wide print:text-gray-600">Prati Student</p>
<p className="text-xl font-bold text-purple-900 print:text-gray-900">₹{perStudentSc.toFixed(2)}</p>
</div>
<div className="text-right">
<p className="text-xs text-purple-600 font-bold uppercase tracking-wide print:text-gray-600">Kul (Total)</p>
<p className="text-2xl font-black text-purple-700 print:text-gray-900">₹{totalScFee.toFixed(2)}</p>
</div>
</div>
<div className="border-t pt-4">
<h3 className="text-sm font-bold text-gray-500 uppercase mb-3">Sabhi Mad (Fee Items)</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{feeData['SC'].items.map(item => (
<FeeItem key={item.id} category="SC" item={item} count={countSc} />
))}
</div>
<FineItem category="SC" count={countSc} />
</div>
</div>
</div>
</div>
{/* Combined Summary Section */}
{summaryRows.length > 0 && (
<div className="bg-white rounded-2xl shadow-sm border border-gray-200 overflow-hidden print:shadow-none print:border-gray-300 print:mt-8 break-inside-avoid">
<div className="p-5 border-b border-gray-200 bg-gray-50 flex items-center print:bg-white">
<TableProperties className="w-6 h-6 mr-3 text-indigo-600 print:hidden" />
<h2 className="text-lg md:text-xl font-bold text-gray-800">Sabhi Mado ka Combined Total</h2>
</div>
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse min-w-[600px] print:min-w-full">
<thead>
<tr className="bg-gray-100 text-gray-600 text-sm uppercase tracking-wider print:bg-white print:border-b-2 print:border-gray-800">
<th className="p-4 border-b font-semibold">Mad (Item)</th>
<th className="p-4 border-b font-semibold text-right">GEN/OBC Total</th>
<th className="p-4 border-b font-semibold text-right">SC Total</th>
<th className="p-4 border-b font-bold text-right text-gray-900">Kul (Combined Total)</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100 print:divide-gray-300">
{summaryRows.map((row, idx) => (
<tr key={idx} className="hover:bg-indigo-50/30 transition-colors">
<td className="p-4 font-medium text-gray-700">{row.name}</td>
<td className="p-4 text-right text-gray-600">₹{row.genAmount.toFixed(2)}</td>
<td className="p-4 text-right text-gray-600">₹{row.scAmount.toFixed(2)}</td>
<td className="p-4 text-right font-bold text-gray-800">₹{row.total.toFixed(2)}</td>
</tr>
))}
</tbody>
<tfoot>
<tr className="bg-gray-50 print:bg-white print:border-t-2 print:border-gray-800">
<td className="p-4 font-bold text-gray-800 text-right uppercase text-sm">Final Amount</td>
<td className="p-4 font-bold text-blue-600 print:text-gray-900 text-right text-lg">₹{totalGenFee.toFixed(2)}</td>
<td className="p-4 font-bold text-purple-600 print:text-gray-900 text-right text-lg">₹{totalScFee.toFixed(2)}</td>
<td className="p-4 font-black text-green-600 print:text-gray-900 text-right text-xl">₹{grandTotal.toFixed(2)}</td>
</tr>
</tfoot>
</table>
</div>
</div>
)}
</div>
{/* Fixed Bottom Grand Total Bar */}
<div className="fixed bottom-0 left-0 right-0 bg-gray-900 text-white shadow-2xl border-t border-gray-800 z-50 print:hidden">
<div className="max-w-6xl mx-auto p-4 sm:px-6 md:px-8 flex flex-row items-center justify-between">
<div className="flex items-center space-x-3 sm:space-x-4">
<div className="bg-gray-800 p-2 sm:p-3 rounded-full hidden sm:block">
<Receipt className="w-6 h-6 sm:w-8 sm:h-8 text-green-400" />
</div>
<div>
<h2 className="text-lg sm:text-2xl font-bold">Grand Total (Sabhi)</h2>
<p className="text-gray-400 text-xs sm:text-sm">
Total Students: {countGen + countSc}
<span className="hidden sm:inline"> ({countGen} GEN/OBC, {countSc} SC)</span>
</p>
</div>
</div>
<div className="flex flex-col items-end">
<div className="flex items-center text-2xl sm:text-4xl md:text-5xl font-black text-green-400">
<IndianRupee className="w-5 h-5 sm:w-8 sm:h-8 mr-1 sm:mr-2" />
{grandTotal.toLocaleString('en-IN', { minimumFractionDigits: 2 })}
</div>
</div>
</div>
</div>
</div>
);
}