Dönem olarak hazırlayacağınız Raporlarda, kurumsal ciddiyet içeren bir form ile sunmak amacıyla yapılan çalışmadır. Not defterine ".html" uzantılı olarak kaydedilmesi yeterlidir.
Şirket logosu "logo.jpg" ismiyle aynı klasörde yer almalıdır. Başlıklardaki içerikleri (Tarih aralığı, Kurum ismi veya Departman, Müdürlük, Başkanlık) gibi ifadeleri "Mevcuttaki kayıtlarda görünenleri "Ctrl+F" ile aratıp kalıcı olarak düzenleyebilirsiniz. Yeni geliştirme ile, başlık alanındaki başlıklara art arda 5 kez basılınca değişime izin vermektedir.
Form açıldığında, içerik boş gelecek şekilde hazırlanmıştır. İçerisine ekleyebileceğiniz data, Örnek olması ve üzerinde, Ekleme, Silme, Değişiklik yapabilmeniz için kodun içine gömülmüştür.

"Şablon" butonuna bastığınızda, Kod için gömülü dosya bilgisayarınıza inecektir.
Excel dosyasını çalıştırdığınızda Örnek içerik aşağıdaki gibi görünecektir. Dört grup olarak lan yer almaktadır. İlgili Alanlarda, düzenleme, ekleme, çıkarma ile dosyayı kaydedin.
"Excel Yükle" butonuna basarak, faaliyetinizle ilgili olarak düzenlediğiniz dosyayı seçin. Prestijli, tek sayfalık özet raporunuz hazır olacaktır.

"Yazdır" butonuna basarak "PDF" formatında kaydedip, dijital olarak "mail, Whatsapp" ile paylaşabilir, sonrasında yazdırabilirsiniz

