Butonlu Envanter Dashboard

Bu çalışma, Tüm Excel çalışmalarını hızlıca filtreleme ve kolayca aranan bilgiye erişebilmek için yapılan, uzun soluklu bir çalışmadır. 

Raporlama ve Analiz. Bu iki kavram etkin rapor sunmayla varlığını sürdürmektedir. Bu aşamada ise en önemli kısım hızdır. Farklı listeler üzerinde analizler söz konusu olduğunda, elimde bir tool olsa oradan hızlıca filtreleyip hızlıca sonuca varabilsem düşüncesinin bir sonucu olarak ortaya çıkan bir üründür.

HTML kod olarak hazırlanan çalışma, sayfa sonunda yer almaktadır. Bir not defteri açıp kodu not alanına yapıştırın. Dosya ismi verip, uzantıyı ".html" olarak kaydetmeniz yeterli. sonrasında, çift tıklayarak çalıştırın.

Bu çalışma ile, "Dosya Seç" ile dosya okuma sağlandıktan sonra; Başlıklara basarak, başlık içinde yer alan farklı ifadelere göre dilimleyici butonlar oluşması, butonlara tıklama ile, filtre ve birden fazla butona basılması ile, filtre içinde filtrelemeye imkan sunmaktadır. Tüm başlıkların sütun içeriklerinde ise, yukarıdaki arama alanında yazarken filtrelemeye imkan sunmaktadır.  

Kodda yapılan çalışmalarla, nihai sonuçtaki kazanımlar; başlık alanında "Envanter" ibaresi alanındaki ifade, dosya ismi olacak şekilde düzenlendi. Ayrıca varsayılan olarak atanan tüm ibareler, HELDESK KOORDİNATÖRLÜĞÜ" ve "MYO MÜDÜRLÜĞÜ" alanlarına seri olarak 5 defa art arda basılmasıyla başlıkta değişime izin verildi.

Uygulama adımları 1: HELDESK KOORDİNATÖRLÜĞÜ" alanına seri olarak 5 defa art arda basılmasıyla başlıkta değişim.

Uygulama adımları 2: "MYO MÜDÜRLÜĞÜ" alanlarına seri olarak 5 defa art arda basılmasıyla başlıkta değişim gerçekleşmektedir.

Başlıklara tıklayarak oluşturulan butonlar, istenirse oluşan satırdaki "Kaldır" butonuna basılarak kaldırılabilir. "Filtreleri Sıfırla" butonuna basarak tüm filtreler toplu olarak kaldırılabilir.

İçeri aktarılan tablodan, başlıklardan hangisine tıklarsanız, dilimleme başlıkları ve içeriğine göre butonlar oluşacaktır.

Butonlara basarak filtreleme işlemlerini gerçekleştirebilirsiniz. Başlık alanında orta kısımda, tablodaki toplam satır sayısı, filtre durumunda bulunan sonuca göre oranı ve bulunan filtre adeti bu alana yansımaktadır. 

Arama penceresi ile tüm sütunlarda alfa numerik olarak arama gerçekleştirebilirsiniz. "Ara Temizle" ile aramayı temizleyebilirsiniz. ("Sayfa Yenile" basıldığında, ilk çalıştığı ana döner, başlıklar üzerinde yapılan değişiklik, varsayılan ifadelere döner) PDF olarak sunma düşüncesi, kurumsal bir form kazandırılması, Telefonlardan da tam net görünüm sağlanması içindir. Html sayfanın çalıştırıldığı konumda "logo.jpg" isminde kurumsal logonuzun olması yeterli. Böylelikle başlıkta logonuz yer alır.

Filtreleme sonrası, Filtrelenen Excel dosyasını indirebilir ve PDF olarak kaydedebilir yazdırabilirsiniz.

Yazdırabilir, PDF olarak kaydedebilirsiniz.

 
HTML Kod:

<!DOCTYPE html>

