added lockli screen
All checks were successful
Build & Deploy Vite App (Docker Compose) / build-and-deploy (push) Successful in 41s

This commit is contained in:
2025-12-03 16:19:12 +03:00
parent 003cae6fc8
commit cb6abbd73d
3 changed files with 179 additions and 23 deletions

View File

@@ -2,10 +2,11 @@ import React from 'react';
import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom'; import { BrowserRouter as Router, Routes, Route, useLocation } from 'react-router-dom';
import Home from './pages/Home'; import Home from './pages/Home';
import Portal from './pages/Portal'; import Portal from './pages/Portal';
import LockliPrivacy from './pages/LockliPrivacy'; // <-- YENİ EKLENDİ import LockliPrivacy from './pages/LockliPrivacy';
import PasswordManagerDetail from './pages/PasswordManagerDetail';
import { AnimatePresence } from 'framer-motion'; import { AnimatePresence } from 'framer-motion';
// Animasyonlu geçişler için bir Wrapper bileşeni
function AnimatedRoutes() { function AnimatedRoutes() {
const location = useLocation(); const location = useLocation();
@@ -14,9 +15,10 @@ function AnimatedRoutes() {
<Routes location={location} key={location.pathname}> <Routes location={location} key={location.pathname}>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/portal" element={<Portal />} /> <Route path="/portal" element={<Portal />} />
{/* Lockli Privacy Policy yalnızca URL'den erişilecek */}
<Route path="/lockli/privacy" element={<LockliPrivacy />} /> <Route path="/lockli/privacy" element={<LockliPrivacy />} />
<Route path="/projects/lockli" element={<PasswordManagerDetail />} />
</Routes> </Routes>
</AnimatePresence> </AnimatePresence>
); );
@@ -30,4 +32,4 @@ function App() {
); );
} }
export default App; export default App;

View File

