Dokümantasyon

Helpdesk ekipleri, günlük operasyonların kesintisiz devam etmesi için onlarca farklı hizmeti aynı anda yönetir. Bu çalışmanın temel amacı, biriminiz tarafından sunulan tüm hizmetler için müdahale ve operasyon adımlarını standart bir yapıya kavuşturmak, aynı zamanda ITIL hizmet yönetimi yaklaşımının gerekliliklerini karşılayacak kapsamlı ve sürdürülebilir bir dokümantasyon altyapısı oluşturmaktır.

Dokümanlar ile aşağıda listelenen başlıklar, dokümantasyonu tamamlanan hizmetleri göstermektedir. Hazırlanan bu örnekler, bundan sonraki dokümantasyon çalışmalarında izlenecek standart yaklaşım ve ilerleme yöntemi açısından rehber niteliği taşımaktadır. Hazırlanan doküman ve hazırlanması gereken dokümanları tek bir tabloda izlenebilmektedir.


Oluşturulan Form aşağıdaki gibidir. 


Form içerisine veri aktarıldığında liste tek sayfada görüntülenmektedir. Dokümanı tamamlananlar dolgu rengi, açık yeşil dolgu olarak yansımakta. Mevcut doküman adeti, dokümante edilen sayısı ve toplama göre % oranı üst başlıkta yer almaktadır.


Datayı Excel üzerinde aşağıdaki gibi oluşturulur. Ana Hizmet, Alt Hizmet seçenekleri ile oluşturulan liste, kendi içinde kural "x" için kural oluşturularak yapılandırılmıştır. "c" sütununda, aynı satıra x konulması ile, hücre arkaplan rengi açık yeşil olarak tanımlanmıştır.



Excel, dışarı aktarıldığın oluşan data, Text formatında aşağıdaki gibi yansır.


Formu PDF olarak kaydedebilir, Yazdırabilirsiniz.