HTML Kod:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>2025-2026 Yıllık Faaliyet Raporu</title>
<script src="https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js"></script>
<style>
:root { --primary: #1a365d; --bg: #f4f7f9; --text: #2d3748; --accent: #2b6cb0; }
* { box-sizing: border-box; }
body {
background-color: var(--bg); color: var(--text); padding: 10px; margin: 0;
font-family: 'Segoe UI', Tahoma, sans-serif; font-size: 12px;
min-height: 100vh; min-height: 100dvh;
}
/* Ekranın kullanılabilir genişliğini ve yüksekliğini kaplar. */
.container { width: 100%; max-width: none; margin: 0 auto; min-height: calc(100vh - 20px); min-height: calc(100dvh - 20px); display: flex; flex-direction: column; }
/* BAŞLIK ALANI */
.header {
display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
background: #fff; padding: 12px 15px; margin-bottom: 20px; border-radius: 4px;
border-bottom: 3px solid var(--primary); box-shadow: 0 1px 3px rgba(0,0,0,0.1); gap: 12px;
}
.header-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.header-left > div { min-width: 0; }
.style-bold-big { font-size: 0.85rem; font-weight: 800; color: var(--primary); }
.style-small-light { font-size: 0.7rem; color: #666; font-weight: normal; }
.header-right { font-size: 0.8rem; font-weight: 800; color: var(--primary); text-align: right; }
.logo { height: 30px; }
.header [data-editable] { overflow-wrap: anywhere; cursor: pointer; touch-action: manipulation; }
.header [data-editable]:not([contenteditable="true"]) { user-select: none; }
.header [contenteditable="true"] { outline: 2px solid var(--accent); outline-offset: 4px; background: #eff6ff; cursor: text; border-radius: 2px; }
/* GRID VE KART YAPISI */
.main-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 15px; flex: 1; }
.col { display: flex; flex-direction: column; gap: 15px; min-width: 0; }
.card { background: white; padding: 12px; border-radius: 4px; border: 1px solid #e2e8f0; min-height: 100px; flex: 1; overflow-wrap: anywhere; }
.card-title {
font-size: 0.8rem; font-weight: 800; color: var(--primary);
margin-bottom: 15px; border-bottom: 1px solid #edf2f7; padding-bottom: 8px; text-transform: uppercase;
}
.data-row { display: flex; justify-content: space-between; font-size: 0.75rem; margin-bottom: 8px; gap: 12px; }
.data-row > span:first-child { min-width: 0; }
/* Sayı ve değerler alanı kalın ve siyah */
.data-val { font-weight: 800; color: #000000; flex-shrink: 0; }
.item-text { font-size: 0.75rem; margin-bottom: 8px; padding: 4px 0; border-bottom: 1px dashed #f1f5f9; }
.progress-bar { height: 8px; background: #edf2f7; border-radius: 4px; margin: 4px 0 10px 0; overflow: hidden; border: 1px solid #cbd5e1; }
.progress-fill { height: 100%; background: var(--accent); transition: width 0.5s ease; }
.toolbar { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 10px; justify-content: flex-end; }
button { padding: 4px 10px; cursor: pointer; border: 1px solid var(--primary); background: white; font-size: 10px; border-radius: 3px; }
@media screen and (max-width: 700px) {
.main-grid { grid-template-columns: minmax(0, 1fr); }
.header { align-items: flex-start; }
.header-right { text-align: left; }
}
@media print {
@page { size: A4 landscape; margin: 5mm; }
html, body { background: white; min-height: auto; -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
* { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
.toolbar { display: none !important; }
.container { width: 100%; min-height: auto; display: block; }
.card { break-inside: avoid; }
.header [contenteditable="true"] { outline: none; background: transparent; }
}
</style>
</head>
<body>
<div class="container">
<div class="toolbar">
<button onclick="document.getElementById('fileInput').click()">📥 Excel Yükle</button>
<button onclick="App.downloadSampleExcel()">📝 Şablon</button>
<button onclick="window.print()">🖨️ Yazdır</button>
<input type="file" id="fileInput" hidden accept=".xlsx" onchange="App.handleFile(event)">
</div>
<header class="header">
<div class="header-left">
<img src="logo.jpg" class="logo" onerror="this.style.display='none'" alt="">
<div>
<div class="style-small-light" data-editable>(Temmuz 2025 – Temmuz 2026)</div>
<div class="style-bold-big" data-editable>YILLIK FAALİYET RAPORU</div>
</div>
</div>
<div class="style-bold-big" data-editable>Kullanıcı Destek Koordinatörlüğü</div>
<div class="header-right" data-editable>TEKNİK İŞLETME MÜDÜRLÜĞÜ</div>
</header>
<div id="dashboard" class="main-grid"></div>
</div>
<script>
const App = {
embeddedData: [
[1, "1. YAPILAN İŞLER", "Kullanıcı Çağrılarına, Çözüm desteği sağlandı.", "3.750 Adet"],
[1, "1. YAPILAN İŞLER", "Bilgisayar Kurulum Tamamlandı", "850 Adet"],
[1, "1. YAPILAN İŞLER", "Tüm Kameraların yazılımları güncellendi.", "90%"],
[1, "1. YAPILAN İŞLER", "Akıllı Form ve Dokümantasyon Çalışmaları yapıldı", "70%"],
[1, "1. YAPILAN İŞLER", "Kurumdaki; VIP, Personel ve Toplantı odalarına Bakım ve Toplantı desteği sağlandı.", "80%"],
[1, "1. YAPILAN İŞLER", "Yazıcı ve Projeksiyonlar için Arıza tespit çalışmaları yapıldı. (Baskı adeti ve Ampul saati)", "100%"],
[2, "2. KURUMA SAĞLANAN KATKILAR", "Kamera sisteminde oluşan yoğun kesintiler, minimum seviyeye indirildi.", "nan"],
[2, "2. KURUMA SAĞLANAN KATKILAR", "Kurumsal yapıdaki formlarla, çalışmalarda bir düzen sağlandı.", "nan"],
[2, "2. KURUMA SAĞLANAN KATKILAR", "Koordinatörlüğün iş ve kapsam sınırları belirlendi.", "nan"],
[2, "2. KURUMA SAĞLANAN KATKILAR", "Ortak Yazıcılarda sık arızaya geçme durumları, yazlım ve donanımsal bakımlarla azaltıldı.", "nan"],
[3, "3. ÇALIŞMALARIN İLERLEME DURUMU", "Koordinatörlük kapsamındaki işlerle ilgili dokümantasyon çalışmaları", "15%"],
[3, "3. ÇALIŞMALARIN İLERLEME DURUMU", "Kameraların Avigilon yazılımına geçme sonrası, yapılandırma işlemleri", "60%"],
[3, "3. ÇALIŞMALARIN İLERLEME DURUMU", 'Mevcut eski kameraların "150 adet" 1/3 yeni kameralarla değişimi', "1%"],
[4, "4. GELECEK DÖNEM HEDEFLERİ", "Kameralar için Bakım anlaşması", "nan"],
[4, "4. GELECEK DÖNEM HEDEFLERİ", "Karel Santral için, Bakım anlaşması", "nan"],
[4, "4. GELECEK DÖNEM HEDEFLERİ", "Tüm Yazıcılar için yer değişikliği çalışması", "nan"],
[4, "4. GELECEK DÖNEM HEDEFLERİ", "Tüm Projeksiyonlar için yer değişikliği çalışması", "nan"],
],
buildDashboard(data) {
const container = document.getElementById('dashboard');
const groups = { 1:[], 2:[], 3:[], 4:[] };
data.forEach(row => {
if(groups[row[0]]) {
let rawVal = row[3];
let val = String(rawVal);
if (val === 'nan' || val === 'null' || val === 'undefined' || val.trim() === '') val = '';
let percentVal = 0;
if (row[0] == 3 && val !== '') {
if (typeof rawVal === 'number') {
percentVal = rawVal <= 1 ? rawVal * 100 : rawVal;
val = Math.round(percentVal) + '%';
} else {
percentVal = parseFloat(val.replace(',', '.').replace('%', '').trim()) || 0;
}
}
groups[row[0]].push({title: row[2], val: val, percent: percentVal});
}
});
container.innerHTML = `
<div class="col">
<div class="card">
<div class="card-title">1. YAPILAN İŞLER</div>
${groups[1].map(i => `<div class="data-row"><span>${i.title}</span><span class="data-val">${i.val}</span></div>`).join('')}
</div>
<div class="card">
<div class="card-title">2. KURUMA SAĞLANAN KATKILAR</div>
${groups[2].map(i => `<div class="item-text">✓ ${i.title}</div>`).join('')}
</div>
</div>
<div class="col">
<div class="card">
<div class="card-title">3. ÇALIŞMALARIN İLERLEME DURUMU</div>
${groups[3].map(i => `
<div>
<div class="data-row"><span>${i.title}</span><span class="data-val">${i.val}</span></div>
<div class="progress-bar"><div class="progress-fill" style="width:${i.percent}%;"></div></div>
</div>
`).join('')}
</div>
<div class="card">
<div class="card-title">4. GELECEK DÖNEM HEDEFLERİ</div>
${groups[4].map(i => `<div class="item-text">🎯 ${i.title}</div>`).join('')}
</div>
</div>
`;
},
handleFile(e) { if (!e.target.files[0]) return; const reader = new FileReader(); reader.onload = (evt) => { const wb = XLSX.read(evt.target.result, { type: 'array' }); const json = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header: 1 }); this.buildDashboard(json.slice(1).filter(r => r[0])); }; reader.readAsArrayBuffer(e.target.files[0]); },
downloadSampleExcel() { const ws = XLSX.utils.aoa_to_sheet([["No", "Bölüm", "Başlık", "Değer"], ...this.embeddedData]); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, "Veri"); XLSX.writeFile(wb, "Faaliyet_Raporu.xlsx"); }
};
// Her başlık kendi üzerinde art arda beş tıklamadan sonra düzenlenir.
function initHeaderEditing() {
let lastTarget = null;
let lastClick = 0;
let clickCount = 0;
const maxInterval = 700;
const resetClicks = () => { lastTarget = null; lastClick = 0; clickCount = 0; };
document.querySelectorAll('.header [data-editable]').forEach(el => {
let originalText = el.textContent;
el.tabIndex = 0;
el.title = 'Düzenlemek için art arda 5 kez tıklayın. Enter: uygula, Esc: vazgeç.';
el.setAttribute('contenteditable', 'false');
el.addEventListener('keydown', event => {
if (!el.isContentEditable) return;
if (event.key === 'Enter' && !event.isComposing) { event.preventDefault(); el.blur(); }
if (event.key === 'Escape') { event.preventDefault(); el.textContent = originalText; el.blur(); }
});
el.addEventListener('blur', () => {
if (!el.isContentEditable) return;
const value = el.textContent.replace(/\s+/g, ' ').trim();
el.textContent = value || originalText;
el.setAttribute('contenteditable', 'false');
resetClicks();
});
// Yapıştırılan içerikten yalnızca düz metni al.
el.addEventListener('paste', event => {
if (!el.isContentEditable) return;
event.preventDefault();
const text = event.clipboardData.getData('text/plain').replace(/\s+/g, ' ');
const selection = window.getSelection();
if (!selection.rangeCount) return;
const range = selection.getRangeAt(0);
if (!el.contains(range.commonAncestorContainer)) return;
range.deleteContents();
const node = document.createTextNode(text);
range.insertNode(node);
range.setStartAfter(node);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
});
el.addEventListener('drop', event => event.preventDefault());
el.addEventListener('click', () => {
if (el.isContentEditable) return;
const now = performance.now();
clickCount = lastTarget === el && now - lastClick <= maxInterval ? clickCount + 1 : 1;
lastTarget = el;
lastClick = now;
if (clickCount !== 5) return;
resetClicks();
originalText = el.textContent;
el.setAttribute('contenteditable', 'true');
el.focus();
const range = document.createRange();
range.selectNodeContents(el);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
});
});
document.addEventListener('click', event => {
if (!event.target.closest('.header [data-editable]')) resetClicks();
});
}
// Açılışta boş gelmesi için [] gönderildi.
window.addEventListener('DOMContentLoaded', () => {
App.buildDashboard([]);
initHeaderEditing();
});
</script>
</body>
</html>

