
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Dinamik_BUTONLU_Resimli_Envanter_Dashboard</title>
<!-- SheetJS kütüphanesi - Excel okuma ve yazma için -->
<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;
}
/* --- BAŞLIK ALANI STİLLERİ (SABİT) --- */
.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: 25px;
gap: 15px;
position: sticky;
top: 20px;
z-index: 1000;
}
.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;
flex-direction: column;
align-items: center;
gap: 15px;
}
.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);
align-items: center;
}
.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;
}
.profile-photo-box {
width: 360px;
height: 200px;
border-radius: 8px;
background: #fff;
border: 1px solid #eef0f3;
box-shadow: 0 4px 10px rgba(0,0,0,0.03);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
cursor: pointer;
user-select: none;
}
.profile-photo-box img {
width: 100%;
height: 100%;
object-fit: contain;
display: none;
}
.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;
flex-direction: column;
gap: 15px;
background: #fff;
padding: 15px;
border-radius: var(--radius);
box-shadow: var(--shadow);
margin-bottom: 15px;
border-left: 5px solid #00c6ff;
position: sticky;
top: 365px;
z-index: 999;
}
.controls-top {
display: flex;
gap: 15px;
align-items: center;
width: 100%;
}
input[type="file"], input[type="text"], button, select {
padding: 8px 15px;
border: 1px solid #d1d5da;
border-radius: 6px;
font-size: 13px;
outline: none;
}
/* DAİMİ ODAKLI GENEL ARAMA ALANI */
#globalSearch {
flex-grow: 1;
margin: 0 10px 0 0;
padding: 10px 15px;
border-radius: 8px;
font-size: 14px;
border: 1px solid #00c6ff !important;
box-shadow: 0 0 0 3px rgba(0, 198, 255, 0.15) !important;
color: #24292e;
transition: 0.3s;
}
#globalSearch::placeholder {
color: #a0aab5;
}
.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, #e9ecef, #dee2e6);
color: #212529; border: 1px solid #ced4da; cursor: pointer; font-weight: bold; transition: 0.3s;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
}
.btn-secondary:hover { box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1); transform: translateY(-1px); background: linear-gradient(135deg, #dee2e6, #ced4da); }
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; }
/* SEKME (SHEET) LİSTESİ STİLLERİ */
#sheetListContainer {
display: none;
gap: 10px;
flex-wrap: wrap;
padding-top: 10px;
border-top: 1px dashed #e1e4e8;
width: 100%;
}
.sheet-btn {
padding: 6px 15px;
border: 1px solid #00c6ff;
background: #fff;
color: #007bff;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
transition: 0.3s;
}
.sheet-btn.active {
background: linear-gradient(135deg, #00c6ff, #0072ff);
color: white;
border-color: transparent;
box-shadow: 0 4px 10px rgba(0, 198, 255, 0.3);
}
.sheet-btn:hover:not(.active) {
background: #e6f2ff;
}
/* --- EXCEL DİLİMLEYİCİ (SLICER) ALANI YENİ TASARIM --- */
.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;
gap: 15px;
margin-bottom: 15px;
}
.slicer-area {
display: flex;
flex-direction: column;
gap: 15px;
}
.slicer-row {
display: flex;
align-items: center;
background: #fff;
padding: 15px;
border-radius: 8px;
border: 1px solid #e1e4e8;
box-shadow: 0 4px 10px rgba(0,0,0,0.04);
width: 100%;
}
.slicer-row-header {
flex: 0 0 250px;
display: flex;
flex-direction: column;
border-right: 1px solid #e1e4e8;
margin-right: 15px;
padding-right: 15px;
}
.slicer-row-title { font-size: 14px; font-weight: 800; color: #1a1e23; margin-bottom: 5px; }
.slicer-row-stats {
font-size: 11px;
color: #6a737d;
font-weight: 600;
}
.slicer-remove-link {
color: #ff6b6b;
cursor: pointer;
font-weight: 800;
text-decoration: none;
transition: 0.2s;
margin-left: 2px;
}
.slicer-remove-link:hover {
color: #e63946;
text-decoration: underline;
}
.slicer-row-buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
flex: 1;
}
.slicer-btn {
padding: 6px 14px;
border-radius: 6px;
font-size: 12px;
font-weight: 600;
border: 1px solid rgba(255,255,255,0.2);
cursor: pointer;
transition: all 0.2s ease;
/* İÇİNDE VERİ OLAN - CANLI CAM SİYAH */
background: rgba(0, 0, 0, 0.85);
color: #fff;
backdrop-filter: blur(5px);
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
}
/* SEÇİLİ OLAN (TIKLANAN) */
.slicer-btn.selected {
background: #000;
border: 1px solid #00c6ff;
box-shadow: 0 0 10px rgba(0, 198, 255, 0.6);
transform: scale(1.05);
}
.slicer-btn:hover:not(.disabled-slicer) {
transform: translateY(-2px);
background: #000;
}
/* PASİF OLAN - AÇIK GRİ */
.slicer-btn.disabled-slicer {
background: #e9ecef !important;
color: #adb5bd !important;
border: 1px solid #dee2e6 !important;
box-shadow: none !important;
cursor: not-allowed;
backdrop-filter: none;
transform: none !important;
}
/* --- 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; }
/* Eşleşen Metin Vurgusu (Açık Yeşil) */
.search-highlight {
background-color: #a5d6a7 !important;
color: #000 !important;
border-radius: 3px;
padding: 1px 2px;
font-weight: bold;
}
/* --- 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; }
.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; }
.search-highlight {
background-color: transparent !important;
color: inherit !important;
padding: 0 !important;
font-weight: normal !important;
}
@page { size: landscape; margin: 10mm; @bottom-right { content: "Sayfa " counter(page) " / " counter(pages); font-size: 10px; } }
}
</style>
</head>
<body>
<!-- SVG GRADYAN TANIMLAMALARI -->
<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>
<!-- BAŞLIK ALANI BAŞLANGICI -->
<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="mainTitle" title="Değiştirmek için 5 kez peş peşe tıklayın">Envanter</div>
<div class="title2" id="subTitle" title="Değiştirmek için 5 kez peş peşe tıklayın">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>
<!-- RESİM ALANI (5 kez tıklayarak sütun değiştirilebilir) -->
<div class="profile-photo-box" id="profileBox" title="Sütun değiştirmek için 5 kez peş peşe tıklayın">
<img id="profileImage" src="" alt="Kayıt Fotoğrafı" onerror="this.style.display='none'" onload="this.style.display='block'">
</div>
</div>
<div class="right" id="rightTitle" title="Değiştirmek için 5 kez peş peşe tıklayın">MYO MÜDÜRLÜĞÜ</div>
</div>
<!-- DOSYA YÜKLEME, ARAMA VE YAZDIRMA -->
<div class="controls no-print">
<div class="controls-top">
<input type="file" id="excelFile" accept=".xls,.xlsx">
<progress id="progressBar" value="0" max="100"></progress>
<button class="btn-secondary" type="button" onclick="window.location.reload()" title="Sayfayı Yenile">Sayfa Yenile</button>
<!-- ARAMA TEMİZLE BUTONU -->
<button class="btn-danger" type="button" onclick="clearGlobalSearch()" title="Aramayı Temizle">Ara Temizle</button>
<!-- GENEL ARAMA ALANI -->
<input type="text" id="globalSearch" placeholder="Tüm başlıklarda, tek yerde Ara..." oninput="applyFiltersAndSlicers()">
<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>
<!-- ÇOKLU SEKME (SHEET) LİSTESİ -->
<div id="sheetListContainer"></div>
</div>
<!-- EXCEL DİLİMLEYİCİ (SLICER) ALANI -->
<div class="slicer-container no-print">
<div class="slicer-header-bar">
<div style="margin-left: auto; display: flex; flex-direction: column; align-items: flex-end;">
<button class="btn-danger" id="btnClearSlicers">✖ Tüm Filtreleri Temizle</button>
<span style="font-size: 10px; color: #ced4da; 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"></div>
</div>
<!-- TABLO ALANI -->
<div class="table-container" id="tableContainer">
<table id="dataTable">
<thead id="tableHead"></thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<!-- SAYFALAMA KONTROLLERİ -->
<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 currentWorkbook = null;
let excelData = [];
let actualHeaders = {};
let headerKeys = [];
let currentFilteredData = [];
let currentPage = 1;
let rowsPerPage = 100;
// --- DİNAMİK RESİM SÜTUNU TANIMLAMASI ---
let imageColumnKey = "D"; // Varsayılan: "D"
// LocalStorage'dan kayıtlı verileri yükleme (Hata Korumalı)
try {
let savedCol = localStorage.getItem('profileImageCol');
if (savedCol && savedCol.trim() !== "") {
imageColumnKey = savedCol.trim().toUpperCase();
}
let savedRight = localStorage.getItem('headerRightText');
if (savedRight && savedRight.trim() !== "") {
document.getElementById('rightTitle').innerText = savedRight;
}
let savedSub = localStorage.getItem('headerSubTitle');
if (savedSub && savedSub.trim() !== "") {
document.getElementById('subTitle').innerText = savedSub;
}
} catch (e) {
console.warn("LocalStorage erişim uyarısı:", e);
}
// --- 5 TIKLAMA İLE METİN GÜNCELLEME SİSTEMİ ---
function attachFiveClickEditor(elementId, storageKey, promptMessage) {
let clickCount = 0;
let clickTimer = null;
const el = document.getElementById(elementId);
if (!el) return;
el.addEventListener('click', function() {
clickCount++;
clearTimeout(clickTimer);
clickTimer = setTimeout(() => { clickCount = 0; }, 2000);
if (clickCount >= 5) {
clickCount = 0;
clearTimeout(clickTimer);
let currentVal = el.innerText;
let newVal = prompt(promptMessage, currentVal);
if (newVal !== null && newVal.trim() !== "") {
el.innerText = newVal.trim();
if (storageKey) {
try {
localStorage.setItem(storageKey, newVal.trim());
} catch (e) {
console.warn("LocalStorage yazılamadı:", e);
}
}
}
}
});
}
// Başlık (title1), Alt Başlık (title2) ve Sağ Başlık (right) için 5 tık dinleyicileri
// Not: mainTitle (title1) sayfa ilk açılışta ve yenilemede daima "Envanter" olması istendiği için localStorage'a yazılmaz.
attachFiveClickEditor('mainTitle', null, "Yeni Başlık Giriniz:");
attachFiveClickEditor('rightTitle', 'headerRightText', "Sağ Başlık Metnini Giriniz (Örn: MYO MÜDÜRLÜĞÜ):");
attachFiveClickEditor('subTitle', 'headerSubTitle', "Alt Başlık Metnini Giriniz (Örn: Helpdesk Koordinatörlüğü):");
// Resim Sütunu İçin 5 Tık Dinleyicisi
let photoClickCount = 0;
let photoClickTimer = null;
document.getElementById('profileBox').addEventListener('click', function() {
photoClickCount++;
clearTimeout(photoClickTimer);
photoClickTimer = setTimeout(() => { photoClickCount = 0; }, 2000);
if (photoClickCount >= 5) {
photoClickCount = 0;
clearTimeout(photoClickTimer);
let colInput = prompt("Resim eşleşmesi yapılacak sütun harfini giriniz (Örn: D, E, F):", imageColumnKey);
if (colInput !== null && colInput.trim() !== "") {
imageColumnKey = colInput.trim().toUpperCase();
try {
localStorage.setItem('profileImageCol', imageColumnKey);
alert(`Resim eşleşme sütunu başarıyla "${imageColumnKey}" olarak güncellendi ve tarayıcıya kaydedildi.`);
} catch (err) {
console.error("Kayıt hatası:", err);
alert(`Sütun "${imageColumnKey}" olarak güncellendi.`);
}
updateProfileImage(currentFilteredData);
}
}
});
const PATH_LENGTH = 125.6;
// Dosya Seçildiğinde Başlığı Dosya Adı Yapma
document.getElementById('excelFile').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
// Dosya uzantısını temizle (.xlsx, .xls vb.) ve başlığa yaz
let fileNameWithoutExt = file.name.replace(/\.[^/.]+$/, "");
document.getElementById('mainTitle').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;
const sheetListContainer = document.getElementById('sheetListContainer');
if (sheetNames.length > 1) {
sheetListContainer.style.display = 'flex';
sheetListContainer.innerHTML = '';
sheetNames.forEach(sheet => {
const btn = document.createElement('button');
btn.className = 'sheet-btn';
btn.innerText = sheet;
btn.onclick = () => loadSheet(sheet);
sheetListContainer.appendChild(btn);
});
} else {
sheetListContainer.style.display = 'none';
sheetListContainer.innerHTML = '';
}
pb.value = 80;
loadSheet(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 loadSheet(sheetName) {
if (!currentWorkbook) return;
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() !== "");
});
} else {
alert(`"${sheetName}" sekmesinde veri bulunamadı!`);
excelData = [];
actualHeaders = {};
headerKeys = [];
}
currentPage = 1;
document.getElementById('globalSearch').value = '';
document.getElementById('slicerArea').innerHTML = '';
document.querySelectorAll('.sheet-btn').forEach(btn => {
if (btn.innerText === sheetName) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
renderHeaders();
currentFilteredData = excelData;
renderPagination();
updateGauges(excelData.length, excelData.length);
updateProfileImage(currentFilteredData);
}
function updateProfileImage(filteredData) {
const imgEl = document.getElementById('profileImage');
if (filteredData && filteredData.length > 0) {
let firstVal = filteredData[0][imageColumnKey];
if (firstVal !== undefined && String(firstVal).trim() !== "") {
let cleanVal = String(firstVal).trim();
imgEl.style.display = 'none';
imgEl.src = `Resim/${cleanVal}.jpg`;
} else {
imgEl.src = "";
imgEl.style.display = 'none';
}
} else {
imgEl.src = "";
imgEl.style.display = 'none';
}
}
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 = 'Filtrelemek (Veri Dilimleyici Oluşturmak) için tıklayın';
th.innerHTML = `<span>${actualHeaders[k]}</span>`;
th.addEventListener('click', () => {
createSlicer(k);
});
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
}
function toSafeLower(str) {
return String(str)
.replace(/İ/g, 'i').replace(/I/g, 'ı')
.replace(/Ğ/g, 'ğ').replace(/Ü/g, 'ü')
.replace(/Ş/g, 'ş').replace(/Ö/g, 'ö')
.replace(/Ç/g, 'ç')
.toLowerCase();
}
function highlightMatch(text, searchVal) {
if (!searchVal) return text;
let lowerText = toSafeLower(text);
let lowerSearch = toSafeLower(searchVal);
let startIndex = 0;
let result = '';
let matchIndex = lowerText.indexOf(lowerSearch, startIndex);
if (matchIndex === -1) return text;
while (matchIndex !== -1) {
result += text.substring(startIndex, matchIndex);
result += `<span class="search-highlight">${text.substring(matchIndex, matchIndex + searchVal.length)}</span>`;
startIndex = matchIndex + searchVal.length;
matchIndex = lowerText.indexOf(lowerSearch, startIndex);
}
result += text.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 globalSearchVal = document.getElementById('globalSearch').value;
pageData.forEach(row => {
let tr = document.createElement('tr');
headerKeys.forEach(k => {
let td = document.createElement('td');
let cellText = row[k] !== undefined && row[k] !== null ? String(row[k]) : '';
td.innerHTML = highlightMatch(cellText, globalSearchVal);
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();
XLSX.utils.book_append_sheet(wb, ws, "Filtrelenmis_Veri");
XLSX.writeFile(wb, "Envanter_Raporu.xlsx");
}
// --- DİNAMİK OLARAK TIKLANAN BAŞLIK İÇİN DİLİMLEYİCİ (SLICER) OLUŞTURMA ---
function createSlicer(colKey) {
if (!headerKeys.includes(colKey)) return;
if (document.getElementById('slicer_' + colKey)) return;
const headerName = actualHeaders[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 slicerArea = document.getElementById('slicerArea');
const rowBox = document.createElement('div');
rowBox.className = 'slicer-row slicer-box';
rowBox.id = 'slicer_' + colKey;
rowBox.dataset.col = colKey;
let buttonsHtml = sortedVals.map(v => `
<button class="slicer-btn" data-val="${v.replace(/"/g, '"')}">${v === "" ? "<i>(Boş Kayıtlar)</i>" : v}</button>
`).join('');
rowBox.innerHTML = `
<div class="slicer-row-header">
<div class="slicer-row-title">${headerName}</div>
<div class="slicer-row-stats" id="stats_${colKey}">Toplam: ${sortedVals.length} | Seçilen: 0 | Aktif: ${sortedVals.length} | <a class="slicer-remove-link" onclick="removeSlicer('${colKey}')" title="Bu filtreyi kaldır">Kaldır</a></div>
</div>
<div class="slicer-row-buttons">
${buttonsHtml}
</div>
`;
slicerArea.appendChild(rowBox);
rowBox.querySelectorAll('.slicer-btn').forEach(btn => {
btn.addEventListener('click', function() {
if (this.classList.contains('disabled-slicer') && !this.classList.contains('selected')) return;
this.classList.toggle('selected');
applyFiltersAndSlicers();
});
});
applyFiltersAndSlicers();
}
window.removeSlicer = function(colKey) {
const box = document.getElementById('slicer_' + colKey);
if (box) {
box.remove();
applyFiltersAndSlicers();
}
};
document.getElementById('btnClearSlicers').addEventListener('click', function() {
document.getElementById('slicerArea').innerHTML = '';
document.getElementById('globalSearch').value = '';
applyFiltersAndSlicers();
});
window.clearGlobalSearch = function() {
document.getElementById('globalSearch').value = '';
applyFiltersAndSlicers();
};
// --- DİLİMLEYİCİ ARAYÜZ (BUTON) GÜNCELLEMESİ ---
function updateSlicerUIs(slicerConds, globalSearchValText) {
const boxes = document.querySelectorAll('.slicer-box');
let globalSearchLower = toSafeLower(globalSearchValText);
boxes.forEach(box => {
const colKey = box.dataset.col;
let otherSlicerConds = slicerConds.filter(cond => cond.col !== colKey);
let subset = excelData;
if (otherSlicerConds.length > 0 || globalSearchLower) {
subset = subset.filter(row => {
let passesSlicer = otherSlicerConds.every(cond => {
let cellVal = row[cond.col] !== undefined ? String(row[cond.col]).trim() : "";
return cond.allowed.includes(cellVal);
});
if (!passesSlicer) return false;
let passesSearch = true;
if (globalSearchLower) {
passesSearch = headerKeys.some(k => {
let cellVal = row[k] !== undefined && row[k] !== null ? String(row[k]) : "";
return toSafeLower(cellVal).includes(globalSearchLower);
});
}
return passesSearch;
});
}
let validVals = new Set();
subset.forEach(row => {
let val = row[colKey] !== undefined ? String(row[colKey]).trim() : "";
validVals.add(val);
});
const buttons = Array.from(box.querySelectorAll('.slicer-btn'));
let activeCount = 0;
let selectedCount = 0;
buttons.forEach(btn => {
const val = btn.dataset.val;
const isSelected = btn.classList.contains('selected');
if (isSelected) selectedCount++;
if (validVals.has(val)) {
btn.classList.remove('disabled-slicer');
activeCount++;
} else {
btn.classList.add('disabled-slicer');
}
});
const statsSpan = document.getElementById('stats_' + colKey);
if (statsSpan) {
const totalCount = buttons.length;
statsSpan.innerHTML = `Toplam: ${totalCount} | Seçilen: ${selectedCount} | Aktif: ${activeCount} | <a class="slicer-remove-link" onclick="removeSlicer('${colKey}')" title="Bu filtreyi kaldır">Kaldır</a>`;
}
});
}
window.applyFiltersAndSlicers = function() {
const boxes = document.querySelectorAll('.slicer-box');
let slicerConds = [];
boxes.forEach(box => {
const colKey = box.dataset.col;
const checkedBtns = box.querySelectorAll('.slicer-btn.selected');
if (checkedBtns.length > 0) {
let allowedVals = Array.from(checkedBtns).map(btn => btn.dataset.val);
slicerConds.push({ col: colKey, allowed: allowedVals });
}
});
const globalSearchValRaw = document.getElementById('globalSearch').value;
const globalSearchLower = toSafeLower(globalSearchValRaw);
updateSlicerUIs(slicerConds, globalSearchValRaw);
let dataToProcess = excelData;
if (slicerConds.length > 0 || globalSearchLower) {
dataToProcess = excelData.filter(row => {
let passesSlicer = slicerConds.every(cond => {
let cellVal = row[cond.col] !== undefined ? String(row[cond.col]).trim() : "";
return cond.allowed.includes(cellVal);
});
if (!passesSlicer) return false;
let passesSearch = true;
if (globalSearchLower) {
passesSearch = headerKeys.some(k => {
let cellVal = row[k] !== undefined && row[k] !== null ? String(row[k]) : "";
return toSafeLower(cellVal).includes(globalSearchLower);
});
}
return passesSearch;
});
}
currentPage = 1;
currentFilteredData = dataToProcess;
renderPagination();
updateGauges(excelData.length, dataToProcess.length);
updateProfileImage(currentFilteredData);
};
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>