<html lang="tr">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width,initial-scale=1">

    <title>Dinamik_BUTONLU_Envanter_Dashboard</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>


    <style>

        /* --- TEMEL DEĞİŞKENLER VE SIFIRLAMALAR --- */

        :root {

            --bg: #f0f2f5;

            --shadow: 0 8px 24px rgba(0,0,0,.05);

            --radius: 10px;

        }

        * { 

            margin: 0; 

            padding: 0; 

            box-sizing: border-box; 

            font-family: "Segoe UI", system-ui, Arial, sans-serif; 

        }

        body { 

            background: var(--bg); 

            padding: 20px; 

        }


        /* --- ORTAK SABİT ALAN (BAŞLIK + ARAMA) --- */

        .sticky-container {

            position: sticky;

            top: 20px;

            z-index: 1000;

            margin-bottom: 20px;

        }

        .sticky-container::before {

            content: "";

            position: absolute;

            top: -20px; 

            left: -20px;

            right: -20px;

            bottom: -10px; 

            background: var(--bg);

            z-index: -1;

        }


        /* --- BAŞLIK ALANI STİLLERİ --- */

        .header { 

            display: flex; 

            justify-content: space-between; 

            align-items: center; 

            padding: 15px 20px; 

            background: #fff; 

            border: 1px solid #e1e4e8; 

            border-radius: var(--radius); 

            box-shadow: 0 4px 20px rgba(0,0,0,0.06); 

            margin-bottom: 15px; 

            gap: 15px;

        }

        .header .left { 

            display: flex; 

            align-items: center; 

            gap: 15px; 

            width: 30%;

        }

        .header .logo img { 

            height: 50px; 

        }

        .header .title1 { 

            font-size: 22px; 

            font-weight: 800; 

            color: #1a1e23; 

            letter-spacing: -0.5px;

            cursor: pointer;

            user-select: none;

        }

        .header .title2 { 

            font-size: 12px; 

            color: #6a737d; 

            font-weight: 600; 

            text-transform: uppercase;

            letter-spacing: 0.5px;

            cursor: pointer;

            user-select: none;

        }

        

        /* --- ESTETİK VE TEKNOLOJİK GRAFİK BÖLÜMÜ (HUD STİLİ) --- */

        .header .center-title { 

            flex-grow: 1; 

            display: flex;

            justify-content: center;

        }

        

        .gauges-wrapper {

            display: flex;

            gap: 30px;

            background: #fafbfc;

            border: 1px solid #eef0f3;

            padding: 12px 30px;

            border-radius: 50px;

            box-shadow: inset 0 2px 4px rgba(0,0,0,0.02), 0 4px 10px rgba(0,0,0,0.03);

        }


        .tech-gauge {

            display: flex;

            flex-direction: column;

            align-items: center;

            width: 100px;

            position: relative;

        }

        

        .tg-label {

            font-size: 11px;

            font-weight: 800;

            color: #586069;

            text-transform: uppercase;

            letter-spacing: 1px;

            margin-bottom: -2px;

            display: flex;

            align-items: center;

            gap: 5px;

            white-space: nowrap;

        }


        .tg-label.total::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: #00c6ff; box-shadow: 0 0 6px #00c6ff; }

        .tg-label.percent::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: #ff416c; box-shadow: 0 0 6px #ff416c; }

        .tg-label.filtered::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: #11998e; box-shadow: 0 0 6px #11998e; }


        .tech-gauge svg {

            width: 100%;

            overflow: visible;

            filter: drop-shadow(0px 4px 4px rgba(0,0,0,0.1));

        }

        

        .tg-value {

            font-size: 22px;

            font-weight: 900;

            fill: #24292e;

            font-family: "Courier New", Courier, monospace;

        }


        .header .right { 

            width: 30%;

            font-size: 13px; 

            font-weight: 800; 

            text-align: right; 

            color: #24292e; 

            letter-spacing: 0.5px;

            cursor: pointer;

            user-select: none;

        }


        /* --- KONTROL & DOSYA YÜKLEME PANELİ --- */

        .controls {

            display: flex;

            gap: 15px;

            align-items: center;

            background: #fff;

            padding: 15px;

            border-radius: var(--radius);

            box-shadow: var(--shadow);

            border-left: 5px solid #00c6ff;

            flex-wrap: wrap;

        }

        input[type="file"], button, select {

            padding: 8px 15px;

            border: 1px solid #d1d5da;

            border-radius: 6px;

            font-size: 13px;

            outline: none;

        }

        input[type="file"]:focus, select:focus { border-color: #00c6ff; }

        

        /* --- GENEL ARAMA (GLOBAL SEARCH) ALANI --- */

        .search-container {

            display: flex;

            flex-grow: 1;

            align-items: center;

            gap: 10px;

            margin: 0 15px;

        }

        

        #globalSearch {

            flex-grow: 1;

            margin: 0;

            padding: 10px 15px;

            border-radius: 6px;

            font-size: 14px;

            outline: none;

            transition: border-color 0.2s, box-shadow 0.2s;

            border: 1px solid #00c6ff;

            box-shadow: 0 0 0 3px rgba(0, 198, 255, 0.15);

        }

        #globalSearch::placeholder {

            color: #b0b3b8;

        }


        .btn-primary {

            background: linear-gradient(135deg, #007bff, #0056b3);

            color: white; border: none; cursor: pointer; font-weight: bold; transition: 0.3s;

            box-shadow: 0 4px 10px rgba(0, 123, 255, 0.3);

        }

        .btn-primary:hover { box-shadow: 0 6px 15px rgba(0, 123, 255, 0.5); transform: translateY(-1px); }

        

        .btn-danger {

            background: linear-gradient(135deg, #dc3545, #a71d2a);

            color: white; border: none; cursor: pointer; font-weight: bold; transition: 0.3s;

            box-shadow: 0 4px 10px rgba(220, 53, 69, 0.3);

        }

        .btn-danger:hover { box-shadow: 0 6px 15px rgba(220, 53, 69, 0.5); transform: translateY(-1px); }


        .btn-success {

            background: linear-gradient(135deg, #28a745, #218838);

            color: white; border: none; cursor: pointer; font-weight: bold; transition: 0.3s;

            box-shadow: 0 4px 10px rgba(40, 167, 69, 0.3);

        }

        .btn-success:hover { box-shadow: 0 6px 15px rgba(40, 167, 69, 0.5); transform: translateY(-1px); }


        .btn-secondary {

            background: linear-gradient(135deg, #e1e4e8, #d1d5da);

            color: #24292e; border: none; cursor: pointer; font-weight: bold; transition: 0.3s;

            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);

        }

        .btn-secondary:hover { box-shadow: 0 6px 15px rgba(0, 0, 0, 0.15); transform: translateY(-1px); }


        progress { width: 150px; height: 18px; display: none; border-radius: 10px; }

        progress::-webkit-progress-bar { background-color: #f0f2f5; border-radius: 10px; }

        progress::-webkit-progress-value { background: linear-gradient(90deg, #00c6ff, #0072ff); border-radius: 10px; }


        /* --- EXCEL SEKMELERİ (TABS) STİLLERİ --- */

        .sheet-tabs-container {

            width: 100%;

            display: flex;

            gap: 8px;

            margin-top: 10px;

            overflow-x: auto;

            padding-bottom: 5px;

        }

        .sheet-tab-btn {

            background: #f8f9fc;

            color: #007bff;

            border: 1px solid #cce5ff;

            padding: 6px 16px;

            border-radius: 20px;

            cursor: pointer;

            font-size: 13px;

            font-weight: 700;

            transition: all 0.3s ease;

            white-space: nowrap;

        }

        .sheet-tab-btn:hover {

            background: #e6f2ff;

            border-color: #007bff;

        }

        .sheet-tab-btn.active {

            background: linear-gradient(135deg, #007bff, #0056b3);

            color: #fff;

            border-color: transparent;

            box-shadow: 0 4px 10px rgba(0, 123, 255, 0.3);

        }


        /* --- VERİ DİLİMLEYİCİ VE BİLEŞEN STİLLERİ --- */

        .slicer-container {

            background: #f8f9fc; 

            padding: 20px;

            border-radius: var(--radius);

            box-shadow: var(--shadow);

            margin-bottom: 20px;

            border: 1px solid #e1e4e8;

        }

        .slicer-header-bar {

            display: flex;

            align-items: center;

            justify-content: flex-end;

            gap: 15px;

            flex-wrap: wrap;

        }

        

        .slicer-area-rows {

            display: flex;

            flex-direction: column;

            gap: 12px;

            margin-top: 15px;

        }


        .slicer-row {

            display: flex;

            align-items: flex-start;

            background: #ffffff;

            border: 1px solid #e1e4e8;

            border-radius: 10px;

            padding: 15px 18px;

            box-shadow: 0 2px 8px rgba(0,0,0,0.03);

            gap: 20px;

        }


        /* YENİ BAŞLIK VE İSTATİSTİK BÖLÜMÜ GÖRÜNÜMÜ */

        .slicer-row-header {

            min-width: 250px;

            flex-shrink: 0;

            display: flex;

            flex-direction: column;

            gap: 6px;

            border-right: 2px solid #f0f2f5;

            padding-right: 15px;

            margin-top: 2px;

        }


        .slicer-row-title {

            font-size: 16px;

            font-weight: 800;

            color: #1a1e23;

            letter-spacing: -0.2px;

        }


        .slicer-row-stats {

            font-size: 13px;

            color: #6a737d;

            display: flex;

            gap: 4px;

            align-items: center;

            flex-wrap: wrap;

        }

        

        .slicer-stat-text {

            color: #586069;

        }


        .slicer-stat-separator {

            color: #d1d5da;

            margin: 0 2px;

        }


        /* GÖRSELE UYGUN KALDIR BUTONU (Kırmızı Metin) */

        .slicer-remove-text-btn {

            background: none;

            border: none;

            color: #f43f5e; 

            font-size: 13px;

            font-weight: 700;

            cursor: pointer;

            padding: 0;

            margin: 0;

            transition: all 0.2s ease;

        }

        .slicer-remove-text-btn:hover {

            color: #e11d48;

            text-decoration: underline;

        }


        /* ÇOK SATIRLI (WRAP) BUTON ALANI */

        .slicer-row-buttons {

            display: flex;

            flex-wrap: wrap; 

            gap: 8px;

            align-items: center;

            flex-grow: 1;

        }


        /* DİLİMLEYİCİ MODERN SIYAH BUTONLARI */

        .glass-btn {

            padding: 7px 16px;

            border-radius: 20px;

            font-size: 12px;

            font-weight: 700;

            cursor: pointer;

            transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);

            user-select: none;

            display: inline-flex;

            align-items: center;

            gap: 6px;

            outline: none;

            white-space: nowrap;

            flex-shrink: 0;

        }


        /* Modern Siyah (İçinde veri olan aktif butonlar) */

        .glass-btn.active-glass {

            background: linear-gradient(135deg, #1f242d, #11141a);

            color: #ffffff;

            border: 1px solid #333a48;

            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.1);

        }

        .glass-btn.active-glass:hover {

            transform: translateY(-2px);

            box-shadow: 0 6px 14px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.2);

            border-color: #00c6ff;

        }


        /* Seçili Durum Vurgusu (Mavi Cam Vurgulu Modern Siyah) */

        .glass-btn.selected-glass {

            background: linear-gradient(135deg, #0f172a, #020617) !important;

            color: #38bdf8 !important;

            border: 1px solid #38bdf8 !important;

            box-shadow: 0 0 12px rgba(56, 189, 248, 0.4), inset 0 1px 0 rgba(56, 189, 248, 0.2) !important;

            transform: translateY(-1px);

        }


        /* Pasif (İçinde veri olmayan) Açık Mat Butonlar */

        .glass-btn.passive-glass {

            background: #f1f5f9;

            color: #94a3b8;

            border: 1px solid #e2e8f0;

            cursor: pointer;

            opacity: 0.6;

            box-shadow: none;

        }

        .glass-btn.passive-glass:hover {

            background: #e2e8f0;

            color: #64748b;

        }


        /* --- TABLO STİLLERİ --- */

        .table-container {

            width: 100%; 

            max-height: 65vh; 

            overflow: auto; 

            background: #fff; 

            border-radius: var(--radius); 

            box-shadow: var(--shadow);

            position: relative;

        }

        

        table { width: max-content; min-width: 100%; border-collapse: separate; border-spacing: 0; }

        th, td { border-bottom: 1px solid #e1e4e8; border-right: 1px solid #e1e4e8; padding: 12px; text-align: left; font-size: 13px; white-space: nowrap; }

        th:first-child, td:first-child { border-left: 1px solid #e1e4e8; }

        

        thead { 

            position: sticky; 

            top: 0; 

            z-index: 20; 

            background: #fff;

            box-shadow: 0 4px 6px rgba(0,0,0,0.06); 

        }


        thead th.main-header { 

            background: #1a1e23 !important; 

            color: white !important; 

            font-weight: bold; 

            border-top: 1px solid #1a1e23;

            cursor: pointer; 

            user-select: none; 

            transition: background 0.2s;

        }

        thead th.main-header:hover {

            background: #2c3238 !important; 

        }


        tbody tr:nth-child(even) { background-color: #f8f9fc; }

        tbody tr:hover { background-color: #e6f2ff; }


        /* --- ARAMA VURGUSU (HIGHLIGHT) STİLİ --- */

        .highlight-match {

            background-color: #d4edda;

            color: #155724;

            font-weight: bold;

            border-radius: 3px;

            padding: 1px 2px;

        }


        /* --- SAYFALAMA (PAGINATION) STİLLERİ --- */

        .pagination-container {

            display: flex;

            justify-content: space-between;

            align-items: center;

            padding: 15px;

            background: #fff;

            border-radius: var(--radius);

            box-shadow: var(--shadow);

            margin-top: 15px;

        }

        .pagination-controls button { margin: 0 5px; }


        /* --- YAZDIRMA (PRINT) VE PDF STİLLERİ --- */

        @media print {

            .no-print { display: none !important; }

            body { background: white; padding: 0; margin: 0; }

            

            .sticky-container { position: static !important; }

            .sticky-container::before { content: none !important; }


            .header { box-shadow: none; border-color: #000; margin-bottom: 20px; position: static; }

            .table-container { box-shadow: none; border-radius: 0; overflow: visible; max-height: none; }

            table { width: 100%; max-width: 100%; font-size: 10px; }

            th, td { white-space: nowrap !important; padding: 6px; }

            thead { position: static; box-shadow: none; }

            

            thead th.main-header { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }

            

            .highlight-match { 

                background-color: transparent !important; 

                color: inherit !important; 

                font-weight: normal !important; 

                padding: 0 !important; 

            }

            

            @page { size: landscape; margin: 10mm; @bottom-right { content: "Sayfa " counter(page) " / " counter(pages); font-size: 10px; } }

        }

    </style>

</head>

<body>


    <svg style="width:0; height:0; position:absolute;" aria-hidden="true" focusable="false">

        <defs>

            <linearGradient id="gradTotal" x1="0%" y1="0%" x2="100%" y2="0%">

                <stop offset="0%" stop-color="#00c6ff" />

                <stop offset="100%" stop-color="#0072ff" />

            </linearGradient>

            <linearGradient id="gradPercent" x1="0%" y1="0%" x2="100%" y2="0%">

                <stop offset="0%" stop-color="#ff416c" />

                <stop offset="100%" stop-color="#ff4b2b" />

            </linearGradient>

            <linearGradient id="gradFiltered" x1="0%" y1="0%" x2="100%" y2="0%">

                <stop offset="0%" stop-color="#11998e" />

                <stop offset="100%" stop-color="#38ef7d" />

            </linearGradient>

        </defs>

    </svg>


    <div class="sticky-container">

        <div class="header">

            <div class="left">

                <div class="logo"><img src="logo.jpg" alt="Logo" onerror="this.style.display='none'"></div>

                <div>

                    <div class="title1" id="mainHeaderTitle">Envanter</div>

                    <div class="title2" id="subHeaderTitle">Helpdesk Koordinatörlüğü</div>

                </div>

            </div>

            

            <div class="center-title">

                <div class="gauges-wrapper">

                    <div class="tech-gauge">

                        <div class="tg-label total">Adet</div>

                        <svg viewBox="0 0 100 55">

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#e1e4e8" stroke-width="14" stroke-linecap="round" />

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#ffffff" stroke-width="14" stroke-dasharray="2 4" />

                            <path id="pathTotal" d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="url(#gradTotal)" stroke-width="10" stroke-linecap="round" 

                                  style="stroke-dasharray: 125.6; stroke-dashoffset: 0; transition: stroke-dashoffset 1s ease-out;" />

                            <text x="50" y="47" text-anchor="middle" class="tg-value" id="gTotal">0</text>

                        </svg>

                    </div>

                    

                    <div class="tech-gauge">

                        <div class="tg-label percent">Oran</div>

                        <svg viewBox="0 0 100 55">

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#e1e4e8" stroke-width="14" stroke-linecap="round" />

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#ffffff" stroke-width="14" stroke-dasharray="2 4" />

                            <path id="pathPercent" d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="url(#gradPercent)" stroke-width="10" stroke-linecap="round" 

                                  style="stroke-dasharray: 125.6; stroke-dashoffset: 125.6; transition: stroke-dashoffset 1s ease-out;" />

                            <text x="50" y="47" text-anchor="middle" class="tg-value" id="gPercent">%0</text>

                        </svg>

                    </div>

                    

                    <div class="tech-gauge">

                        <div class="tg-label filtered">Filtre Adet</div> 

                        <svg viewBox="0 0 100 55">

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#e1e4e8" stroke-width="14" stroke-linecap="round" />

                            <path d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="#ffffff" stroke-width="14" stroke-dasharray="2 4" />

                            <path id="pathFiltered" d="M 10 50 A 40 40 0 0 1 90 50" fill="none" stroke="url(#gradFiltered)" stroke-width="10" stroke-linecap="round" 

                                  style="stroke-dasharray: 125.6; stroke-dashoffset: 125.6; transition: stroke-dashoffset 1s ease-out;" />

                            <text x="50" y="47" text-anchor="middle" class="tg-value" id="gFiltered">0</text>

                        </svg>

                    </div>

                </div>

            </div>

            

            <div class="right" id="rightHeaderTitle">MYO MÜDÜRLÜĞÜ</div>

        </div>


        <div class="controls no-print">

            <input type="file" id="excelFile" accept=".xls,.xlsx">

            <progress id="progressBar" value="0" max="100"></progress>

            

            <div class="search-container no-print">

                <button class="btn-secondary" type="button" onclick="resetPage();">Sayfa Yenile</button>

                <button class="btn-danger" type="button" onclick="document.getElementById('globalSearch').value=''; applyFiltersAndSlicers();">Ara Temizle</button>

                <input type="text" id="globalSearch" placeholder="Tüm başlıklarda, tek yerde Ara..." oninput="applyFiltersAndSlicers()">

            </div>

            

            <div style="margin-left: auto; display: flex; gap: 10px;">

                <button class="btn-success" type="button" onclick="exportToExcel()">📥 Excel İndir</button>

                <button class="btn-primary" type="button" onclick="window.print()">🖨️ PDF / Yazdır</button>

            </div>

            

            <div id="sheetTabsContainer" class="sheet-tabs-container" style="display: none;"></div>

        </div>

    </div> 


    <div class="slicer-container no-print">

        <div class="slicer-header-bar">

            <div style="display: flex; flex-direction: column; align-items: flex-end;">

                <button class="btn-danger" id="btnClearSlicers">✖ Filtreleri Sıfırla</button>

                <span style="font-size: 10px; color: #6c757d; margin-top: 5px; font-weight: 600; letter-spacing: 0.5px;">AI Web Designer: Muhammed Yusuf OLGUN</span>

            </div>

        </div>

        <div id="slicerArea" class="slicer-area-rows"></div>

    </div>


    <div class="table-container" id="tableContainer">

        <table id="dataTable">

            <thead id="tableHead"></thead>

            <tbody id="tableBody"></tbody>

        </table>

    </div>


    <div class="pagination-container no-print" id="paginationContainer" style="display:none;">

        <div id="pageInfo" style="font-size: 13px; font-weight: bold; color: #586069;"></div>

        <div class="pagination-controls">

            <button class="btn-primary" onclick="changePage(-1)">Önceki</button>

            <span id="pageIndicator" style="margin: 0 10px; font-weight: bold; color: #24292e;"></span>

            <button class="btn-primary" onclick="changePage(1)">Sonraki</button>

        </div>

        <div>

            <select id="rowsPerPage" onchange="changeRowsPerPage()">

                <option value="50">Sayfada 50 Kayıt</option>

                <option value="100" selected>Sayfada 100 Kayıt</option>

                <option value="500">Sayfada 500 Kayıt</option>

                <option value="1000">Sayfada 1000 Kayıt</option>

            </select>

        </div>

    </div>


    <script>

        let excelData = [];      

        let actualHeaders = {};  

        let headerKeys = [];     


        let currentFilteredData = [];

        let currentPage = 1;

        let rowsPerPage = 100;


        const PATH_LENGTH = 125.6;

        

        let currentWorkbook = null;

        let currentSheetName = "";


        // DİLİMLEYİCİ DURUM YÖNETİMİ

        let activeSlicerCols = []; 

        let slicerSelections = {}; 


        // --- BAŞLIK VE METİN YÖNETİMİ (5 TIKLAMA İLE DÜZENLEME) ---

        function setupFiveClickEdit(elementId, promptMessage) {

            const el = document.getElementById(elementId);

            if (!el) return;

            let clicks = 0;

            let timer = null;

            el.addEventListener('click', function(e) {

                clicks++;

                clearTimeout(timer);

                if (clicks >= 5) {

                    clicks = 0;

                    const currentVal = el.innerText.trim();

                    const newVal = prompt(promptMessage || "Yeni metni girin:", currentVal);

                    if (newVal !== null && newVal.trim() !== "") {

                        el.innerText = newVal.trim();

                    }

                } else {

                    timer = setTimeout(() => {

                        clicks = 0;

                    }, 1200);

                }

            });

        }


        setupFiveClickEdit('mainHeaderTitle', 'Başlık metnini değiştirin:');

        setupFiveClickEdit('subHeaderTitle', 'Alt başlık metnini değiştirin:');

        setupFiveClickEdit('rightHeaderTitle', 'Sağ başlık metnini değiştirin:');


        // Sayfa Yenile fonksiyonu (Başlığı Envanter yapar ve sayfayı temizler/yeniler)

        window.resetPage = function() {

            const titleEl = document.getElementById('mainHeaderTitle');

            if (titleEl) titleEl.innerText = "Envanter";

            window.location.reload();

        };


        document.getElementById('excelFile').addEventListener('change', function(e) {

            const file = e.target.files[0];

            if (!file) return;


            // Seçilen dosyanın ismini al ve uzantısını (.xlsx, .xls vb.) kaldır

            const fileNameWithoutExt = file.name.substring(0, file.name.lastIndexOf('.')) || file.name;

            const titleEl = document.getElementById('mainHeaderTitle');

            if (titleEl) {

                titleEl.innerText = fileNameWithoutExt;

            }


            const pb = document.getElementById('progressBar');

            pb.style.display = 'block';

            pb.value = 10;


            const reader = new FileReader();

            reader.onload = function(event) {

                pb.value = 40;

                try {

                    const data = new Uint8Array(event.target.result);

                    currentWorkbook = XLSX.read(data, {type: 'array'});

                    pb.value = 60;


                    const sheetNames = currentWorkbook.SheetNames;

                    renderSheetTabs(sheetNames);

                    

                    loadSheetData(sheetNames[0]);


                    pb.value = 100;

                    setTimeout(() => { pb.style.display = 'none'; }, 500);


                } catch (error) {

                    alert("Dosya okunurken bir hata oluştu: " + error.message);

                    pb.style.display = 'none';

                }

            };

            reader.readAsArrayBuffer(file);

        });


        function renderSheetTabs(sheetNames) {

            const container = document.getElementById('sheetTabsContainer');

            

            if (sheetNames.length <= 1) {

                container.style.display = 'none';

                container.innerHTML = '';

                return;

            }

            

            container.style.display = 'flex';

            container.innerHTML = '';

            

            sheetNames.forEach(name => {

                const btn = document.createElement('button');

                btn.className = 'sheet-tab-btn';

                btn.innerText = name;

                btn.onclick = () => {

                    const pb = document.getElementById('progressBar');

                    pb.style.display = 'block';

                    pb.value = 50;

                    

                    setTimeout(() => {

                        loadSheetData(name);

                        pb.style.display = 'none';

                    }, 50);

                };

                container.appendChild(btn);

            });

        }


        function loadSheetData(sheetName) {

            currentSheetName = sheetName;

            

            const container = document.getElementById('sheetTabsContainer');

            if(container.style.display !== 'none') {

                const btns = container.querySelectorAll('.sheet-tab-btn');

                btns.forEach(btn => {

                    if (btn.innerText === sheetName) btn.classList.add('active');

                    else btn.classList.remove('active');

                });

            }


            const worksheet = currentWorkbook.Sheets[sheetName];

            const rawData = XLSX.utils.sheet_to_json(worksheet, {header: "A", defval: ""});


            if (rawData.length > 0) {

                actualHeaders = rawData[0];

                headerKeys = Object.keys(actualHeaders).filter(key => String(actualHeaders[key]).trim() !== "");

                

                excelData = rawData.slice(1).filter(row => {

                    return headerKeys.some(key => String(row[key]).trim() !== "");

                });

                

                currentPage = 1;

                activeSlicerCols = [];

                slicerSelections = {};

                document.getElementById('slicerArea').innerHTML = '';


                renderHeaders();

                

                currentFilteredData = excelData; 

                renderPagination();

                updateGauges(excelData.length, excelData.length);

                

                document.getElementById('globalSearch').value = ''; 

            } else {

                alert(`"${sheetName}" sekmesinde veri bulunamadı!`);

                excelData = [];

                currentFilteredData = [];

                renderHeaders();

                renderPagination();

                updateGauges(0, 0);

            }

        }


        function renderHeaders() {

            const thead = document.getElementById('tableHead');

            thead.innerHTML = '';


            let headerRow = document.createElement('tr');


            headerKeys.forEach(k => {

                let th = document.createElement('th');

                th.className = 'main-header';

                th.title = 'Filtre / Dilimleyici eklemek için tıklayın';

                th.textContent = actualHeaders[k];

                

                // Başlığa tıklandığında dilimleyici ekleme işlemi

                th.addEventListener('click', () => {

                    if (!activeSlicerCols.includes(k)) {

                        activeSlicerCols.push(k);

                        slicerSelections[k] = new Set();

                        renderSlicerStructure();

                        applyFiltersAndSlicers();

                    }

                });


                headerRow.appendChild(th);

            });


            thead.appendChild(headerRow);

        }


        function escapeHTML(str) {

            return String(str !== undefined ? str : '').replace(/[&<>'"]/g, 

                tag => ({

                    '&': '&amp;', '<': '&lt;', '>': '&gt;', "'": '&#39;', '"': '&quot;'

                }[tag])

            );

        }


        function getHighlightedHTML(text, searchVal) {

            const str = String(text !== undefined ? text : '');

            if (!searchVal) return escapeHTML(str);

            

            const lowerText = str.toLocaleLowerCase('tr-TR');

            const lowerSearch = searchVal.toLocaleLowerCase('tr-TR');

            

            let startIndex = 0;

            let result = '';

            let index;

            

            while ((index = lowerText.indexOf(lowerSearch, startIndex)) > -1) {

                result += escapeHTML(str.substring(startIndex, index));

                result += `<span class="highlight-match">${escapeHTML(str.substring(index, index + searchVal.length))}</span>`;

                startIndex = index + searchVal.length;

            }

            result += escapeHTML(str.substring(startIndex));

            return result;

        }


        function renderPagination() {

            const tbody = document.getElementById('tableBody');

            tbody.innerHTML = '';


            const totalRows = currentFilteredData.length;

            if (totalRows === 0) {

                document.getElementById('paginationContainer').style.display = 'none';

                return;

            }


            document.getElementById('paginationContainer').style.display = 'flex';

            

            const totalPages = Math.ceil(totalRows / rowsPerPage);

            if (currentPage > totalPages) currentPage = totalPages;

            if (currentPage < 1) currentPage = 1;


            const startIndex = (currentPage - 1) * rowsPerPage;

            const endIndex = Math.min(startIndex + rowsPerPage, totalRows);


            const pageData = currentFilteredData.slice(startIndex, endIndex);

            const searchVal = document.getElementById('globalSearch').value.trim();


            pageData.forEach(row => {

                let tr = document.createElement('tr');

                headerKeys.forEach(k => {

                    let td = document.createElement('td');

                    td.innerHTML = getHighlightedHTML(row[k], searchVal);

                    tr.appendChild(td);

                });

                tbody.appendChild(tr);

            });


            document.getElementById('pageInfo').innerText = `Toplam ${totalRows} kayıttan ${startIndex + 1} - ${endIndex} arası gösteriliyor.`;

            document.getElementById('pageIndicator').innerText = `Sayfa ${currentPage} / ${totalPages}`;

        }


        window.changePage = function(dir) {

            currentPage += dir;

            renderPagination();

            document.getElementById('tableContainer').scrollTo({ top: 0, behavior: 'smooth' });

        }


        window.changeRowsPerPage = function() {

            rowsPerPage = parseInt(document.getElementById('rowsPerPage').value);

            currentPage = 1;

            renderPagination();

            document.getElementById('tableContainer').scrollTo({ top: 0, behavior: 'smooth' });

        }


        window.exportToExcel = function() {

            if (!currentFilteredData || currentFilteredData.length === 0) {

                alert("Dışa aktarılacak veri bulunamadı.");

                return;

            }

            

            const exportData = currentFilteredData.map(row => {

                let cleanRow = {};

                headerKeys.forEach(k => {

                    cleanRow[actualHeaders[k]] = row[k];

                });

                return cleanRow;

            });


            const ws = XLSX.utils.json_to_sheet(exportData);

            const wb = XLSX.utils.book_new();

            

            let exportSheetName = currentSheetName ? currentSheetName.substring(0, 31) : "Filtrelenmis_Veri"; 

            

            XLSX.utils.book_append_sheet(wb, ws, exportSheetName);

            XLSX.writeFile(wb, `Envanter_Raporu_${exportSheetName}.xlsx`);

        }


        /* --- VERİ DİLİMLEYİCİ (SLICER) VE FİLTRELEME MANTIĞI --- */


        function renderSlicerStructure() {

            const container = document.getElementById('slicerArea');

            container.innerHTML = '';


            activeSlicerCols.forEach(colKey => {

                const headerName = actualHeaders[colKey] || colKey;

                

                let uniqueVals = new Set();

                excelData.forEach(row => {

                    let val = row[colKey] !== undefined ? String(row[colKey]).trim() : "";

                    uniqueVals.add(val);

                });

                let sortedVals = Array.from(uniqueVals).sort((a, b) => a.localeCompare(b, 'tr-TR', {numeric: true}));


                const rowDiv = document.createElement('div');

                rowDiv.className = 'slicer-row';

                rowDiv.id = 'slicer_row_' + colKey;


                const headerDiv = document.createElement('div');

                headerDiv.className = 'slicer-row-header';

                

                headerDiv.innerHTML = `

                    <div class="slicer-row-title">${escapeHTML(headerName)}</div>

                    <div class="slicer-row-stats" id="stats_${colKey}">

                        <span class="slicer-stat-text">Toplam: ${sortedVals.length}</span> <span class="slicer-stat-separator">|</span>

                        <span class="slicer-stat-text">Seçilen: 0</span> <span class="slicer-stat-separator">|</span>

                        <span class="slicer-stat-text">Aktif: ${sortedVals.length}</span> <span class="slicer-stat-separator">|</span>

                        <button type="button" class="slicer-remove-text-btn" onclick="removeSlicerRow('${colKey}')" title="Bu dilimleyiciyi kaldır">Kaldır</button>

                    </div>

                `;


                const buttonsDiv = document.createElement('div');

                buttonsDiv.className = 'slicer-row-buttons';

                buttonsDiv.id = 'buttons_' + colKey;


                sortedVals.forEach(val => {

                    const btn = document.createElement('button');

                    btn.type = 'button';

                    btn.className = 'glass-btn active-glass';

                    btn.dataset.col = colKey;

                    btn.dataset.val = val;

                    btn.innerHTML = val === "" ? "<i>(Boş Kayıtlar)</i>" : escapeHTML(val);

                    

                    btn.addEventListener('click', function() {

                        toggleSlicerValue(colKey, val);

                    });


                    buttonsDiv.appendChild(btn);

                });


                rowDiv.appendChild(headerDiv);

                rowDiv.appendChild(buttonsDiv);

                container.appendChild(rowDiv);

            });

        }


        // TEK BİR SÜTUN DİLİMLEYİCİSİNİ KALDIRMA FONKSİYONU

        window.removeSlicerRow = function(colKey) {

            activeSlicerCols = activeSlicerCols.filter(c => c !== colKey);

            delete slicerSelections[colKey];

            const row = document.getElementById('slicer_row_' + colKey);

            if (row) row.remove();

            applyFiltersAndSlicers();

        };


        function toggleSlicerValue(colKey, val) {

            if (!slicerSelections[colKey]) {

                slicerSelections[colKey] = new Set();

            }

            const set = slicerSelections[colKey];

            if (set.has(val)) {

                set.delete(val);

            } else {

                set.add(val);

            }

            applyFiltersAndSlicers();

        }


        document.getElementById('btnClearSlicers').addEventListener('click', function() {

            activeSlicerCols = [];

            slicerSelections = {};

            document.getElementById('slicerArea').innerHTML = '';

            document.getElementById('globalSearch').value = '';

            applyFiltersAndSlicers();

        });


        window.applyFiltersAndSlicers = function() {

            const globalSearchVal = document.getElementById('globalSearch').value.trim().toLocaleLowerCase('tr-TR');


            let dataToProcess = excelData.filter(row => {

                for (let col of activeSlicerCols) {

                    const selectedSet = slicerSelections[col];

                    if (selectedSet && selectedSet.size > 0) {

                        let cellVal = row[col] !== undefined ? String(row[col]).trim() : "";

                        if (!selectedSet.has(cellVal)) return false;

                    }

                }

                if (globalSearchVal !== "") {

                    let passesSearch = headerKeys.some(key => {

                        let cellVal = String(row[key] || '').toLocaleLowerCase('tr-TR');

                        return cellVal.includes(globalSearchVal);

                    });

                    if (!passesSearch) return false;

                }

                return true;

            });


            currentPage = 1;

            currentFilteredData = dataToProcess;


            updateSlicerRowsUI(globalSearchVal);


            renderPagination();

            updateGauges(excelData.length, dataToProcess.length);

        };


        function updateSlicerRowsUI(globalSearchVal) {

            activeSlicerCols.forEach(colKey => {

                let subsetForCol = excelData.filter(row => {

                    for (let otherCol of activeSlicerCols) {

                        if (otherCol === colKey) continue; 

                        const set = slicerSelections[otherCol];

                        if (set && set.size > 0) {

                            let cellVal = row[otherCol] !== undefined ? String(row[otherCol]).trim() : "";

                            if (!set.has(cellVal)) return false;

                        }

                    }

                    if (globalSearchVal !== "") {

                        return headerKeys.some(key => {

                            let cellVal = String(row[key] || '').toLocaleLowerCase('tr-TR');

                            return cellVal.includes(globalSearchVal);

                        });

                    }

                    return true;

                });


                let validVals = new Set();

                subsetForCol.forEach(row => {

                    let val = row[colKey] !== undefined ? String(row[colKey]).trim() : "";

                    validVals.add(val);

                });


                const selectedSet = slicerSelections[colKey] || new Set();


                const statsElem = document.getElementById('stats_' + colKey);

                const buttonsElem = document.getElementById('buttons_' + colKey);


                if (buttonsElem) {

                    const buttons = buttonsElem.querySelectorAll('.glass-btn');

                    const totalCount = buttons.length;

                    const selectedCount = selectedSet.size;

                    const activeCount = validVals.size;


                    if (statsElem) {

                        statsElem.innerHTML = `

                            <span class="slicer-stat-text">Toplam: ${totalCount}</span> <span class="slicer-stat-separator">|</span>

                            <span class="slicer-stat-text">Seçilen: ${selectedCount}</span> <span class="slicer-stat-separator">|</span>

                            <span class="slicer-stat-text">Aktif: ${activeCount}</span> <span class="slicer-stat-separator">|</span>

                            <button type="button" class="slicer-remove-text-btn" onclick="removeSlicerRow('${colKey}')" title="Bu dilimleyiciyi kaldır">Kaldır</button>

                        `;

                    }


                    buttons.forEach(btn => {

                        const val = btn.dataset.val;

                        const isSelected = selectedSet.has(val);

                        const isActive = validVals.has(val);


                        btn.className = 'glass-btn';


                        if (isSelected) {

                            btn.classList.add('selected-glass');

                        } else if (isActive) {

                            btn.classList.add('active-glass'); 

                        } else {

                            btn.classList.add('passive-glass'); 

                        }

                    });

                }

            });

        }


        function updateGauges(total, filtered) {

            document.getElementById('gTotal').textContent = total;

            

            let percent = total === 0 ? 0 : Math.round((filtered / total) * 100);

            document.getElementById('gPercent').textContent = '%' + percent;

            document.getElementById('gFiltered').textContent = filtered;


            document.getElementById('pathTotal').style.strokeDashoffset = total > 0 ? 0 : PATH_LENGTH;

            

            let pOffset = PATH_LENGTH - (PATH_LENGTH * percent / 100);

            document.getElementById('pathPercent').style.strokeDashoffset = isNaN(pOffset) ? PATH_LENGTH : pOffset;

            

            let fOffset = PATH_LENGTH - (PATH_LENGTH * percent / 100);

            document.getElementById('pathFiltered').style.strokeDashoffset = isNaN(fOffset) ? PATH_LENGTH : fOffset;

        }

    </script>

</body>

</html>