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 && (

1. Veritabanı Bağlantısı

setConfig({...config, dbHost: e.target.value})} /> setConfig({...config, dbName: e.target.value})} /> setConfig({...config, dbUser: e.target.value})} /> setConfig({...config, dbPass: e.target.value})} />
)} {/* Adım 2: SMTP */} {step === 2 && (

2. E-Posta / Bildirim Altyapısı

setConfig({...config, smtpHost: e.target.value})} /> setConfig({...config, smtpPort: e.target.value})} /> setConfig({...config, smtpUser: e.target.value})} /> setConfig({...config, smtpPass: e.target.value})} />
)} {/* Adım 3: Lisans ve Admin */} {step === 3 && (

3. Lisanslama ve Yönetici

setConfig({...config, licenseKey: e.target.value})} />
setConfig({...config, adminEmail: e.target.value})} /> setConfig({...config, adminPass: e.target.value})} />
)}
); }; // 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 &&
{error}
}
setEmail(e.target.value)} placeholder="ornek@sirket.com" />
setPassword(e.target.value)} placeholder="••••••••" />

İş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 && (
setNewEmp({...newEmp, name: e.target.value})} />
setNewEmp({...newEmp, email: e.target.value})} />
setNewEmp({...newEmp, password: e.target.value})} />
)}
{employees.length === 0 && } {employees.map(emp => ( ))}
Personel Adı E-Posta Adresi Şifre İşlemler
Kayıtlı personel bulunmuyor.
{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ı */}

Hesaplama Parametreleri

setPeriod(e.target.value)} />
setGross(e.target.value)} />
setOvertimeHours(e.target.value)} />
setOvertimeMins(e.target.value)} />
setDeductions(e.target.value)} />
{/* 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) */}

Ticari Lisans: Yazılım: Visutopya Media Group

{/* Tailwind için gerekli custom animasyon */}