178 lines
7.8 KiB
React
178 lines
7.8 KiB
React
import React, { useState } from 'react';
|
||
import { Calculator, Copy, Check } from 'lucide-react';
|
||
|
||
export const VatCalculator = () => {
|
||
const [mode, setMode] = useState('haric'); // 'haric' (net to gross) or 'dahil' (gross to net)
|
||
const [amount, setAmount] = useState(100000);
|
||
const [vatRate, setVatRate] = useState(20); // 1, 10, 20
|
||
const [tevkifatRate, setTevkifatRate] = useState(0); // 0, 50 (5/10), 70 (7/10), 90 (9/10)
|
||
const [copied, setCopied] = useState(false);
|
||
|
||
// Calculations
|
||
let netAmount = 0;
|
||
let vatAmount = 0;
|
||
let grossAmount = 0;
|
||
let tevkifatAmount = 0;
|
||
let payableVat = 0;
|
||
|
||
if (mode === 'haric') {
|
||
netAmount = Number(amount) || 0;
|
||
vatAmount = netAmount * (vatRate / 100);
|
||
grossAmount = netAmount + vatAmount;
|
||
} else {
|
||
grossAmount = Number(amount) || 0;
|
||
netAmount = grossAmount / (1 + vatRate / 100);
|
||
vatAmount = grossAmount - netAmount;
|
||
}
|
||
|
||
if (tevkifatRate > 0) {
|
||
tevkifatAmount = vatAmount * (tevkifatRate / 100);
|
||
payableVat = vatAmount - tevkifatAmount;
|
||
} else {
|
||
payableVat = vatAmount;
|
||
}
|
||
|
||
const handleCopy = () => {
|
||
const summary = `Matrah: ₺${netAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })} | KDV (%${vatRate}): ₺${vatAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })} | Genel Toplam: ₺${grossAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}`;
|
||
navigator.clipboard.writeText(summary);
|
||
setCopied(true);
|
||
setTimeout(() => setCopied(false), 2000);
|
||
};
|
||
|
||
return (
|
||
<div className="max-w-3xl mx-auto my-4 space-y-4 animate-fade-in">
|
||
|
||
<div className="bg-white rounded-xl border border-slate-200 p-5 shadow-xs flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-10 h-10 rounded-xl bg-blue-600 text-white flex items-center justify-center shadow-md">
|
||
<Calculator className="w-6 h-6" />
|
||
</div>
|
||
<div>
|
||
<h1 className="text-xl font-black text-slate-900">İhale KDV & Tevkifat Hesaplayıcı</h1>
|
||
<p className="text-xs text-slate-500">Teklif matrahından KDV dahil tutarı veya KDV dahilden net matrahı hesaplayın.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white rounded-2xl border border-slate-200 p-6 shadow-xs space-y-6">
|
||
|
||
{/* Mode Selector */}
|
||
<div className="grid grid-cols-2 gap-3 p-1 bg-slate-100 rounded-xl">
|
||
<button
|
||
onClick={() => setMode('haric')}
|
||
className={`py-2.5 rounded-lg text-xs font-extrabold transition-all ${
|
||
mode === 'haric' ? 'bg-white text-blue-700 shadow-2xs' : 'text-slate-600 hover:text-slate-900'
|
||
}`}
|
||
>
|
||
KDV Hariç ➔ KDV Dahil Hesapla
|
||
</button>
|
||
<button
|
||
onClick={() => setMode('dahil')}
|
||
className={`py-2.5 rounded-lg text-xs font-extrabold transition-all ${
|
||
mode === 'dahil' ? 'bg-white text-blue-700 shadow-2xs' : 'text-slate-600 hover:text-slate-900'
|
||
}`}
|
||
>
|
||
KDV Dahil ➔ Matrah Hesapla
|
||
</button>
|
||
</div>
|
||
|
||
{/* Inputs */}
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs">
|
||
<div>
|
||
<label className="block font-bold text-slate-700 mb-1.5">
|
||
{mode === 'haric' ? 'Teklif Tutarı (KDV Hariç Matrah)' : 'KDV Dahil Toplam Tutar'}
|
||
</label>
|
||
<div className="relative">
|
||
<input
|
||
type="number"
|
||
value={amount}
|
||
onChange={(e) => setAmount(e.target.value)}
|
||
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2.5 font-mono text-sm font-bold text-slate-900 focus:ring-2 focus:ring-blue-500"
|
||
/>
|
||
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 font-bold">TL</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block font-bold text-slate-700 mb-1.5">KDV Oranı</label>
|
||
<div className="flex gap-1.5">
|
||
{[1, 10, 20].map(rate => (
|
||
<button
|
||
key={rate}
|
||
type="button"
|
||
onClick={() => setVatRate(rate)}
|
||
className={`flex-1 py-2 rounded-lg font-extrabold transition-colors border ${
|
||
vatRate === rate ? 'bg-blue-600 text-white border-blue-600' : 'bg-slate-50 text-slate-700 border-slate-300 hover:bg-slate-100'
|
||
}`}
|
||
>
|
||
%{rate}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block font-bold text-slate-700 mb-1.5">Tevkifat Oranı (Varsa)</label>
|
||
<select
|
||
value={tevkifatRate}
|
||
onChange={(e) => setTevkifatRate(Number(e.target.value))}
|
||
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2.5 font-semibold text-slate-800"
|
||
>
|
||
<option value={0}>Tevkifat Yok</option>
|
||
<option value={50}>5/10 Tevkifat (%50)</option>
|
||
<option value={70}>7/10 Tevkifat (%70)</option>
|
||
<option value={90}>9/10 Tevkifat (%90)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Results Card */}
|
||
<div className="bg-slate-900 text-white p-6 rounded-xl space-y-4 shadow-lg">
|
||
<div className="flex items-center justify-between border-b border-slate-800 pb-3">
|
||
<span className="font-bold text-xs uppercase tracking-wider text-slate-300">Hesaplama Sonucu</span>
|
||
<button
|
||
onClick={handleCopy}
|
||
className="bg-slate-800 hover:bg-slate-700 text-blue-400 font-semibold px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 transition-colors border border-slate-700"
|
||
>
|
||
{copied ? <Check className="w-3.5 h-3.5 text-emerald-400" /> : <Copy className="w-3.5 h-3.5" />}
|
||
<span>{copied ? 'Kopyalandı!' : 'Kopyala'}</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 text-xs">
|
||
<div className="bg-white/5 p-3 rounded-lg border border-white/10">
|
||
<span className="text-slate-400 font-medium block">Net Matrah (KDV Hariç)</span>
|
||
<span className="text-lg font-black text-white mt-1 block">
|
||
₺{netAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="bg-white/5 p-3 rounded-lg border border-white/10">
|
||
<span className="text-slate-400 font-medium block">Hesaplanan KDV (%{vatRate})</span>
|
||
<span className="text-lg font-black text-amber-400 mt-1 block">
|
||
₺{vatAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="bg-white/5 p-3 rounded-lg border border-white/10">
|
||
<span className="text-slate-400 font-medium block">Genel Toplam (KDV Dahil)</span>
|
||
<span className="text-xl font-black text-emerald-400 mt-1 block">
|
||
₺{grossAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{tevkifatRate > 0 && (
|
||
<div className="bg-blue-950/80 p-3.5 rounded-lg border border-blue-800 text-xs flex justify-between">
|
||
<span className="text-slate-300">Tevkifat Tutarı: <strong className="text-amber-300">₺{tevkifatAmount.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}</strong></span>
|
||
<span className="text-slate-300">Ödenecek KDV: <strong className="text-emerald-400">₺{payableVat.toLocaleString('tr-TR', { maximumFractionDigits: 2 })}</strong></span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
);
|
||
};
|