feat: İhaleist - Akıllı İhale Takip ve Analiz Platformu (Full PRD + 7 Enterprise Modül)

This commit is contained in:
2026-08-07 16:30:24 +03:00
commit ece74feefe
35 changed files with 7820 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
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>
);
};