import React, { useState, useEffect, useMemo } from 'react';
import {
LayoutDashboard, Users, Calculator, Settings, LogOut, FileText,
Mail, Key, ShieldCheck, CheckCircle, AlertCircle, Building,
UserCircle, Download, FileSignature, Menu, X, Plus, Trash2, Edit
} from 'lucide-react';
// --- YARDIMCI FONKSİYONLAR VE KANCA (HOOK) ---
// Verileri kalıcı hale getirmek için özel Hook
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
}
// PDF Kütüphanelerini Dinamik Yükleme Fonksiyonu
const loadPDFLibraries = async () => {
const loadScript = (src, globalVar) => new Promise((resolve, reject) => {
if (window[globalVar]) return resolve(window[globalVar]);
if (document.querySelector(`script[src="${src}"]`)) {
const check = setInterval(() => {
if (window[globalVar]) { clearInterval(check); resolve(window[globalVar]); }
}, 100);
return;
}
const script = document.createElement('script');
script.src = src;
script.onload = () => resolve(window[globalVar]);
script.onerror = reject;
document.body.appendChild(script);
});
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js', 'jspdf');
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.31/jspdf.plugin.autotable.min.js', 'jspdf-autotable'); // AutoTable eklentisi
};
// --- ENDÜSTRİYEL BORDRO HESAPLAMA MOTORU ---
const calculatePayroll = (grossSalary, overtimeHours = 0, overtimeMins = 0, deductions = 0) => {
const gross = parseFloat(grossSalary) || 0;
// SGK İşçi Payı (%14)
const sgkWorker = gross * 0.14;
// İşsizlik Sigortası İşçi Payı (%1)
const unemploymentWorker = gross * 0.01;
// Gelir Vergisi Matrahı
const taxBase = gross - (sgkWorker + unemploymentWorker);
// Dinamik Vergi Dilimleri (2026 Simülasyonu)
let incomeTax = 0;
if (taxBase <= 110000 / 12) incomeTax = taxBase * 0.15;
else if (taxBase <= 230000 / 12) incomeTax = taxBase * 0.20;
else if (taxBase <= 870000 / 12) incomeTax = taxBase * 0.27;
else incomeTax = taxBase * 0.35; // Basitleştirilmiş aylık dilim
// Damga Vergisi (Binde 7.59)
const stampTax = gross * 0.00759;
// Mesai Hesaplama (Saatlik ücretin %50 fazlası, aylık 225 saat baz alınır)
const hourlyRate = gross / 225;
const overtimeRate = hourlyRate * 1.5;
const totalOvertimeHours = parseFloat(overtimeHours) + (parseFloat(overtimeMins) / 60);
const overtimePay = totalOvertimeHours * overtimeRate;
// Net Maaş
const netSalary = gross - (sgkWorker + unemploymentWorker + incomeTax + stampTax) + overtimePay - parseFloat(deductions);
return {
gross: gross.toFixed(2),
sgkWorker: sgkWorker.toFixed(2),
unemploymentWorker: unemploymentWorker.toFixed(2),
taxBase: taxBase.toFixed(2),
incomeTax: incomeTax.toFixed(2),
stampTax: stampTax.toFixed(2),
overtimePay: overtimePay.toFixed(2),
deductions: parseFloat(deductions).toFixed(2),
net: netSalary.toFixed(2)
};
};
// --- BİLEŞENLER ---
// 1. Kurulum ve Sistem Sihirbazı
const SetupWizard = ({ onComplete }) => {
const [step, setStep] = useState(1);
const [config, setConfig] = useState({
dbHost: 'localhost', dbName: 'morbordro_saas', dbUser: 'root', dbPass: '',
smtpHost: 'smtp.morbordro.com', smtpPort: '587', smtpUser: '', smtpPass: '',
licenseKey: '', adminEmail: '', adminPass: ''
});
const handleNext = () => setStep(s => s + 1);
const handleComplete = () => onComplete(config);
return (
Mor Bordro Kurulumu
SaaS Altyapısı ve Veritabanı Yapılandırması
{/* Adım Göstergeleri */}
{[1, 2, 3].map(i => (
= i ? 'bg-purple-600' : 'bg-slate-200'}`} />
))}
{/* Adım 1: Veritabanı */}
{step === 1 && (
)}
{/* Adım 2: SMTP */}
{step === 2 && (
)}
{/* Adım 3: Lisans ve Admin */}
{step === 3 && (
3. Lisanslama ve Yönetici
)}
);
};
// 2. Login Ekranı
const Login = ({ users, onLogin }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const handleLogin = (e) => {
e.preventDefault();
const user = users.find(u => u.email === email && u.password === password);
if (user) {
onLogin(user);
} else {
setError('E-posta veya şifre hatalı. (Kurulumda belirlediğiniz bilgileri deneyin)');
}
};
return (
M
Mor Bordro'ya Giriş
Ticari Kurumsal Yönetim Sistemi
{error &&
}
İşletmenizin Dijital Gücü
Bordro hesaplamaları, personel yönetimi ve maliyet analizleri tek bir platformda, üst düzey güvenlik ile.
);
};
// 3. Karşılama Modalı (Onboarding)
const WelcomeModal = ({ user, onClose }) => {
return (
{user.role === 'admin' ?
:
}
Hoş Geldiniz, {user.name || 'Yönetici'}
{user.role === 'admin' ? 'SaaS Yönetim Paneli' : 'Personel Portalı'}
Mor Bordro ticari otomasyon sistemine başarıyla giriş yaptınız. Yetki seviyeniz: {user.role.toUpperCase()}
{user.role === 'admin' ? (
<>
- Şirketleri ve personelleri yönetebilirsiniz.
- Resmi bordro hesaplamaları yapabilirsiniz.
- PDF dökümleri alabilirsiniz.
>
) : (
<>
- Kendi bordro geçmişinizi görüntüleyebilirsiniz.
- İzin ve mesai bilgilerinize ulaşabilirsiniz.
>
)}
);
};
// --- ANA UYGULAMA BİLEŞENİ ---
export default function App() {
// Kalıcı Durumlar
const [systemConfig, setSystemConfig] = useLocalStorage('mb_config', null);
const [users, setUsers] = useLocalStorage('mb_users', []);
const [payrolls, setPayrolls] = useLocalStorage('mb_payrolls', []);
// Oturum ve Arayüz Durumları
const [currentUser, setCurrentUser] = useState(null);
const [currentView, setCurrentView] = useState('dashboard');
const [showWelcome, setShowWelcome] = useState(false);
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
// İlk Kurulum Kontrolü ve Varsayılan Admin Ataması
useEffect(() => {
if (systemConfig && users.length === 0) {
setUsers([{
id: '1',
name: 'Sistem Yöneticisi',
email: systemConfig.adminEmail,
password: systemConfig.adminPass,
role: 'admin',
companyId: 'root'
}]);
}
}, [systemConfig, users, setUsers]);
const handleSetupComplete = (config) => {
setSystemConfig({ ...config, isDemo: !config.licenseKey });
};
const handleLogin = (user) => {
setCurrentUser(user);
setShowWelcome(true);
};
const handleLogout = () => {
setCurrentUser(null);
setCurrentView('dashboard');
};
// --- ALT GÖRÜNÜM BİLEŞENLERİ ---
// Dashboard Görünümü
const DashboardView = () => {
const isEmployee = currentUser.role === 'employee';
const myPayrolls = payrolls.filter(p => p.employeeId === currentUser.id);
const allPayrolls = currentUser.role === 'admin' ? payrolls : [];
const totalGross = allPayrolls.reduce((sum, p) => sum + parseFloat(p.calc.gross), 0);
const totalNet = allPayrolls.reduce((sum, p) => sum + parseFloat(p.calc.net), 0);
return (
Sistem Özeti
{!isEmployee && (
Toplam Personel
{users.filter(u => u.role === 'employee').length}
Toplam Brüt Maliyet
₺{totalGross.toLocaleString('tr-TR')}
Toplam Ödenen Net
₺{totalNet.toLocaleString('tr-TR')}
)}
{isEmployee && (
Son Bordrom
{myPayrolls.length > 0 ? (
Dönem{myPayrolls[myPayrolls.length-1].period}
Brüt₺{myPayrolls[myPayrolls.length-1].calc.gross}
Kesintiler₺{(parseFloat(myPayrolls[myPayrolls.length-1].calc.gross) - parseFloat(myPayrolls[myPayrolls.length-1].calc.net)).toFixed(2)}
Net Ödenen₺{myPayrolls[myPayrolls.length-1].calc.net}
) : (
Henüz bordro kaydınız bulunmamaktadır.
)}
)}
);
};
// Personel Yönetimi Görünümü
const PersonnelView = () => {
const [isAdding, setIsAdding] = useState(false);
const [newEmp, setNewEmp] = useState({ name: '', email: '', password: '', role: 'employee' });
const employees = users.filter(u => u.role === 'employee');
const handleAdd = (e) => {
e.preventDefault();
setUsers([...users, { ...newEmp, id: Date.now().toString() }]);
setIsAdding(false);
setNewEmp({ name: '', email: '', password: '', role: 'employee' });
};
const handleDelete = (id) => {
if(window.confirm('Emin misiniz?')) {
setUsers(users.filter(u => u.id !== id));
}
};
return (
Personel Yönetimi
{isAdding && (
)}
| Personel Adı |
E-Posta Adresi |
Şifre |
İşlemler |
{employees.length === 0 && | Kayıtlı personel bulunmuyor. |
}
{employees.map(emp => (
| {emp.name} |
{emp.email} |
{emp.password} |
|
))}
);
};
// Bordro Motoru ve PDF Çıktı Görünümü
const PayrollView = () => {
const employees = users.filter(u => u.role === 'employee');
const [selectedEmp, setSelectedEmp] = useState('');
const [period, setPeriod] = useState('2026-02');
const [gross, setGross] = useState('');
const [overtimeHours, setOvertimeHours] = useState('0');
const [overtimeMins, setOvertimeMins] = useState('0');
const [deductions, setDeductions] = useState('0');
const [currentCalc, setCurrentCalc] = useState(null);
const [isGeneratingPDF, setIsGeneratingPDF] = useState(false);
const handleCalculate = (e) => {
e.preventDefault();
if(!selectedEmp) return alert("Lütfen personel seçin");
const calc = calculatePayroll(gross, overtimeHours, overtimeMins, deductions);
setCurrentCalc(calc);
};
const handleSave = () => {
if(!currentCalc || !selectedEmp) return;
const newPayroll = {
id: Date.now().toString(),
employeeId: selectedEmp,
period,
calc: currentCalc,
date: new Date().toISOString()
};
setPayrolls([...payrolls, newPayroll]);
alert("Bordro başarıyla sisteme kaydedildi.");
setCurrentCalc(null);
setGross('');
};
// PDF Üretim Mantığı
const generatePDF = async () => {
if (!currentCalc || !selectedEmp) return;
setIsGeneratingPDF(true);
try {
await loadPDFLibraries();
const emp = users.find(u => u.id === selectedEmp);
const { window } = window; // referans düzeltme
const doc = new window.jspdf.jsPDF();
// Header: Mor Bordro Markalaması
doc.setFillColor(30, 27, 75); // Indigo-900
doc.rect(0, 0, 210, 40, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(24);
doc.text("MOR BORDRO", 14, 25);
doc.setFontSize(10);
doc.setTextColor(147, 51, 234); // Purple-600
doc.text("morbordro.com | Resmi Ucret Pusulasi", 14, 32);
// Belge Bilgileri
doc.setTextColor(50, 50, 50);
doc.setFontSize(12);
doc.text(`Personel: ${emp.name}`, 14, 50);
doc.text(`Donem: ${period}`, 14, 58);
doc.text(`Tarih: ${new Date().toLocaleDateString('tr-TR')}`, 150, 50);
// AutoTable ile Tablo Çizimi
const tableData = [
['Brut Ucret', `TL ${currentCalc.gross}`],
['Mesai Ucreti', `TL ${currentCalc.overtimePay}`],
['SGK Isci Payi (%14)', `TL ${currentCalc.sgkWorker}`],
['Issizlik Isci Payi (%1)', `TL ${currentCalc.unemploymentWorker}`],
['Gelir Vergisi', `TL ${currentCalc.incomeTax}`],
['Damga Vergisi', `TL ${currentCalc.stampTax}`],
['Diger Kesintiler', `TL ${currentCalc.deductions}`],
];
doc.autoTable({
startY: 65,
head: [['Kalem', 'Tutar']],
body: tableData,
theme: 'striped',
headStyles: { fillColor: [147, 51, 234] }, // Purple
margin: { top: 10 }
});
const finalY = doc.lastAutoTable.finalY || 150;
// Net Maaş Vurgusu
doc.setFillColor(243, 244, 246);
doc.rect(14, finalY + 10, 182, 15, 'F');
doc.setFontSize(14);
doc.setTextColor(30, 27, 75);
doc.text(`NET ODENEN: TL ${currentCalc.net}`, 20, finalY + 20);
// Ticari İmza / Footer (KRİTİK)
doc.setFontSize(9);
doc.setTextColor(150, 150, 150);
doc.text("Yazilim: Visutopya Media Group", 14, 290);
doc.text("Bu belge 5070 sayili kanun geregince elektronik olarak uretilmistir.", 14, 285);
doc.save(`Bordro_${emp.name.replace(/\s+/g, '_')}_${period}.pdf`);
} catch (error) {
console.error("PDF oluşturulurken hata oluştu:", error);
alert("PDF oluşturulamadı. Konsolu kontrol edin.");
} finally {
setIsGeneratingPDF(false);
}
};
return (
Endüstriyel Bordro Motoru
{/* Form Alanı */}
{/* Sonuç Alanı */}
{currentCalc ? (
SİMÜLASYON
Bordro Özeti
Brüt Tutar₺{currentCalc.gross}
Mesai Kazancı+₺{currentCalc.overtimePay}
SGK (%14)-₺{currentCalc.sgkWorker}
Gelir Vergisi-₺{currentCalc.incomeTax}
Ödenecek Net Tutar
₺{currentCalc.net}
) : (
Hesaplama yapmak için sol taraftaki formu doldurun.
Güncel SGK ve Vergi dilimleri kullanılarak hassas hesaplama yapılır.
)}
);
};
// Ayarlar ve Destek Görünümü
const SettingsView = () => (
Sistem Ayarları
Lisans Durumu
Mevcut Sürüm: {systemConfig.isDemo ? 'Demo Modu' : 'Tam Sürüm'}
Multi-tenant modül aktiftir. Veritabanı bağlantısı: {systemConfig.dbHost}
Müşteri Desteği
Teknik destek talepleriniz doğrudan Visutopya Media Group'a iletilir.
);
// --- ANA RENDER AKIŞI ---
// 1. Kurulum Yapılmamışsa Sihirbazı Göster
if (!systemConfig) {
return
;
}
// 2. Oturum Açılmamışsa Login Göster
if (!currentUser) {
return
;
}
// 3. Ana Uygulama Düzeni (Layout)
const isAdmin = currentUser.role === 'admin';
return (
{/* Karşılama Modalı */}
{showWelcome &&
setShowWelcome(false)} />}
{/* Sidebar / Sol Menü */}
{/* Main İçerik Alanı */}
{/* Üst Bar */}
{currentUser.name}
{/* Dinamik İçerik */}
{currentView === 'dashboard' &&
}
{currentView === 'personnel' && isAdmin &&
}
{currentView === 'payroll' && isAdmin &&
}
{currentView === 'settings' && isAdmin &&
}
{/* Müşteri İmzası (KRİTİK - KALDIRILAMAZ) */}
{/* Tailwind için gerekli custom animasyon */}
);
}