Görev Takip Sistemi Kaynak Kodları
Ön İzleme : https://www.bilgiveteknoloji.com/demo-gorev-takip-sistemi/
Anasayfa
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Siber Güvenlik Görev Takip & Dashboard</title>
<style>
:root{
--bg:#f7f9fc;
--card:#ffffff;
--ink:#0f172a;
--muted:#6b7280;
--line:#e5e7eb;
--accent:#2563eb;
--accent-2:#10b981;
--danger:#ef4444;
--warn:#f59e0b;
--radius:16px;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,Segoe UI,Roboto,Helvetica,Arial}
.container{max-width:1200px;margin:24px auto;padding:0 16px}
header{display:flex;gap:16px;align-items:center;justify-content:space-between;margin-bottom:16px}
.title{font-size:22px;font-weight:700}
.subtitle{color:var(--muted)}
/* Cards */
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 6px 14px rgba(2,8,20,.06)}
.card h3{margin:0 0 8px 0;font-size:16px}
.card .body{padding:16px}
/* Dashboard summary */
.kpi{display:flex;align-items:center;justify-content:space-between;border-radius:12px;padding:12px 14px;background:#fafafa;border:1px solid var(--line)}
.kpi .val{font-size:20px;font-weight:700}
.prog{height:8px;background:#eef2ff;border-radius:8px;overflow:hidden;border:1px solid var(--line)}
.prog > span{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--accent-2));width:0%}
/* Tabs */
.tabs{display:flex;gap:8px;flex-wrap:wrap}
.tab{border:1px solid var(--line);background:var(--card);padding:8px 12px;border-radius:999px;cursor:pointer;color:var(--muted)}
.tab.active{background:var(--accent);border-color:var(--accent);color:#fff}
/* Tables */
table{width:100%;border-collapse:collapse}
th,td{padding:10px;border-bottom:1px solid var(--line);vertical-align:top}
th{text-align:left;color:var(--muted);font-weight:600;font-size:13px}
tbody tr.done td{background:rgba(16,185,129,.06)}
select,input,textarea,button{font:inherit}
select,input[type="text"],input[type="date"],textarea{width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:#fff}
textarea{resize:vertical;min-height:36px}
.row-actions{display:flex;gap:8px}
.btn{border:1px solid var(--line);background:#fff;border-radius:10px;padding:8px 10px;cursor:pointer}
.btn.primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn.ghost{background:transparent}
.btn.danger{border-color:var(--danger);color:var(--danger);background:#fff}
.badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:12px;border:1px solid var(--line);background:#fff}
.badge.ok{border-color:var(--accent-2);color:var(--accent-2)}
.badge.no{border-color:var(--danger);color:var(--danger)}
/* Layout sizing */
.col-12{grid-column:span 12}
.col-6{grid-column:span 6}
.col-4{grid-column:span 4}
.col-3{grid-column:span 3}
@media (max-width:900px){.col-6,.col-4,.col-3{grid-column:span 12}}
</style>
</head>
<body>
<div class="container">
<header>
<div>
<div class="title">Siber Güvenlik Görev Takip & Dashboard</div>
<div class="subtitle">4 grup (Red, Blue, Forensics, Threat Intel) – Durumlar anlık özetlenir</div>
</div>
<div class="tabs" id="tabs">
<button class="tab active" data-target="dashboard">Dashboard</button>
<button class="tab" data-target="Red Team">Red Team</button>
<button class="tab" data-target="Blue Team">Blue Team</button>
<button class="tab" data-target="Forensics">Forensics</button>
<button class="tab" data-target="Threat Intel">Threat Intel</button>
</div>
</header>
<!-- DASHBOARD -->
<section class="grid" id="view-dashboard">
<div class="card col-12">
<div class="body">
<h3>Genel Özet</h3>
<div class="grid">
<div class="col-3">
<div class="kpi">
<div>
<div>Toplam Görev</div>
<div class="subtitle">Tüm gruplar</div>
</div>
<div class="val" id="kpi-total">0</div>
</div>
</div>
<div class="col-3">
<div class="kpi">
<div>
<div>Tamamlanan</div>
<div class="subtitle">Yapıldı</div>
</div>
<div class="val" id="kpi-done">0</div>
</div>
</div>
<div class="col-3">
<div class="kpi">
<div>
<div>Açık</div>
<div class="subtitle">Yapılmadı</div>
</div>
<div class="val" id="kpi-open">0</div>
</div>
</div>
<div class="col-3">
<div class="kpi">
<div>
<div>Tamamlanma Oranı</div>
<div class="subtitle">%</div>
</div>
<div class="val" id="kpi-rate">%0</div>
</div>
</div>
</div>
</div>
</div>
<div class="card col-12">
<div class="body">
<h3>Grup Bazlı Durum</h3>
<div class="grid" id="group-summaries">
<!-- JS ile doldurulacak -->
</div>
</div>
</div>
<div class="card col-12">
<div class="body">
<h3>Tüm Görevler (Detay)</h3>
<div style="overflow:auto">
<table id="allTable">
<thead>
<tr>
<th>Grup</th>
<th>Tarih</th>
<th>Görev</th>
<th>Durum</th>
<th>Sorumlu</th>
<th>Notlar</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</section>
<!-- GROUP VIEWS (Red, Blue, Forensics, Threat Intel) -->
<section id="view-Red Team" class="grid" hidden></section>
<section id="view-Blue Team" class="grid" hidden></section>
<section id="view-Forensics" class="grid" hidden></section>
<section id="view-Threat Intel" class="grid" hidden></section>
</div>
<script>
// Basit veri modeli (tarayıcı belleğinde). İstersen localStorage kullanılabilir.
const GROUPS = ["Red Team","Blue Team","Forensics","Threat Intel"];
const store = {
"Red Team": [
{date: today(-1), task:"Phishing kampanya simülasyonu", status:"Yapılmadı", owner:"Ali", note:"Onay bekliyor"},
{date: today(-3), task:"İç ağ sızma testi", status:"Yapıldı", owner:"Ayşe", note:"Raporlandı"}
],
"Blue Team": [
{date: today(0), task:"EDR kural güncelleme", status:"Yapıldı", owner:"Mehmet", note:"Yeni IOC eklendi"}
],
"Forensics": [
{date: today(-2), task:"Disk imajı analizi", status:"Yapılmadı", owner:"Zeynep", note:"Zaman çizelgesi çıkarılıyor"}
],
"Threat Intel": [
{date: today(-5), task:"APT X TTP takibi", status:"Yapıldı", owner:"Can", note:"Rapor/IOC paylaşıldı"}
]
};
function today(offsetDays=0){
const d=new Date(); d.setDate(d.getDate()+offsetDays);
return d.toISOString().slice(0,10);
}
// UI: Tab geçişleri
const tabs=document.getElementById('tabs');
tabs.addEventListener('click', (e)=>{
if(!e.target.classList.contains('tab')) return;
document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));
e.target.classList.add('active');
const target=e.target.dataset.target;
showView(target);
});
function showView(target){
// hide all
document.querySelectorAll('section[id^="view-"]').forEach(s=>s.hidden=true);
if(target==='dashboard'){
document.getElementById('view-dashboard').hidden=false;
refreshDashboard();
}else{
const id = 'view-'+target;
const sec=document.getElementById(id);
sec.hidden=false;
renderGroup(target, sec);
}
}
// Grup sayfasını çiz
function renderGroup(groupName, container){
container.innerHTML='';
const card = document.createElement('div');
card.className='card col-12';
card.innerHTML=`<div class="body">
<h3>${groupName} Görevleri</h3>
<div class="row-actions" style="margin: 8px 0 12px;">
<button class="btn primary" id="addRow">Yeni Görev Ekle</button>
<button class="btn" id="saveData">Verileri Kaydet (tarayıcı)
</button>
</div>
<div style="overflow:auto">
<table>
<thead>
<tr>
<th>Tarih</th>
<th>Görev</th>
<th>Durum</th>
<th>Sorumlu</th>
<th>Notlar</th>
<th></th>
</tr>
</thead>
<tbody id="tbody"></tbody>
</table>
</div>
</div>`;
container.appendChild(card);
const tbody = card.querySelector('#tbody');
function drawRows(){
tbody.innerHTML='';
store[groupName].forEach((row,idx)=>{
const tr=document.createElement('tr');
if(row.status==='Yapıldı') tr.classList.add('done');
tr.innerHTML=`
<td><input type="date" value="${row.date}"></td>
<td><input type="text" value="${escapeHtml(row.task)}" placeholder="Görev başlığı"></td>
<td>
<select>
<option ${row.status==='Yapılmadı'?'selected':''}>Yapılmadı</option>
<option ${row.status==='Yapıldı'?'selected':''}>Yapıldı</option>
</select>
</td>
<td><input type="text" value="${escapeHtml(row.owner||'')}"></td>
<td><textarea>${escapeHtml(row.note||'')}</textarea></td>
<td class="row-actions">
<button class="btn ghost" data-act="dup" data-idx="${idx}">Çoğalt</button>
<button class="btn danger" data-act="del" data-idx="${idx}">Sil</button>
</td>`;
tbody.appendChild(tr);
});
}
drawRows();
// Ekle
card.querySelector('#addRow').addEventListener('click',()=>{
store[groupName].unshift({date: today(), task:'', status:'Yapılmadı', owner:'', note:''});
drawRows();
refreshDashboard();
});
// Kaydet (tarayıcı localStorage)
card.querySelector('#saveData').addEventListener('click',()=>{
localStorage.setItem('cyberTasks', JSON.stringify(store));
alert('Veriler tarayıcıya kaydedildi.');
});
// Satır olayları (input/select/textarea değişimleri, sil/çoğalt)
tbody.addEventListener('input', syncFromTable);
tbody.addEventListener('change', syncFromTable);
tbody.addEventListener('click', (e)=>{
const btn=e.target.closest('button');
if(!btn) return;
const idx=+btn.dataset.idx;
const act=btn.dataset.act;
if(act==='del'){
if(confirm('Bu görevi silmek istiyor musun?')){
store[groupName].splice(idx,1);
}
}
if(act==='dup'){
const c = JSON.parse(JSON.stringify(store[groupName][idx]));
store[groupName].splice(idx,0,c);
}
drawRows();
refreshDashboard();
});
function syncFromTable(){
const rows=[...tbody.querySelectorAll('tr')];
store[groupName]=rows.map(tr=>{
const [d,task,sel,owner,note] = [
tr.querySelector('input[type="date"]').value,
tr.querySelector('input[type="text"]').value,
tr.querySelector('select').value,
tr.querySelectorAll('input[type="text"]')[1]?.value || '',
tr.querySelector('textarea').value
];
if(sel==='Yapıldı') tr.classList.add('done'); else tr.classList.remove('done');
return {date:d, task, status:sel, owner, note};
});
refreshDashboard();
}
}
// Dashboard hesaplamaları
function refreshDashboard(){
const all = GROUPS.flatMap(g=>store[g].map(r=>({...r, group:g})));
// KPIs
const total = all.length;
const done = all.filter(x=>x.status==='Yapıldı').length;
const open = total - done;
const rate = total? Math.round(done*100/total):0;
setText('kpi-total', total);
setText('kpi-done', done);
setText('kpi-open', open);
setText('kpi-rate', `%${rate}`);
// Grup özet kartları
const wrap=document.getElementById('group-summaries');
wrap.innerHTML='';
GROUPS.forEach(g=>{
const arr=store[g];
const t=arr.length;
const d=arr.filter(x=>x.status==='Yapıldı').length;
const o=t-d; const r=t?Math.round(d*100/t):0;
const card=document.createElement('div');
card.className='card col-3';
card.innerHTML=`<div class="body">
<h3>${g}</h3>
<div class="grid" style="gap:10px;grid-template-columns:1fr 1fr;">
<div class="kpi"><div>Toplam</div><div class="val">${t}</div></div>
<div class="kpi"><div>Yapıldı</div><div class="val">${d}</div></div>
<div class="kpi"><div>Açık</div><div class="val">${o}</div></div>
<div style="grid-column:span 2">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
<span class="subtitle">Tamamlanma</span>
<span class="badge ${r===100?'ok':(r===0?'no':'')}">%${r}</span>
</div>
<div class="prog"><span style="width:${r}%"></span></div>
</div>
</div>
</div>`;
wrap.appendChild(card);
});
// Detay tablo
const tbody=document.querySelector('#allTable tbody');
tbody.innerHTML='';
all.sort((a,b)=> (a.group.localeCompare(b.group) || b.date.localeCompare(a.date)) );
all.forEach(row=>{
const tr=document.createElement('tr');
if(row.status==='Yapıldı') tr.classList.add('done');
tr.innerHTML=`<td>${row.group}</td>
<td>${row.date}</td>
<td>${escapeHtml(row.task)}</td>
<td>${row.status}</td>
<td>${escapeHtml(row.owner||'')}</td>
<td>${escapeHtml(row.note||'')}</td>`;
tbody.appendChild(tr);
});
}
function setText(id, v){document.getElementById(id).textContent=v}
function escapeHtml(s){return (s+"").replace(/[&<>"']/g, c=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[c]))}
// localStorage'dan yükleme (varsa)
const saved = localStorage.getItem('cyberTasks');
if(saved){
try{ const obj=JSON.parse(saved); for(const g of GROUPS){ if(Array.isArray(obj[g])) store[g]=obj[g]; } }catch{}
}
// İlk çizim
refreshDashboard();
// Varsayılan görünüm: Dashboard açık (zaten)
</script>
</body>
</html>
Arşiv Sayfası
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Siber Güvenlik Görev Takip & Dashboard</title>
<style>
:root{
--bg:#f7f9fc;
--card:#ffffff;
--ink:#0f172a;
--muted:#6b7280;
--line:#e5e7eb;
--accent:#2563eb;
--accent-2:#10b981;
--danger:#ef4444;
--warn:#f59e0b;
--radius:16px;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,Segoe UI,Roboto,Helvetica,Arial}
.container{max-width:1200px;margin:24px auto;padding:0 16px}
header{display:flex;gap:16px;align-items:center;justify-content:space-between;margin-bottom:16px}
.title{font-size:22px;font-weight:700}
.subtitle{color:var(--muted)}
/* Cards */
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:0 6px 14px rgba(2,8,20,.06)}
.card h3{margin:0 0 8px 0;font-size:16px}
.card .body{padding:16px}
/* Dashboard summary */
.kpi{display:flex;align-items:center;justify-content:space-between;border-radius:12px;padding:12px 14px;background:#fafafa;border:1px solid var(--line)}
.kpi .val{font-size:20px;font-weight:700}
.prog{height:8px;background:#eef2ff;border-radius:8px;overflow:hidden;border:1px solid var(--line)}
.prog > span{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--accent-2));width:0%}
/* Tabs */
.tabs{display:flex;gap:8px;flex-wrap:wrap}
.tab{border:1px solid var(--line);background:var(--card);padding:8px 12px;border-radius:999px;cursor:pointer;color:var(--muted)}
.tab.active{background:var(--accent);border-color:var(--accent);color:#fff}
/* Tables */
table{width:100%;border-collapse:collapse}
th,td{padding:10px;border-bottom:1px solid var(--line);vertical-align:top}
th{text-align:left;color:var(--muted);font-weight:600;font-size:13px}
tbody tr.done td{background:rgba(16,185,129,.06)}
select,input,textarea,button{font:inherit}
select,input[type="text"],input[type="date"],textarea{width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:10px;background:#fff}
textarea{resize:vertical;min-height:36px}
.row-actions{display:flex;gap:8px}
.btn{border:1px solid var(--line);background:#fff;border-radius:10px;padding:8px 10px;cursor:pointer}
.btn.primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn.ghost{background:transparent}
.btn.danger{border-color:var(--danger);color:var(--danger);background:#fff}
.badge{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;font-size:12px;border:1px solid var(--line);background:#fff}
.badge.ok{border-color:var(--accent-2);color:var(--accent-2)}
.badge.no{border-color:var(--danger);color:var(--danger)}
/* Archive specific styles */
.filter-bar {display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 16px; align-items: end;}
.filter-group {display: flex; flex-direction: column; min-width: 150px;}
.filter-group label {font-size: 13px; margin-bottom: 4px; color: var(--muted);}
.search-box {position: relative; min-width: 250px;}
.search-box input {padding-left: 36px;}
.search-box:before {content: "🔍"; position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted);}
.stats-bar {display: flex; gap: 16px; margin: 16px 0; flex-wrap: wrap;}
.stat-item {display: flex; align-items: center; gap: 8px; font-size: 14px;}
.export-actions {display: flex; gap: 8px; justify-content: flex-end; margin-bottom: 16px;}
.highlight {background-color: rgba(255, 255, 0, 0.2);}
/* Layout sizing */
.col-12{grid-column:span 12}
.col-6{grid-column:span 6}
.col-4{grid-column:span 4}
.col-3{grid-column:span 3}
@media (max-width:900px){.col-6,.col-4,.col-3{grid-column:span 12}}
</style>
</head>
<body>
<div class="container">
<header>
<div>
<div class="title">Siber Güvenlik Görev Takip & Dashboard</div>
<div class="subtitle">4 grup (Red, Blue, Forensics, Threat Intel) – Durumlar anlık özetlenir</div>
</div>
<div class="tabs" id="tabs">
<button class="tab active" data-target="dashboard">Dashboard</button>
<button class="tab" data-target="Red Team">Red Team</button>
<button class="tab" data-target="Blue Team">Blue Team</button>
<button class="tab" data-target="Forensics">Forensics</button>
<button class="tab" data-target="Threat Intel">Threat Intel</button>
<button class="tab" data-target="archive">Arşiv</button>
</div>
</header>
<!-- DASHBOARD -->
<section class="grid" id="view-dashboard">
<!-- ... Mevcut dashboard içeriği ... -->
</section>
<!-- GROUP VIEWS (Red, Blue, Forensics, Threat Intel) -->
<section id="view-Red Team" class="grid" hidden></section>
<section id="view-Blue Team" class="grid" hidden></section>
<section id="view-Forensics" class="grid" hidden></section>
<section id="view-Threat Intel" class="grid" hidden></section>
<!-- ARCHIVE VIEW -->
<section id="view-archive" class="grid" hidden>
<div class="card col-12">
<div class="body">
<h3>Görev Arşivi - Detaylı Arama ve Filtreleme</h3>
<div class="filter-bar">
<div class="search-box">
<label for="archive-search">Anahtar Kelime</label>
<input type="text" id="archive-search" placeholder="Görev, not veya sorumlu içinde ara...">
</div>
<div class="filter-group">
<label for="archive-group">Grup</label>
<select id="archive-group">
<option value="">Tüm Gruplar</option>
<option value="Red Team">Red Team</option>
<option value="Blue Team">Blue Team</option>
<option value="Forensics">Forensics</option>
<option value="Threat Intel">Threat Intel</option>
</select>
</div>
<div class="filter-group">
<label for="archive-status">Durum</label>
<select id="archive-status">
<option value="">Tüm Durumlar</option>
<option value="Yapıldı">Yapıldı</option>
<option value="Yapılmadı">Yapılmadı</option>
</select>
</div>
<div class="filter-group">
<label for="archive-date-from">Başlangıç Tarihi</label>
<input type="date" id="archive-date-from">
</div>
<div class="filter-group">
<label for="archive-date-to">Bitiş Tarihi</label>
<input type="date" id="archive-date-to">
</div>
<div class="filter-group">
<label> </label>
<button class="btn primary" id="archive-search-btn">Ara</button>
</div>
<div class="filter-group">
<label> </label>
<button class="btn" id="archive-reset-btn">Sıfırla</button>
</div>
</div>
<div class="export-actions">
<button class="btn" id="archive-export-csv">CSV Olarak Dışa Aktar</button>
<button class="btn" id="archive-export-json">JSON Olarak Dışa Aktar</button>
</div>
<div class="stats-bar">
<div class="stat-item">
<span class="subtitle">Toplam Sonuç:</span>
<span id="archive-total-results">0</span>
</div>
<div class="stat-item">
<span class="subtitle">Tamamlanan:</span>
<span id="archive-done-results">0</span>
</div>
<div class="stat-item">
<span class="subtitle">Tamamlanmayan:</span>
<span id="archive-not-done-results">0</span>
</div>
</div>
<div style="overflow:auto">
<table id="archiveTable">
<thead>
<tr>
<th data-sort="group">Grup ↕</th>
<th data-sort="date">Tarih ↕</th>
<th data-sort="task">Görev ↕</th>
<th data-sort="status">Durum ↕</th>
<th data-sort="owner">Sorumlu ↕</th>
<th>Notlar</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</section>
</div>
<script>
// Basit veri modeli (tarayıcı belleğinde). İstersen localStorage kullanılabilir.
const GROUPS = ["Red Team","Blue Team","Forensics","Threat Intel"];
const store = {
"Red Team": [
{date: today(-1), task:"Phishing kampanya simülasyonu", status:"Yapılmadı", owner:"Ali", note:"Onay bekliyor"},
{date: today(-3), task:"İç ağ sızma testi", status:"Yapıldı", owner:"Ayşe", note:"Raporlandı"}
],
"Blue Team": [
{date: today(0), task:"EDR kural güncelleme", status:"Yapıldı", owner:"Mehmet", note:"Yeni IOC eklendi"}
],
"Forensics": [
{date: today(-2), task:"Disk imajı analizi", status:"Yapılmadı", owner:"Zeynep", note:"Zaman çizelgesi çıkarılıyor"}
],
"Threat Intel": [
{date: today(-5), task:"APT X TTP takibi", status:"Yapıldı", owner:"Can", note:"Rapor/IOC paylaşıldı"}
]
};
function today(offsetDays=0){
const d=new Date(); d.setDate(d.getDate()+offsetDays);
return d.toISOString().slice(0,10);
}
// UI: Tab geçişleri
const tabs=document.getElementById('tabs');
tabs.addEventListener('click', (e)=>{
if(!e.target.classList.contains('tab')) return;
document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));
e.target.classList.add('active');
const target=e.target.dataset.target;
showView(target);
});
function showView(target){
// hide all
document.querySelectorAll('section[id^="view-"]').forEach(s=>s.hidden=true);
if(target==='dashboard'){
document.getElementById('view-dashboard').hidden=false;
refreshDashboard();
} else if(target === 'archive') {
document.getElementById('view-archive').hidden=false;
initArchiveView();
} else {
const id = 'view-'+target;
const sec=document.getElementById(id);
sec.hidden=false;
renderGroup(target, sec);
}
}
// Grup sayfasını çiz
function renderGroup(groupName, container){
// ... Mevcut grup render kodu ...
}
// Dashboard hesaplamaları
function refreshDashboard(){
// ... Mevcut dashboard yenileme kodu ...
}
function setText(id, v){document.getElementById(id).textContent=v}
function escapeHtml(s){return (s+"").replace(/[&<>"']/g, c=>({"&":"&","<":"<",">":">","\"":""","'":"'"}[c]))}
// localStorage'dan yükleme (varsa)
const saved = localStorage.getItem('cyberTasks');
if(saved){
try{ const obj=JSON.parse(saved); for(const g of GROUPS){ if(Array.isArray(obj[g])) store[g]=obj[g]; } }catch{}
}
// Arşiv görünümü işlevleri
function initArchiveView() {
// Tarih aralığı için varsayılan değerleri ayarla (son 30 gün)
const defaultFrom = new Date();
defaultFrom.setDate(defaultFrom.getDate() - 30);
document.getElementById('archive-date-from').value = defaultFrom.toISOString().slice(0,10);
document.getElementById('archive-date-to').value = today();
// Arama butonu
document.getElementById('archive-search-btn').addEventListener('click', performArchiveSearch);
// Sıfırlama butonu
document.getElementById('archive-reset-btn').addEventListener('click', resetArchiveFilters);
// Dışa aktarma butonları
document.getElementById('archive-export-csv').addEventListener('click', exportToCSV);
document.getElementById('archive-export-json').addEventListener('click', exportToJSON);
// Sıralama için başlık tıklamaları
document.querySelectorAll('#archiveTable th[data-sort]').forEach(th => {
th.addEventListener('click', () => {
const sortField = th.getAttribute('data-sort');
const currentDir = th.getAttribute('data-sort-dir') || 'asc';
const newDir = currentDir === 'asc' ? 'desc' : 'asc';
// Tüm sıralama göstergelerini sıfırla
document.querySelectorAll('#archiveTable th[data-sort]').forEach(h => {
h.innerHTML = h.innerHTML.replace(/ ↕| ↑| ↓/, '') + ' ↕';
});
// Mevcut sütunu güncelle
th.setAttribute('data-sort-dir', newDir);
th.innerHTML = th.innerHTML.replace(/ ↕| ↑| ↓/, '') + (newDir === 'asc' ? ' ↑' : ' ↓');
// Sonuçları yeniden sırala ve göster
performArchiveSearch(sortField, newDir);
});
});
// İlk aramayı yap
performArchiveSearch();
}
function performArchiveSearch(sortField = 'date', sortDir = 'desc') {
const searchTerm = document.getElementById('archive-search').value.toLowerCase();
const groupFilter = document.getElementById('archive-group').value;
const statusFilter = document.getElementById('archive-status').value;
const dateFrom = document.getElementById('archive-date-from').value;
const dateTo = document.getElementById('archive-date-to').value;
// Tüm görevleri birleştir
let allTasks = [];
for (const group of GROUPS) {
allTasks = allTasks.concat(store[group].map(task => ({...task, group})));
}
// Filtreleme
let filteredTasks = allTasks.filter(task => {
// Grup filtresi
if (groupFilter && task.group !== groupFilter) return false;
// Durum filtresi
if (statusFilter && task.status !== statusFilter) return false;
// Tarih aralığı filtresi
if (dateFrom && task.date < dateFrom) return false;
if (dateTo && task.date > dateTo) return false;
// Arama terimi filtresi (görev, not, sorumlu alanlarında ara)
if (searchTerm) {
const inTask = task.task.toLowerCase().includes(searchTerm);
const inNote = task.note.toLowerCase().includes(searchTerm);
const inOwner = task.owner.toLowerCase().includes(searchTerm);
if (!inTask && !inNote && !inOwner) return false;
}
return true;
});
// Sıralama
filteredTasks.sort((a, b) => {
let aValue = a[sortField];
let bValue = b[sortField];
// Tarih sıralaması için özel işlem
if (sortField === 'date') {
aValue = new Date(aValue);
bValue = new Date(bValue);
}
if (aValue < bValue) return sortDir === 'asc' ? -1 : 1;
if (aValue > bValue) return sortDir === 'asc' ? 1 : -1;
return 0;
});
// İstatistikleri güncelle
const total = filteredTasks.length;
const done = filteredTasks.filter(t => t.status === 'Yapıldı').length;
const notDone = total - done;
document.getElementById('archive-total-results').textContent = total;
document.getElementById('archive-done-results').textContent = done;
document.getElementById('archive-not-done-results').textContent = notDone;
// Sonuçları göster
renderArchiveResults(filteredTasks, searchTerm);
}
function renderArchiveResults(tasks, searchTerm) {
const tbody = document.querySelector('#archiveTable tbody');
tbody.innerHTML = '';
if (tasks.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" style="text-align: center;">Arama kriterlerinize uygun görev bulunamadı.</td></tr>';
return;
}
tasks.forEach(task => {
const tr = document.createElement('tr');
if (task.status === 'Yapıldı') tr.classList.add('done');
// Vurgulama fonksiyonu
const highlightText = (text) => {
if (!searchTerm || !text) return escapeHtml(text);
const regex = new RegExp(`(${searchTerm})`, 'gi');
return escapeHtml(text).replace(regex, '<span class="highlight">$1</span>');
};
tr.innerHTML = `
<td>${task.group}</td>
<td>${task.date}</td>
<td>${highlightText(task.task)}</td>
<td>${task.status}</td>
<td>${highlightText(task.owner)}</td>
<td>${highlightText(task.note)}</td>
`;
tbody.appendChild(tr);
});
}
function resetArchiveFilters() {
document.getElementById('archive-search').value = '';
document.getElementById('archive-group').value = '';
document.getElementById('archive-status').value = '';
// Varsayılan tarih aralığını sıfırla (son 30 gün)
const defaultFrom = new Date();
defaultFrom.setDate(defaultFrom.getDate() - 30);
document.getElementById('archive-date-from').value = defaultFrom.toISOString().slice(0,10);
document.getElementById('archive-date-to').value = today();
// Sıralama göstergelerini sıfırla
document.querySelectorAll('#archiveTable th[data-sort]').forEach(th => {
th.removeAttribute('data-sort-dir');
th.innerHTML = th.innerHTML.replace(/ ↕| ↑| ↓/, '') + ' ↕';
});
// Tüm görevleri göster
performArchiveSearch();
}
function exportToCSV() {
const searchTerm = document.getElementById('archive-search').value;
const groupFilter = document.getElementById('archive-group').value;
const statusFilter = document.getElementById('archive-status').value;
const dateFrom = document.getElementById('archive-date-from').value;
const dateTo = document.getElementById('archive-date-to').value;
// Tüm görevleri birleştir
let allTasks = [];
for (const group of GROUPS) {
allTasks = allTasks.concat(store[group].map(task => ({...task, group})));
}
// Aynı filtrelemeyi uygula
let filteredTasks = allTasks.filter(task => {
if (groupFilter && task.group !== groupFilter) return false;
if (statusFilter && task.status !== statusFilter) return false;
if (dateFrom && task.date < dateFrom) return false;
if (dateTo && task.date > dateTo) return false;
if (searchTerm) {
const searchLower = searchTerm.toLowerCase();
const inTask = task.task.toLowerCase().includes(searchLower);
const inNote = task.note.toLowerCase().includes(searchLower);
const inOwner = task.owner.toLowerCase().includes(searchLower);
if (!inTask && !inNote && !inOwner) return false;
}
return true;
});
// CSV başlık satırı
let csvContent = "Grup,Tarih,Görev,Durum,Sorumlu,Notlar\n";
// Veri satırları
filteredTasks.forEach(task => {
const row = [
task.group,
task.date,
`"${task.task.replace(/"/g, '""')}"`,
task.status,
task.owner,
`"${(task.note || '').replace(/"/g, '""')}"`
];
csvContent += row.join(',') + '\n';
});
// İndirme işlemi
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
// Dosya adını oluştur
let fileName = 'gorev_raporu';
if (groupFilter) fileName += `_${groupFilter.replace(' ', '_')}`;
if (statusFilter) fileName += `_${statusFilter}`;
if (dateFrom && dateTo) fileName += `_${dateFrom}-${dateTo}`;
fileName += '.csv';
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function exportToJSON() {
const searchTerm = document.getElementById('archive-search').value;
const groupFilter = document.getElementById('archive-group').value;
const statusFilter = document.getElementById('archive-status').value;
const dateFrom = document.getElementById('archive-date-from').value;
const dateTo = document.getElementById('archive-date-to').value;
// Tüm görevleri birleştir
let allTasks = [];
for (const group of GROUPS) {
allTasks = allTasks.concat(store[group].map(task => ({...task, group})));
}
// Aynı filtrelemeyi uygula
let filteredTasks = allTasks.filter(task => {
if (groupFilter && task.group !== groupFilter) return false;
if (statusFilter && task.status !== statusFilter) return false;
if (dateFrom && task.date < dateFrom) return false;
if (dateTo && task.date > dateTo) return false;
if (searchTerm) {
const searchLower = searchTerm.toLowerCase();
const inTask = task.task.toLowerCase().includes(searchLower);
const inNote = task.note.toLowerCase().includes(searchLower);
const inOwner = task.owner.toLowerCase().includes(searchLower);
if (!inTask && !inNote && !inOwner) return false;
}
return true;
});
// Meta veri ekle
const exportData = {
meta: {
exportedAt: new Date().toISOString(),
filters: {
searchTerm,
groupFilter,
statusFilter,
dateFrom,
dateTo
},
totalResults: filteredTasks.length
},
tasks: filteredTasks
};
// İndirme işlemi
const jsonString = JSON.stringify(exportData, null, 2);
const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
// Dosya adını oluştur
let fileName = 'gorev_raporu';
if (groupFilter) fileName += `_${groupFilter.replace(' ', '_')}`;
if (statusFilter) fileName += `_${statusFilter}`;
if (dateFrom && dateTo) fileName += `_${dateFrom}-${dateTo}`;
fileName += '.json';
link.setAttribute('download', fileName);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// İlk çizim
refreshDashboard();
// Varsayılan görünüm: Dashboard açık (zaten)
</script>
</body>
</html>