153 lines
5.7 KiB
TypeScript
153 lines
5.7 KiB
TypeScript
import type { Metadata } from "next";
|
||
|
||
export const metadata: Metadata = { title: "Kaptanın Seyir Defteri" };
|
||
|
||
interface Goal {
|
||
id: string;
|
||
title: string;
|
||
description: string;
|
||
status: "completed" | "in-progress" | "planned" | "paused";
|
||
progress: number;
|
||
category: string;
|
||
deadline?: string;
|
||
notes?: string;
|
||
}
|
||
|
||
const goals: Goal[] = [
|
||
{
|
||
id: "1",
|
||
title: "OSCP Sertifikası",
|
||
description: "Offensive Security Certified Professional sınavını geçmek",
|
||
status: "in-progress",
|
||
progress: 35,
|
||
category: "Sertifika",
|
||
deadline: "2026-12-31",
|
||
notes: "Active Directory modüllerine odaklanılıyor",
|
||
},
|
||
{
|
||
id: "2",
|
||
title: "denizbektas.com.tr Yayına Almak",
|
||
description: "Kişisel siteyi oluşturmak ve yayına almak",
|
||
status: "completed",
|
||
progress: 100,
|
||
category: "Proje",
|
||
},
|
||
{
|
||
id: "3",
|
||
title: "50 HTB Makinesi",
|
||
description: "HackTheBox'ta 50 makine çözmek",
|
||
status: "in-progress",
|
||
progress: 47,
|
||
category: "Pratik",
|
||
},
|
||
{
|
||
id: "4",
|
||
title: "Podcast Başlatmak",
|
||
description: "Türkçe siber güvenlik podcast'i yayınlamak",
|
||
status: "planned",
|
||
progress: 10,
|
||
category: "İçerik",
|
||
deadline: "2026-09-01",
|
||
},
|
||
{
|
||
id: "5",
|
||
title: "Red Team Araç Geliştirme",
|
||
description: "Kendi C2 implant'ımı yazmak (Rust ile)",
|
||
status: "paused",
|
||
progress: 15,
|
||
category: "Geliştirme",
|
||
},
|
||
{
|
||
id: "6",
|
||
title: "10 Infosec Yazısı",
|
||
description: "Kaliteli teknik yazılar yayınlamak",
|
||
status: "in-progress",
|
||
progress: 10,
|
||
category: "İçerik",
|
||
},
|
||
];
|
||
|
||
const statusConfig = {
|
||
completed: { label: "Tamamlandı", color: "#00d4aa", icon: "✓" },
|
||
"in-progress": { label: "Devam Ediyor", color: "#f59e0b", icon: "◉" },
|
||
planned: { label: "Planlandı", color: "#64748b", icon: "◎" },
|
||
paused: { label: "Durduruldu", color: "#ef4444", icon: "⏸" },
|
||
};
|
||
|
||
export default function SeyirDefteriPage() {
|
||
const byCategory = goals.reduce((acc, g) => {
|
||
if (!acc[g.category]) acc[g.category] = [];
|
||
acc[g.category].push(g);
|
||
return acc;
|
||
}, {} as Record<string, Goal[]>);
|
||
|
||
return (
|
||
<div style={{ maxWidth: "100%" }}>
|
||
<div className="page-title">Kaptanın Seyir Defteri</div>
|
||
<p style={{ fontSize: "0.8rem", color: "var(--text-muted)", marginBottom: "2rem" }}>
|
||
Hedeflerim ve ilerleme durumum
|
||
</p>
|
||
|
||
{/* Summary */}
|
||
<div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: "0.5rem", marginBottom: "2rem" }}>
|
||
{(["completed", "in-progress", "planned", "paused"] as const).map((s) => {
|
||
const count = goals.filter((g) => g.status === s).length;
|
||
const cfg = statusConfig[s];
|
||
return (
|
||
<div key={s} className="card" style={{ textAlign: "center", padding: "0.75rem" }}>
|
||
<div style={{ fontSize: "1.2rem", fontWeight: 700, color: cfg.color }}>{count}</div>
|
||
<div style={{ fontSize: "0.65rem", color: "var(--text-muted)", marginTop: "0.2rem" }}>{cfg.label}</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* Goals by category */}
|
||
{Object.entries(byCategory).map(([cat, catGoals]) => (
|
||
<div key={cat} style={{ marginBottom: "1.5rem" }}>
|
||
<div className="section-header">{cat}</div>
|
||
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
|
||
{catGoals.map((goal) => {
|
||
const cfg = statusConfig[goal.status];
|
||
return (
|
||
<div key={goal.id} className="card">
|
||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "1rem", marginBottom: "0.4rem" }}>
|
||
<div>
|
||
<div style={{ display: "flex", alignItems: "center", gap: "0.4rem", marginBottom: "0.2rem" }}>
|
||
<span style={{ color: cfg.color, fontSize: "0.7rem" }}>{cfg.icon}</span>
|
||
<h3 style={{ fontSize: "0.9rem", fontWeight: 600, color: "var(--text)" }}>{goal.title}</h3>
|
||
</div>
|
||
<p style={{ fontSize: "0.78rem", color: "var(--text-muted)" }}>{goal.description}</p>
|
||
</div>
|
||
<span style={{ fontSize: "0.65rem", background: "var(--bg)", border: `1px solid ${cfg.color}`, color: cfg.color, padding: "0.15rem 0.4rem", borderRadius: "3px", flexShrink: 0 }}>
|
||
{cfg.label}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Progress bar */}
|
||
<div style={{ marginTop: "0.75rem" }}>
|
||
<div style={{ display: "flex", justifyContent: "space-between", fontSize: "0.7rem", color: "var(--text-muted)", marginBottom: "0.25rem" }}>
|
||
<span>İlerleme</span>
|
||
<span style={{ color: cfg.color, fontWeight: 600 }}>{goal.progress}%</span>
|
||
</div>
|
||
<div style={{ height: "4px", background: "var(--bg)", borderRadius: "2px", overflow: "hidden" }}>
|
||
<div style={{ height: "100%", width: `${goal.progress}%`, background: cfg.color, borderRadius: "2px", transition: "width 0.3s" }} />
|
||
</div>
|
||
</div>
|
||
|
||
{(goal.deadline || goal.notes) && (
|
||
<div style={{ marginTop: "0.5rem", fontSize: "0.72rem", color: "var(--text-muted)", display: "flex", gap: "1rem", flexWrap: "wrap" }}>
|
||
{goal.deadline && <span>🗓 {new Date(goal.deadline).toLocaleDateString("tr-TR", { year: "numeric", month: "long" })}</span>}
|
||
{goal.notes && <span>📝 {goal.notes}</span>}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|