@@ -3,11 +3,10 @@ import { motion, AnimatePresence } from 'framer-motion';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { import {
Terminal, Globe, Shield, Database, Terminal, Globe, Shield, Database,
Github, Twitter, Linkedin, Mail, Lock, X, ChevronRight, ExternalLink, Mail, Lock, X, ChevronRight, ExternalLink, Github, Linkedin,
Coffee, Zap, Smartphone, Layers, Box, Server, Loader2 // Loader2 eklendi Coffee, Zap, Smartphone, Layers, Box, Server, Loader2
} from 'lucide-react'; } from 'lucide-react';
// --- YETENEKLER ---
const skills = [ const skills = [
{ name: "Java", icon: <Coffee size={20} />, desc: "Core & Enterprise" }, { name: "Java", icon: <Coffee size={20} />, desc: "Core & Enterprise" },
{ name: "Spring Boot", icon: <Zap size={20} />, desc: "Microservices" }, { name: "Spring Boot", icon: <Zap size={20} />, desc: "Microservices" },
@@ -27,10 +26,10 @@ const projects = [
link: "#" link: "#"
}, },
{ {
title: "Mobil Sosyal Medya Uygulaması", title: "Mobil Offline Parola Yöneticisi",
desc: "Flutter kullanılarak geliştirilen, hem iOS hem Android uyumlu modern arayüzlü sosyal medya uygulaması.", desc: "Flutter kullanılarak geliştirilen, hem iOS hem Android uyumlu modern arayüzlü tamamen local parola yöneticisi.",
tags: ["Flutter", "Dart", "Rest API"], tags: ["Flutter", "Dart", "HiveDB"],
link: "#" link: "/projects/lockli"
}, },
{ {
title: "Linux Tabanlı Sunucu Altyapısı", title: "Linux Tabanlı Sunucu Altyapısı",
@@ -45,7 +44,6 @@ export default function Home() {
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
// Yeni State'ler
const [error, setError] = useState(""); const [error, setError] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -73,13 +71,10 @@ export default function Home() {
const data = await response.json(); const data = await response.json();
if (response.ok) { if (response.ok) {
// Başarılı giriş: Token'ı sakla
// Backend'den { token: "..." } veya { accessToken: "..." } döndüğünü varsayıyoruz.
sessionStorage.setItem('authToken', data.token || data.accessToken); sessionStorage.setItem('authToken', data.token || data.accessToken);
sessionStorage.setItem('user', JSON.stringify(data.user)); // Opsiyonel: Kullanıcı bilgisini sakla sessionStorage.setItem('user', JSON.stringify(data.user));
navigate('/portal'); navigate('/portal');
} else { } else {
// Backend'den gelen hata mesajını göster
setError(data.message || "Giriş başarısız. Bilgileri kontrol edin."); setError(data.message || "Giriş başarısız. Bilgileri kontrol edin.");
} }
} catch (err) { } catch (err) {
@@ -190,7 +185,6 @@ export default function Home() {
</div> </div>
</section> </section>
{/* --- PROJELER --- */}
<section id="projects" className="py-20 px-6 bg-slate-900/50"> <section id="projects" className="py-20 px-6 bg-slate-900/50">
<div className="max-w-6xl mx-auto"> <div className="max-w-6xl mx-auto">
<h2 className="text-3xl font-bold text-white mb-12 flex items-center gap-3"> <h2 className="text-3xl font-bold text-white mb-12 flex items-center gap-3">
@@ -198,7 +192,14 @@ export default function Home() {
</h2> </h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{projects.map((project, index) => ( {projects.map((project, index) => (
<div key={index} className="glass-panel p-6 rounded-2xl hover:-translate-y-1 transition-transform duration-300 flex flex-col h-full"> <div key={index} onClick={() => {
if (project.link.startsWith('/')) {
navigate(project.link);
}
else if (project.link !== '#') {
window.open(project.link, '_blank');
}
}} className="glass-panel p-6 rounded-2xl hover:-translate-y-1 transition-transform duration-300 flex flex-col h-full cursor-pointer group">
<h3 className="text-xl font-bold text-white mb-3">{project.title}</h3> <h3 className="text-xl font-bold text-white mb-3">{project.title}</h3>
<p className="text-slate-400 mb-6 text-sm leading-relaxed flex-grow">{project.desc}</p> <p className="text-slate-400 mb-6 text-sm leading-relaxed flex-grow">{project.desc}</p>
<div className="flex flex-wrap gap-2 mb-6"> <div className="flex flex-wrap gap-2 mb-6">
@@ -208,16 +209,15 @@ export default function Home() {
</span> </span>
))} ))}
</div> </div>
<a href={project.link} className="inline-flex items-center text-sm font-semibold text-white hover:text-orange-400 transition-colors mt-auto"> <div className="inline-flex items-center text-sm font-semibold text-white group-hover:text-orange-400 transition-colors mt-auto">
İncele <ExternalLink size={14} className="ml-2" /> İncele <ExternalLink size={14} className="ml-2" />
</a> </div>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</section> </section>
{/* --- İLETİŞİM --- */}
<section id="contact" className="py-20 px-6 text-center"> <section id="contact" className="py-20 px-6 text-center">
<div className="max-w-2xl mx-auto"> <div className="max-w-2xl mx-auto">
<h2 className="text-3xl font-bold text-white mb-6">İletişime Geç</h2> <h2 className="text-3xl font-bold text-white mb-6">İletişime Geç</h2>
@@ -240,7 +240,6 @@ export default function Home() {
</div> </div>
</section> </section>
{/* --- LOGİN MODAL --- */}
<AnimatePresence> <AnimatePresence>
{isModalOpen && ( {isModalOpen && (
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4"> <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">

View File

@@ -0,0 +1,155 @@
import React from 'react';
import { motion } from 'framer-motion';
import { useNavigate } from 'react-router-dom';
import {
ArrowLeft, Shield, Smartphone, Lock,
WifiOff, Database, Apple, Play, CheckCircle
} from 'lucide-react';
export default function PasswordManagerDetail() {
const navigate = useNavigate();
// Özellik listesi
const features = [
"AES-256 Bit Şifreleme",
"İnternet İzni Gerektirmez (Tamamen Offline)",
"Biyometrik Giriş (FaceID / TouchID)",
"Otomatik Parola Oluşturucu",
"Kategorize Edilmiş Kayıtlar",
"Yedekleme ve Geri Yükleme (Şifreli)"
];
return (
<div className="bg-slate-900 min-h-screen text-slate-200 selection:bg-orange-500 selection:text-white pb-20">
{/* --- NAVBAR / GERİ DÖN --- */}
<nav className="fixed top-0 left-0 right-0 z-50 glass-panel border-b border-white/10 px-6 py-4">
<div className="max-w-6xl mx-auto flex items-center">
<button
onClick={() => navigate(-1)}
className="flex items-center gap-2 text-slate-400 hover:text-white transition-colors"
>
<ArrowLeft size={20} />
<span className="font-medium">Ana Sayfaya Dön</span>
</button>
</div>
</nav>
{/* --- İÇERİK --- */}
<div className="max-w-6xl mx-auto px-6 pt-32">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
{/* --- SOL TARAFTAKİ GÖRSEL ALANI --- */}
<motion.div
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="flex flex-col items-center"
>
{/* Logo ve Başlık (Mobil Görünüm için) */}
<div className="flex items-center gap-4 mb-8 lg:hidden">
<div className="p-4 bg-orange-500/10 rounded-2xl border border-orange-500/20">
<Shield size={40} className="text-orange-500" />
</div>
<h1 className="text-2xl font-bold text-white">Mobil Offline<br/>Parola Yöneticisi</h1>
</div>
{/* Telefon Mockup'ı */}
<div className="relative w-[280px] h-[580px] bg-slate-800 rounded-[3rem] border-8 border-slate-700 shadow-2xl shadow-orange-500/10 flex items-center justify-center overflow-hidden">
{/* Ekran İçi Temsili Görsel */}
<div className="absolute inset-0 bg-slate-900 flex flex-col items-center justify-center p-6 text-center">
<Lock size={64} className="text-orange-500 mb-4 opacity-50" />
<h3 className="text-xl font-bold text-white mb-2">Güvenli Kasa</h3>
<p className="text-xs text-slate-500">Tüm verileriniz cihazınızda şifreli olarak saklanır.</p>
<div className="w-full mt-8 space-y-3">
<div className="h-12 bg-slate-800 rounded-lg w-full animate-pulse"></div>
<div className="h-12 bg-slate-800 rounded-lg w-full animate-pulse delay-75"></div>
<div className="h-12 bg-slate-800 rounded-lg w-full animate-pulse delay-150"></div>
</div>
</div>
{/* Kamera Çentiği */}
<div className="absolute top-0 w-32 h-6 bg-slate-700 rounded-b-xl z-10"></div>
</div>
{/* Alt Bilgi Kartları */}
<div className="flex gap-4 mt-8">
<div className="bg-slate-800/50 p-3 rounded-lg border border-white/5 text-center w-24">
<Smartphone className="mx-auto mb-2 text-blue-400" size={20} />
<span className="text-xs text-slate-400">iOS & Android</span>
</div>
<div className="bg-slate-800/50 p-3 rounded-lg border border-white/5 text-center w-24">
<WifiOff className="mx-auto mb-2 text-red-400" size={20} />
<span className="text-xs text-slate-400">%100 Offline</span>
</div>
<div className="bg-slate-800/50 p-3 rounded-lg border border-white/5 text-center w-24">
<Database className="mx-auto mb-2 text-green-400" size={20} />
<span className="text-xs text-slate-400">Hive DB</span>
</div>
</div>
</motion.div>
{/* --- SAĞ TARAFTAKİ DETAYLAR --- */}
<motion.div
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
{/* Logo ve Başlık (Desktop) */}
<div className="hidden lg:flex items-center gap-6 mb-8">
<div className="p-5 bg-gradient-to-br from-orange-500/20 to-red-500/20 rounded-3xl border border-orange-500/30 shadow-lg shadow-orange-500/10">
<Shield size={48} className="text-orange-500" />
</div>
<div>
<h1 className="text-4xl font-bold text-white mb-2">Mobil Offline Parola Yöneticisi</h1>
<span className="px-3 py-1 bg-white/5 rounded-full text-xs text-slate-400 border border-white/10">v1.2.0 Stable</span>
</div>
</div>
<h2 className="text-xl font-semibold text-white mb-4">Proje Hakkında</h2>
<p className="text-slate-400 leading-relaxed mb-8 text-lg">
Günümüzdeki bulut tabanlı parola yöneticilerinin güvenlik risklerine karşı geliştirilmiş, tamamen yerel çalışan bir mobil uygulamadır.
Hiçbir veriniz sunucuya gönderilmez. Flutter ile geliştirilen uygulama, Hive veritabanı kullanarak verileri cihazınızın belleğinde
şifreli (AES-256) olarak saklar.
</p>
<h2 className="text-xl font-semibold text-white mb-4">Öne Çıkan Özellikler</h2>
<ul className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-10">
{features.map((item, index) => (
<li key={index} className="flex items-start gap-2 text-slate-300 text-sm">
<CheckCircle size={18} className="text-orange-500 shrink-0 mt-0.5" />
{item}
</li>
))}
</ul>
<div className="border-t border-white/10 pt-8">
<h3 className="text-sm font-semibold text-slate-400 uppercase tracking-wider mb-4">Şimdi İndirin</h3>
<div className="flex flex-wrap gap-4">
{/* App Store Butonu */}
<button className="flex items-center gap-3 bg-white text-black px-6 py-3 rounded-xl hover:bg-slate-200 transition-colors">
<Apple size={28} fill="currentColor" />
<div className="text-left">
<div className="text-[10px] font-medium leading-none mb-1">Download on the</div>
<div className="text-lg font-bold leading-none">App Store</div>
</div>
</button>
{/* Google Play Butonu */}
<button className="flex items-center gap-3 bg-transparent border border-white/20 text-white px-6 py-3 rounded-xl hover:bg-white/5 transition-colors">
<Play size={26} fill="currentColor" />
<div className="text-left">
<div className="text-[10px] font-medium leading-none mb-1 uppercase">Get it on</div>
<div className="text-lg font-bold leading-none">Google Play</div>
</div>
</button>
</div>
</div>
</motion.div>
</div>
</div>
</div>
);
}