HTML Kodu:
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>BT Hizmet Kataloğu - Dokümantasyon - MYO (Tek Sayfa)</title>
    <script src="https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js"></script>
    <style>
        :root {
            --m: #0b4f8a;
            --bg: #eef4f8;
            --shadow: 0 3px 10px rgba(0,0,0,.12);
            --radius: 8px;
        }
        * { margin: 0; padding: 0; box-sizing: border-box; font-family: "Segoe UI", Arial, sans-serif; }
        body { background: var(--bg); padding: 8px; }
        
        .header { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; background: #fff; border: 2px solid #000; border-radius: var(--radius); box-shadow: var(--shadow); margin-bottom: 8px; gap: 15px; }
        .left { display: flex; align-items: center; gap: 12px; }
        .logo img { height: 46px; }
        .title1 { font-size: 22px; font-weight: 700; white-space: nowrap; }
        .title2 { font-size: 11px; white-space: nowrap; }
        
        /* Başlıktaki alan kalın siyah çerçeve ve kalın yazılar */
        .center-kpis { display: flex; gap: 10px; justify-content: center; align-items: center; flex-grow: 1; }
        .header-kpi { background: #fff; border: 2px solid #000; border-radius: var(--radius); padding: 2px 12px; text-align: center; min-width: 110px; box-shadow: 0 2px 5px rgba(0,0,0,0.05); }
        .header-kpi h2 { font-size: 16px; color: #000; font-weight: 700; margin-bottom: 0px; line-height: 1.2; }
        .header-kpi div { font-size: 11px; font-weight: 700; color: #000; }
        
        /* Dökümantasyon alanı açık yeşil vurgusu */
        .header-kpi.header-green { background: #d4edda; border-color: #0f5132; }
        .header-kpi.header-green h2 { color: #0f5132; }
        .header-kpi.header-green div { color: #0f5132; }
        
        .right { font-size: 14px; font-weight: 700; white-space: nowrap; text-align: right; }
        
        .panel { background: #fff; border: 1px solid var(--m); border-radius: 10px; padding: 10px; box-shadow: var(--shadow); }
        .msg { font-size: 13px; font-weight: 600; color: var(--m); margin-bottom: 8px; }
        .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 8px; }
        
        /* Alt hizmet kartları ve listesi */
        .card { background: #fff; border: 1px solid var(--m); border-radius: 8px; overflow: hidden; box-shadow: 0 2px 6px rgba(0,0,0,.08); transition: .2s; height: max-content; }
        .card:hover { transform: translateY(-2px); }
        .cardhead { background: var(--m); color: #fff; padding: 5px 10px; font-size: 13px; font-weight: 700; line-height: 1.2; }
        .card ul { list-style: none; padding: 0; margin: 0; }
        .card li { padding: 3px 10px; font-size: 12px; border-top: 1px solid #edf2f7; line-height: 1.2; }
        
        /* Eklenen Açık Yeşil Vurgu Sınıfı */
        .highlight-green { background-color: #d4edda !important; color: #0f5132 !important; font-weight: 600; }
        
        .empty { padding: 35px; text-align: center; color: #777; }
        .toolbar { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
        button { background: var(--m); color: #fff; border: none; padding: 8px 14px; border-radius: 6px; cursor: pointer; font-size: 12px; transition: .2s; }
        button:hover { opacity: .92; }
        input[type=file] { display: none; }
        
        /* Yazdırma Kuralları (Tek Sayfa ve Boşluksuz Tasarım) */
        @page { size: A4 landscape; margin: 4mm; }
        @media print {
            body { padding: 0; margin: 0; background: #fff; }
            .toolbar, .msg { display: none!important; }
            .panel { border: none !important; padding: 0 !important; box-shadow: none !important; }
            
            /* Üst başlıkların yazdırırken sığması için font ve boşluk küçültmeleri */
            .header { gap: 5px !important; padding: 4px 8px !important; }
            .logo img { height: 34px !important; }
            .title1 { font-size: 14px !important; white-space: nowrap !important; }
            .title2 { font-size: 9px !important; white-space: nowrap !important; }
            .right { font-size: 10px !important; white-space: nowrap !important; }
            .header-kpi { min-width: 70px !important; padding: 2px 4px !important; }
            .header-kpi h2 { font-size: 13px !important; }
            .header-kpi div { font-size: 9px !important; }

            /* Sayfada boş yer kalmaması ve kartların satırı tam kaplaması için auto-fit yapısı */
            .grid { 
                display: grid !important; 
                grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; 
                gap: 5px !important; 
            }
            
            /* Alt hizmet kutucuklarının içi tamamen sıkıştırıldı (boşluklar kaldırıldı) */
            .card { 
                break-inside: avoid; 
                page-break-inside: avoid; 
                height: max-content; 
                border: 1px solid var(--m) !important;
                box-shadow: none !important;
                margin: 0 !important;
                padding: 0 !important;
            }
            .cardhead { 
                page-break-after: avoid; 
                padding: 2px 5px !important; /* Başlık iç boşluğu minimize edildi */
                font-size: 11px !important;
                line-height: 1.1 !important;
            }
            .card ul { 
                padding: 0 !important; 
                margin: 0 !important; 
            }
            .card li { 
                padding: 1px 5px !important; /* Alt hizmet satır içi boşlukları sıfıra yakın daraltıldı */
                font-size: 10px !important;
                line-height: 1.1 !important;
                margin: 0 !important;
                border-top: 1px solid #ddd !important;
            }
            
            /* Genel ölçeklendirmeyi tek sayfaya zorlamak için hafifçe optimize et */
            * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
        }
    </style>
</head>
<body>

<div class="header">
    <div class="left">
        <div class="logo"><img src="logo.jpg" onerror="this.style.display='none'"></div>
        <div>
            <div class="title1">BT Hizmet Kataloğu - Dokümantasyon</div>
            <div class="title2">Helpdesk</div>
        </div>
    </div>
    
    <div class="center-kpis">
        <div class="header-kpi"><h2 id="ana">0</h2><div>Ana Hizmet</div></div>
        <div class="header-kpi"><h2 id="alt">0</h2><div>Alt Hizmet</div></div>
        <div class="header-kpi header-green"><h2 id="dokuman-adet">0</h2><div>Dökümantasyon</div></div>
        <div class="header-kpi header-green"><h2 id="dokuman-yuzde">%0</h2><div>Dökümantasyon</div></div>
    </div>
    
    <div class="right">MYO</div>
</div>

<div class="panel">
    <div id="msg" class="msg">Excel dosyası yükleyin.</div>
    <div id="grid" class="grid">
        <div class="empty">BT Hizmet Kataloğunu görüntülemek için Excel dosyası seçiniz.</div>
    </div>
    <div class="toolbar">
        <button id="btnImport">📥 Excel İçe Aktar</button>
        <button id="btnExport">📤 Excel Dışa Aktar</button>
        <button onclick="window.print()">🖨 Yazdır / PDF</button>
        <input id="excelFile" type="file" accept=".xlsx,.xls">
    </div>
</div>

<script>
const App = {
    rows: [],
    serviceMap: new Map(),

    init() {
        this.ui = {
            grid: document.getElementById("grid"),
            msg: document.getElementById("msg"),
            ana: document.getElementById("ana"),
            alt: document.getElementById("alt"),
            dokumanAdet: document.getElementById("dokuman-adet"),
            dokumanYuzde: document.getElementById("dokuman-yuzde"),
            file: document.getElementById("excelFile"),
            importBtn: document.getElementById("btnImport"),
            exportBtn: document.getElementById("btnExport")
        };

        if (this.ui.importBtn && this.ui.file) {
            this.ui.importBtn.addEventListener("click", () => {
                this.ui.file.click();
            });
        }
        
        if (this.ui.file) {
            this.ui.file.addEventListener("change", (e) => {
                this.readExcel(e);
            });
        }
        
        if (this.ui.exportBtn) {
            this.ui.exportBtn.addEventListener("click", () => {
                this.exportExcel();
            });
        }
    },

    reset() {
        this.rows = [];
        this.serviceMap.clear();
    },

    formatNumber(val) {
        if (val === undefined || val === null || val === "") return "0";
        const str = val.toString().trim();
        const clean = str.replace(/\./g, '').replace(/,/g, '');
        const num = Number(clean);
        if (!isNaN(num) && clean !== "") {
            return num.toLocaleString("tr-TR");
        }
        return str;
    },

    readExcel(e) {
        const file = e.target.files[0];
        if (!file) return;
        
        const reader = new FileReader();
        reader.onload = (x) => {
            try {
                const dataData = new Uint8Array(x.target.result);
                const wb = XLSX.read(dataData, { type: "array" });
                const sheetName = wb.SheetNames[0];
                const sheet = wb.Sheets[sheetName];
                const data = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: "" });
                
                this.parseExcel(data);
            } catch (err) {
                alert("Excel dosyası çözümlenirken hata oluştu: " + err.message);
                console.error(err);
            }
        };
        reader.readAsArrayBuffer(file);
    },

    parseExcel(data) {
        try {
            this.reset();
            if (!data || data.length === 0) {
                alert("Dosya boş veya geçersiz.");
                return;
            }

            for (let i = 2; i < data.length; i++) {
                const row = data[i];
                if (!row) continue;
                
                const ana = (row[0] !== undefined && row[0] !== null) ? row[0].toString().trim() : "";
                const alt = (row[1] !== undefined && row[1] !== null) ? row[1].toString().trim() : "";
                
                // Dolgu rengi kontrolü yerine 'x' ibaresi arıyoruz
                let isHighlighted = false;
                
                // Satırdaki herhangi bir hücrede 'x' veya 'X' ibaresi varsa (C, D vs. sütunlarında olabilir)
                for (let j = 0; j < row.length; j++) {
                    if (row[j] !== undefined && row[j] !== null) {
                        let val = row[j].toString().trim().toLowerCase();
                        if (val === "x") {
                            isHighlighted = true;
                            break;
                        }
                    }
                }
                
                // Veya alt hizmet (B sütunu) içinde "x", " (x)", " [x]" geçiyorsa
                let altLower = alt.toLowerCase();
                if (altLower === "x" || altLower.endsWith(" x") || altLower.includes("(x)") || altLower.includes("[x]")) {
                    isHighlighted = true;
                }
                
                if (ana !== "" && alt !== "") {
                    // Export işlemi bozulmasın diye orijinal ana ve alt metnini de yolluyoruz
                    this.rows.push({ ana, alt, isHighlighted });
                    if (!this.serviceMap.has(ana)) {
                        this.serviceMap.set(ana, []);
                    }
                    this.serviceMap.get(ana).push({ name: alt, isHighlighted });
                }
            }

            this.render();
        } catch (err) {
            alert("Veriler işlenirken hata oluştu: " + err.message);
            console.error(err);
        } finally {
            if (this.ui.file) this.ui.file.value = "";
        }
    },

    render() {
        try {
            if (this.serviceMap.size === 0) {
                this.ui.grid.innerHTML = '<div class="empty">Gösterilecek veri bulunamadı veya geçersiz format.</div>';
                this.ui.msg.textContent = "Veri bulunamadı.";
            } else {
                let cardsHtml = "";
                for (const [service, list] of this.serviceMap) {
                    let itemsHtml = "";
                    for (const item of list) {
                        // Eğer 'x' bulunduysa CSS sınıfı ile açık yeşil yapıyoruz
                        let cls = item.isHighlighted ? ' class="highlight-green"' : '';
                        itemsHtml += `<li${cls}>${item.name}</li>`;
                    }
                    cardsHtml += `
                        <div class="card">
                            <div class="cardhead">${service} (${this.formatNumber(list.length)})</div>
                            <ul>${itemsHtml}</ul>
                        </div>
                    `;
                }
                this.ui.grid.innerHTML = cardsHtml;
                this.ui.msg.textContent = `Excel başarıyla yüklendi. Toplam ${this.formatNumber(this.rows.length)} alt hizmet listelendi.`;
            }

            this.ui.ana.textContent = this.formatNumber(this.serviceMap.size);
            this.ui.alt.textContent = this.formatNumber(this.rows.length);
            
            // Dökümantasyon (Açık yeşil renkli satırların) hesaplaması
            let highlightedCount = this.rows.filter(r => r.isHighlighted).length;
            let perc = this.rows.length > 0 ? Math.round((highlightedCount / this.rows.length) * 100) : 0;
            
            this.ui.dokumanAdet.textContent = this.formatNumber(highlightedCount);
            this.ui.dokumanYuzde.textContent = "%" + perc;

        } catch (err) {
            console.error("Arayüz güncellenirken hata:", err);
        }
    },

    exportExcel() {
        try {
            if (!this.rows.length) {
                alert("Aktarılacak veya dışa aktarılacak yüklü bir veri bulunamadı. Önce Excel yükleyin.");
                return;
            }

            const firstRow = ["Ana Hizmet", "Alt Hizmet"];
            const secondRow = ["", ""];
            const aoa = [firstRow, secondRow];

            this.rows.forEach((r) => {
                const rowData = [r.ana, r.alt];
                aoa.push(rowData);
            });

            const ws = XLSX.utils.aoa_to_sheet(aoa);
            const wb = XLSX.utils.book_new();
            XLSX.utils.book_append_sheet(wb, ws, "BT Hizmet Kataloğu");
            XLSX.writeFile(wb, "BT_Hizmet_Dashboard_v49.xlsx");
        } catch (err) {
            alert("Excel dışa aktarılırken hata oluştu: " + err.message);
            console.error(err);
        }
    }
};

document.addEventListener("DOMContentLoaded", () => App.init());
</script>
</body>
</html>