<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="theme-color" content="#f8fafc">
<title>İSG Risk Analizi - Fine_Kinney</title>
<style>
:root{--bg:#f8fafc;--surface:#fff;--ink:#17283b;--muted:#64748b;--line:#cbd5e1;--blue:#0284c7;--blue2:#0369a1;--light:#eaf5fc;--radius:6px}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:#edf3f8;color:var(--ink);font:13px/1.42 'Segoe UI',Arial,sans-serif}button,input,textarea,select{font:inherit}button{cursor:pointer}input,textarea,select{width:100%;border:1px solid #cbd5e1;background:#fff;border-radius:5px;min-height:35px;padding:7px 9px;outline:none;color:#1e293b}textarea{min-height:62px;resize:vertical}input:focus,select:focus,textarea:focus{border-color:#0284c7;box-shadow:0 0 0 2px #0284c71c}label{display:block;font-size:11.5px;font-weight:650;color:#334155;margin:0 0 4px}.required:after{content:' *';color:#bd4046}.muted,.hint{font-size:11px;color:#64748b}.olgun-frame{margin:8px;border:2px solid #7f95a9;border-radius:7px;background:#f8fafc;min-height:calc(100vh - 16px);box-shadow:0 5px 19px #223d5511}.top-header{position:sticky;top:0;background:#fff;z-index:30;border-top:4px solid #0284c7;border-bottom:1px solid #bfd0de;border-radius:4px 4px 0 0}.headerinner{min-height:76px;display:flex;align-items:center;gap:13px;padding:9px 19px}.brand-logo{width:62px;height:55px;display:grid;place-items:center;flex:none;background:#f8fafc;border:1px solid #cbd5e1;border-radius:5px;padding:3px;font-weight:800;color:#075985;font-size:12px}.brand-logo img{width:100%;height:100%;object-fit:contain}.header-titles{min-width:0;flex:1}.header-titles h1{margin:0;font-size:18px;color:#075985;font-weight:750;line-height:1.2}.header-titles p{margin:4px 0 0;color:#475569;font-size:12px}.brand-org{max-width:40%;text-align:right;margin-left:auto;font-size:16px;font-weight:750;white-space:nowrap;color:#1e3a57;overflow:hidden;text-overflow:ellipsis}.top-header [contenteditable=true]{outline:2px dashed #0284c7;outline-offset:3px;white-space:normal}.wrap{padding:12px 17px 7px;display:grid;gap:10px}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.metric{background:#fff;border:1px solid #cbd5e1;border-top:3px solid var(--blue);border-radius:6px;padding:10px 13px}.metric small{font-size:10px;display:block;color:#64748b;font-weight:700}.metric b{display:block;margin-top:3px;color:#0f3652;font-size:24px;line-height:1.18}.card{background:#fff;border:1px solid var(--line);border-radius:6px;box-shadow:0 2px 8px #25425b08;min-width:0}.card-header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;background:#eaf5fc;border-bottom:1px solid #b9d4e6;border-left:4px solid #0284c7;border-radius:5px 5px 0 0}.card-header h2{margin:0;display:flex;align-items:center;gap:7px;color:#084c71;font-size:14px}.sect{font-size:10px;background:#d2eafb;border-radius:4px;padding:3px 6px;color:#075985}.card-body{padding:11px 13px}.grid{display:grid;gap:10px}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.strip{margin:11px 0 8px;display:flex;align-items:center;gap:8px;color:#075985;font-size:12px;font-weight:800;border-bottom:1px solid #dcecf5;padding:0 0 6px}.strip:first-child{margin-top:0}.strip em{display:grid;place-items:center;font-style:normal;width:22px;height:22px;background:#eaf6fc;border:1px solid #b8daed;border-radius:4px;font-size:10px;color:#0369a1}.method-lock{min-height:35px;background:#eef8ff;border:1px solid #b9def7;border-radius:5px;color:#075985;font-weight:750;padding:8px 10px;font-size:12px;display:flex;justify-content:space-between;gap:5px}.method-lock span{font-size:10px;font-weight:500;color:#64748b}.assessment{display:grid;grid-template-columns: minmax(340px,0.88fr) minmax(480px,1.12fr);gap:12px}.score-stack{display:grid;grid-template-columns:1fr;gap:8px}.score-panel{border:1px solid #cbd5e1;border-radius:6px;overflow:hidden;background:#fff;min-width:0}.score-panel[data-side=before]{border-top:3px solid #125ba9}.score-panel[data-side=after]{border-top:3px solid #8651b4}.score-head{background:#f2f7fb;padding:7px 9px;display:flex;justify-content:space-between;align-items:center;gap:8px;border-bottom:1px solid #e3eaf0;font-size:12px}.score-head i{display:inline-block;width:9px;height:9px;background:#125ba9;margin-right:5px;border-radius:50%}.score-panel[data-side=after] .score-head i{background:#8651b4;border-radius:2px}.factor-fields{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px;padding:8px}.score-result{border-top:1px solid #e8edf1;background:#f7fafc;display:flex;align-items:center;justify-content:space-between;gap:5px;padding:6px 9px}.score-result b{font-size:20px;line-height:1.1}.score-result small{display:block;font-size:10px;color:#64748b}.risk-tag{display:inline-block;font-size:10px;font-weight:800;padding:4px 7px;border-radius:4px;border:1px solid transparent;text-align:center}.risk-low{background:#ddf7e4;border-color:#a7e0bc;color:#166534}.risk-possible{background:#fff8df;border-color:#edd994;color:#805b14}.risk-important{background:#ffedbd;border-color:#ebc367;color:#865408}.risk-high{background:#ffe0c5;border-color:#e5a074;color:#9e4512}.risk-critical{background:#ffdee2;border-color:#ec9eaa;color:#a51d30}.risk-na{background:#edf2f7;border-color:#ced6e0;color:#64748b}.guide{background:#fff;border:1px solid #aac6d7;border-radius:6px;overflow:hidden;min-width:0}.guide-head{display:flex;align-items:center;justify-content:space-between;gap:9px;padding:9px 11px;background:#e9f4fc;border-bottom:1px solid #cbdde8}.guide-head strong{font-size:12px;color:#0b4669}.mode-tabs{display:flex;gap:5px;flex-wrap:wrap}.mode-btn{border:1px solid #bfd6e7;background:#fff;color:#315d76;padding:5px 8px;border-radius:5px;min-height:30px;font-weight:750;font-size:11px}.mode-btn.active{background:#dcedfb;color:#075985;border-color:#73acd0}.guide-body{padding:11px 12px}.bands{display:grid;gap:6px}.band{display:grid;grid-template-columns:100px minmax(30px,1fr) 102px;align-items:center;gap:7px}.band-name{font-weight:700;font-size:11px;white-space:nowrap}.band-bar{background:#f0f4f8;border:1px solid #dfe7ee;border-radius:5px;height:26px;position:relative;overflow:hidden}.band-fill{height:100%;border-radius:3px;width:100%;opacity:.88;transition:width .18s}.band[data-band=low] .band-fill{background:#73cf93}.band[data-band=possible] .band-fill{background:#eed572}.band[data-band=important] .band-fill{background:#f7b359}.band[data-band=high] .band-fill{background:#f28b59}.band[data-band=critical] .band-fill{background:#e97575}.band-count{font-weight:750;color:#334155;font-size:11px;text-align:right;white-space:nowrap}.band[data-current=yes] .band-bar{outline:2px solid #125ba9;outline-offset:1px}.band[data-target=yes] .band-bar{box-shadow:0 0 0 3px #a763c1 inset}.guide-summary{margin-top:10px;padding:8px 9px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:5px;color:#475569;font-size:11px;min-height:32px}.legend{display:flex;gap:7px;flex-wrap:wrap;margin-top:9px}.legend span{font-size:10px;color:#475569}.legend b{font-size:10px;color:#334155}.instruction{color:#536b7d;font-size:11px;margin-top:8px}.verifiedbox{margin-top:10px;background:#f7fbfd;border:1px solid #d9e7ef;padding:9px 10px;border-radius:5px}.checkline{display:flex;gap:8px;align-items:center;font-size:11.5px;color:#334155}.checkline input{width:16px;min-height:0;height:16px;accent-color:#0284c7}.actionbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:12px}.spacer{flex:1}.btn{border:1px solid #cbd5e1;border-radius:5px;padding:7px 10px;min-height:34px;background:#fff;color:#334155;display:inline-flex;align-items:center;justify-content:center;gap:5px;font-weight:650;white-space:nowrap}.btn:hover{background:#f1f5f9}.btn-primary{background:#0284c7;color:#fff;border-color:#0284c7}.btn-primary:hover{background:#0369a1}.btn-sm{min-height:27px;padding:4px 7px;font-size:11px}.btn-danger{color:#b42332;border-color:#f0bac3}.btn-softblue{background:#e8f4ff;border-color:#b7d8fb;color:#075985}.btn-softgreen{background:#eaf8ef;border-color:#b9e2c6;color:#166534}.btn-softgray{background:#f1f3f5;border-color:#d6dbe1;color:#334155}.filters{display:flex;gap:7px;flex-wrap:wrap}.filters input{width:190px}.filters select{width:155px}.tablebox{overflow-x:auto}.tablebox table{border-collapse:collapse;width:100%;min-width:1120px}.tablebox th{background:#eef5f9;color:#475569;font-weight:750;font-size:11px;text-align:left;padding:8px 7px;border-bottom:1px solid #d6e2ea;white-space:nowrap}.tablebox td{font-size:11px;padding:8px 7px;border-bottom:1px solid #e9eff4;vertical-align:top;overflow-wrap:anywhere}.tablebox tbody tr:hover{background:#f7fbfe}.cellsub{font-size:10px;color:#64748b;margin-top:3px}.ellips{max-width:235px;white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.empty{text-align:center;padding:21px 10px;color:#64748b;font-size:12px}.table-bottom{padding:8px 12px}.bottom-actions{padding:4px 17px 5px;display:flex}.bottom-panel{display:flex;gap:9px;flex-wrap:wrap;background:#fff;border:1px solid #b9cbd9;padding:9px 11px;border-radius:6px}.risk-ui-footer{margin:0 17px 10px;padding:7px 0 5px;border-top:1px solid #dbe3eb;color:#475569;font-size:11px;white-space:nowrap;overflow:auto}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.notice{position:fixed;bottom:18px;right:18px;z-index:80;background:#143d54;color:white;padding:10px 13px;border-radius:6px;max-width:420px;box-shadow:0 5px 18px #12374c30;font-size:12px;opacity:0;pointer-events:none;transition:opacity .15s}.notice.show{opacity:1}#printPages,#printPageFrame{display:none}
@media(max-width:1080px){.assessment{grid-template-columns:1fr}.score-stack{grid-template-columns:repeat(2,minmax(0,1fr))}.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:720px){.olgun-frame{margin:3px}.headerinner{padding:8px 10px;flex-wrap:wrap}.brand-logo{width:49px;height:49px}.header-titles h1{font-size:15px}.header-titles p{font-size:10px}.brand-org{max-width:100%;width:100%;font-size:11px;white-space:normal}.wrap{padding:9px;gap:9px}.card-header{flex-wrap:wrap}.g5,.g4,.g3,.g2{grid-template-columns:1fr}.span2,.span3{grid-column:auto}.score-stack{grid-template-columns:1fr}.factor-fields{grid-template-columns:1fr 1fr 1fr}.band{grid-template-columns:83px 1fr 75px}.mode-btn{font-size:10px}.filters{width:100%}.filters input,.filters select{flex:1;width:100%;min-width:120px}.bottom-actions{padding:6px 9px}.bottom-panel{width:100%;gap:5px}.bottom-panel .btn{flex:1 1 120px;white-space:normal}.risk-ui-footer{margin:0 9px 8px;font-size:10px}}
@media(max-width:420px){.factor-fields{grid-template-columns:1fr}.band{grid-template-columns:87px 1fr 65px}.band-name,.band-count{font-size:10px}}
@page{size:A4 landscape;margin:9mm 9mm 16mm 9mm;@bottom-right{content:'Sayfa ' counter(page) ' / ' counter(pages);font:9pt Arial;color:#34495c}}
@media print{
html,body{margin:0!important;padding:0!important;background:#fff!important;-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important;font-size:9px!important}
#olgunFrame,#toast{display:none!important}
#printPages{display:table!important;width:100%!important;border:0!important;border-spacing:0;table-layout:fixed;border-collapse:separate}
#printPages>thead{display:table-header-group!important}
#printPages>tbody{display:table-row-group!important}
#printPages>thead>tr>td{padding:0 0 8px;border:0}
#printPages>tbody>tr>td{padding:0 10px 8px;border:0}
#printPageFrame{display:block!important;position:fixed;inset:0;border:1.5px solid #7f95a9;pointer-events:none;z-index:50}
.print-header{display:flex;align-items:center;gap:10px;padding:6px 12px;border-top:4px solid #0284c7;border-bottom:1.5px solid #7f95a9;background:white;height:65px}
.print-header img{width:50px;height:47px;object-fit:contain}
.print-header h1{font-size:17px;color:#075985;margin:0}.print-header p{font-size:10px;color:#475569;margin:3px 0 0}
.print-header .print-org{margin-left:auto;font-size:14px;color:#1e3a57;font-weight:700;text-align:right}
.print-section{border:1px solid #b5c9d8;border-radius:4px;margin-bottom:8px;break-inside:auto}
.print-section h3{font-size:11px;background:#eaf5fc;border-left:4px solid #0284c7;color:#075985;margin:0;padding:5px 8px;break-after:avoid}
.print-summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:5px;padding:7px}
.print-summary div{border:1px solid #dbe3eb;padding:5px;font-size:9px}
.print-summary b{display:block;color:#075985;font-size:13px}
.print-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:8px}
.print-field{border:1px solid #dbe3eb;border-radius:3px;min-height:23px;padding:4px 6px;overflow-wrap:anywhere;white-space:pre-wrap;font-size:9px}
.print-field b{display:block;font-size:8px;color:#64748b;font-weight:600;margin-bottom:2px}
.print-table{border-collapse:collapse;width:100%;table-layout:fixed}.print-table th,.print-table td{border:1px solid #dbe3eb;padding:4px 5px;font-size:8px;vertical-align:top;overflow-wrap:anywhere}.print-table th{background:#eaf5fc;color:#075985}.print-table thead{display:table-header-group}.print-table tr{break-inside:avoid}.print-note{font-size:9px;color:#64748b;padding:6px 8px}.print-no-break{break-inside:avoid}
}
/* RISK UI - yazdırılabilir, gerçek SVG vektör grafikler. Zemin grafiği tercihi gerektirmez. */
@media print {
.print-chart-section{break-inside:avoid!important;page-break-inside:avoid!important}
.print-chart-layout{display:grid!important;grid-template-columns:1fr 1fr!important;gap:9px!important;padding:8px!important;align-items:start!important}
.print-chart-box{min-width:0;border:1px solid #cbd5e1;border-radius:4px;padding:5px 7px 7px;break-inside:avoid!important;page-break-inside:avoid!important;background:white!important}
.print-chart-box h4{margin:0 0 3px;font-size:10px;color:#075985}
.print-chart-box svg{display:block;width:100%;height:auto;max-height:132px;overflow:visible}
.print-chart-foot{font-size:9px;color:#425567;line-height:1.35;min-height:16px;margin:2px 2px 0}
.print-chart-key{display:flex;flex-wrap:wrap;gap:7px;margin-top:4px;color:#334155;font-size:8.5px}
.print-chart-key span{display:inline-flex;gap:3px;align-items:center}
.print-chart-key .mark{display:inline-block;width:9px;height:9px;border:2px solid #125ba9}
.print-chart-key .mark.target{border:2px dashed #8651b4}
.print-chart-section,.print-chart-section *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
}
/* Tek satır nihai altbilgi: başka satır veya aşağıda boşluk yok. */
.risk-ui-footer{margin:0 17px 0!important;padding:5px 0 5px!important;border-top:1px solid #dbe3eb;white-space:nowrap!important;overflow-x:auto;overflow-y:hidden;box-sizing:border-box;line-height:1.4!important;}
.risk-ui-footer-single-line{display:inline-block;white-space:nowrap!important;font-size:11px;font-weight:500;letter-spacing:-.08px;}
@media(min-width:721px) and (max-width:1100px){.risk-ui-footer-single-line{font-size:10px}}
@media(max-width:720px){.risk-ui-footer{margin:0 9px 0!important;padding:4px 0 4px!important}.risk-ui-footer-single-line{font-size:10px}}
@media print{.risk-ui-footer{display:none!important}}
/* Yazdırmada her sayfanın başlığında resim veya LOGO yedeği */
@media print{
.print-logo-box{width:54px;height:50px;display:grid;place-items:center;flex:0 0 54px;min-width:54px;overflow:hidden}
.print-logo-box img{display:block;max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
.print-logo-box img[hidden],.print-logo-box b[hidden]{display:none!important}
.print-logo-box b{font-size:11px;color:#075985}
}
</style>
</head>
<body>
<div id="olgunFrame" class="olgun-frame">
<header class="top-header"><div class="headerinner">
<button id="logoTrigger" class="brand-logo" type="button" title="Logoyu değiştirmek için art arda 5 kez tıklayın"><img id="brandLogo" alt="Kurum logosu" hidden><span id="logoFallback">LOGO</span></button>
<div class="header-titles"><h1 id="brandTitle" title="Düzenlemek için 5 kez tıklayın">İSG RİSK ANALİZİ – FINE-KINNEY</h1><p id="brandSubtitle" title="Düzenlemek için 5 kez tıklayın">(Olasılık × Frekans × Şiddet · Risk Değerlendirme ve Aksiyon Takip Formu)</p></div>
<div id="brandOrg" class="brand-org" title="Düzenlemek için 5 kez tıklayın">OLGUN BAŞKANLIĞI</div>
</div><input id="logoChooser" type="file" class="sr-only" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml"></header>
<main class="wrap" id="app">
<section class="metrics" aria-label="Risk özetleri">
<div class="metric"><small>TOPLAM RİSK KAYDI</small><b id="statTotal">0</b></div>
<div class="metric"><small>YÜKSEK / ÇOK YÜKSEK · AÇIK</small><b id="statHigh">0</b></div>
<div class="metric"><small>HEDEF TARİHİ GEÇEN</small><b id="statOverdue">0</b></div>
<div class="metric"><small>ÖNLEMİ DOĞRULANAN</small><b id="statVerified">0</b></div>
</section>
<section class="card" aria-labelledby="metaTitle"><div class="card-header"><h2 id="metaTitle"><span class="sect">01</span> İşyeri ve Değerlendirme Bilgileri</h2><span class="hint">Giriş alanları her yeni açılışta boş başlar.</span></div>
<div class="card-body grid g5">
<div><label for="metaCompany">İşyeri / Firma</label><input data-meta="company" id="metaCompany" placeholder="Örn. ABC Metal Sanayi"></div>
<div><label for="metaFacility">Tesis / Birim</label><input data-meta="facility" id="metaFacility" placeholder="Örn. Üretim Tesisi"></div>
<div><label for="metaPrepared">Hazırlayan / Ekip</label><input data-meta="prepared" id="metaPrepared" placeholder="Risk değerlendirme ekibi"></div>
<div><label for="metaDate">Değerlendirme Tarihi</label><input data-meta="date" id="metaDate" type="date"></div>
<div><label for="metaRevision">Revizyon / Belge No.</label><input data-meta="revision" id="metaRevision" placeholder="Örn. FK-2026 / Rev.00"></div>
</div></section>
<section class="card" aria-labelledby="formTitle"><div class="card-header"><h2 id="formTitle"><span class="sect">02</span> <span id="formTitleText">Yeni Risk Kaydı</span></h2><button class="btn btn-sm" id="newBtn" type="button">+ Yeni Kayıt</button></div>
<div class="card-body"><form id="riskForm" novalidate autocomplete="off">
<div class="strip"><em>01</em> Riskin Tanımlanması</div>
<div class="grid g4">
<div><label>Değerlendirme Yöntemi</label><div class="method-lock">Fine-Kinney <span>O × F × Ş</span></div></div>
<div><label for="unit" class="required">Bölüm / Alan</label><input id="unit" required placeholder="Örn. Pres Atölyesi"></div>
<div><label for="activity" class="required">Faaliyet / İş Adımı</label><input id="activity" required placeholder="Örn. Pres kalıbı değişimi"></div>
<div><label for="affected">Etkilenebilecek Kişiler</label><input id="affected" placeholder="Operatör, bakım personeli..."></div>
</div>
<div class="grid g3" style="margin-top:9px">
<div><label class="required" for="hazard">Tehlike Kaynağı</label><textarea required id="hazard" placeholder="Korumasız hareketli parçalar"></textarea></div>
<div><label class="required" for="risk">Risk / Olası Zarar</label><textarea required id="risk" placeholder="Sıkışma, uzuv yaralanması"></textarea></div>
<div><label for="existing">Mevcut Kontroller / Önlemler</label><textarea id="existing" placeholder="Mevcut makine koruyucuları, talimatlar..."></textarea></div>
</div>
<div class="strip"><em>02</em> Fine-Kinney Risk Puanlaması – Olasılık × Frekans × Şiddet</div>
<div class="assessment">
<div class="score-stack">
<section class="score-panel" data-side="before"><div class="score-head"><strong><i></i> MEVCUT RİSK</strong><span class="hint">Tedbir öncesi</span></div><div class="factor-fields" id="beforeFields"></div><div class="score-result"><div><small>Mevcut risk puanı</small><b id="beforeScore">—</b></div><span class="risk-tag risk-na" id="beforeLevel">Değerlendirilmedi</span></div></section>
<section class="score-panel" data-side="after"><div class="score-head"><strong><i></i> HEDEF RİSK</strong><span class="hint" id="afterTitle">Planlanan seviye</span></div><div class="factor-fields" id="afterFields"></div><div class="score-result"><div><small>Hedef / kalan risk puanı</small><b id="afterScore">—</b></div><span class="risk-tag risk-na" id="afterLevel">Değerlendirilmedi</span></div></section>
</div>
<section class="guide" aria-labelledby="guideTitle"><div class="guide-head"><strong id="guideTitle">▦ Fine-Kinney Risk Ölçeği</strong><div class="mode-tabs" role="group" aria-label="Risk görünümü"><button type="button" id="modeSingle" class="mode-btn active" aria-pressed="true">Tekil Risk</button><button type="button" id="modeDistribution" class="mode-btn" aria-pressed="false">Genel Risk Dağılımı</button></div></div>
<div class="guide-body"><div id="bandVisual" class="bands" aria-label="Fine Kinney risk düzeyleri"></div><div id="guideSummary" class="guide-summary" aria-live="polite">Olasılık, frekans ve şiddet seçildiğinde sonuç burada görüntülenir.</div><div class="legend"><span><b style="color:#125ba9">●</b> Mevcut risk</span><span><b style="color:#8651b4">◆</b> Hedef risk</span></div><div class="instruction">Risk sınırları: 20, 70, 200 ve 400 puan. Sınır değerleri için sırasıyla üst kategori kullanılır; 400 puan yüksek risk kapsamındadır. Farklı kurumsal sınıflamalar bulunabilir.</div></div></section>
</div>
<div class="strip"><em>03</em> Tedbirler ve Aksiyon Takibi</div>
<div class="grid g4">
<div class="span2"><label for="action" class="required">Alınacak Tedbirler / Düzeltici Faaliyet</label><textarea id="action" required placeholder="Tehlikeyi ortadan kaldırma, mühendislik kontrolü..."></textarea></div>
<div><label for="responsible">Sorumlu Kişi / Birim</label><input id="responsible" placeholder="Örn. Bakım Müdürü"></div>
<div><label for="deadline">Hedef Tamamlanma Tarihi</label><input id="deadline" type="date"></div>
<div><label for="status">Aksiyon Durumu</label><select id="status"><option value="">Seçiniz</option><option value="open">Açık</option><option value="inprogress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div>
<div class="span3"><label for="notes">Ek Açıklamalar / Doğrulama Kanıtı</label><input id="notes" placeholder="Ölçüm, fotoğraf veya kontrol tutanağı açıklaması..."></div>
</div>
<div class="verifiedbox"><label class="checkline"><input id="verified" type="checkbox"> Önlem uygulandı, etkinliği kontrol edildi ve <b>kalan risk doğrulandı</b></label><div class="hint">Yalnızca “Tamamlandı” durumunda kullanılabilir. İşaretli değilse hedef puan gerçekleşmiş sonuç değildir.</div></div>
<div class="actionbar"><button type="submit" id="saveBtn" class="btn btn-primary">+ Riski Kaydet</button><button type="button" id="cancelBtn" class="btn">Vazgeç / Yeni Kayıt</button><span class="spacer"></span><span class="hint" id="formInfo">Yeni kayıt oluşturuluyor.</span></div>
</form></div></section>
<section class="card" aria-labelledby="tableTitle"><div class="card-header"><h2 id="tableTitle"><span class="sect">03</span> Risk Kayıtları ve Aksiyon Takibi <span id="recordCount" class="hint">(0)</span></h2>
<div class="filters"><input type="search" id="searchInput" autocomplete="off" placeholder="Kayıtlarda ara..." aria-label="Kayıtlarda ara"><select id="statusFilter" aria-label="Duruma göre filtrele"><option value="">Tüm durumlar</option><option value="open">Açık</option><option value="inprogress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div></div>
<div class="tablebox"><table aria-label="Fine-Kinney risk kayıtları"><thead><tr><th>No.</th><th>Bölüm / Faaliyet</th><th>Tehlike / Risk</th><th>Mevcut O×Fת</th><th>Hedef O×Fת</th><th>Tedbir / Aksiyon</th><th>Termin / Sorumlu</th><th>Durum</th><th>İşlemler</th></tr></thead><tbody id="riskRows"></tbody></table><div class="empty" id="emptyState">Henüz risk kaydı bulunmuyor. Yukarıdaki formdan risk ekleyebilirsiniz.</div></div>
<div class="table-bottom"><button type="button" class="btn btn-danger btn-sm" id="clearBtn">Tüm Kayıtları Sil</button></div></section>
</main>
<div class="bottom-actions"><div class="bottom-panel" aria-label="Dosya ve yazdırma işlemleri"><button type="button" id="importBtn" class="btn btn-softblue">⬆ Excel İçeri Aktar</button><button type="button" id="exportBtn" class="btn btn-softgreen">⬇ Excel Dışarı Aktar</button><button type="button" id="pdfBtn" class="btn btn-softgray">▣ PDF / Yazdır</button><input id="importFile" type="file" class="sr-only" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"></div></div>
<footer class="risk-ui-footer" aria-label="Fine-Kinney yöntem, kullanım alanı ve faktör puanları"><span class="risk-ui-footer-single-line">Temel özelliği: Olasılık × Frekans × Şiddet Kullanım alanı: Fabrikalar, atölyeler, üretim tesisleri (Olasılık: 0,1 – 0,2 – 0,5 – 1 – 3 – 6 – 10) (Frekans: 0,5 – 1 – 2 – 3 – 6 – 10) (Şiddet: 1 – 3 – 7 – 15 – 40 – 100)</span></footer>
</div>
<table id="printPages" role="presentation"><thead><tr><td><div id="printHeader"></div></td></tr></thead><tbody><tr><td><div id="printReport"></div></td></tr></tbody></table><div id="printPageFrame"></div>
<div id="toast" class="notice" role="status" aria-live="polite"></div>
<script>
'use strict';
(()=>{
const $=id=>document.getElementById(id);
const today=()=>{const d=new Date();return [d.getFullYear(),String(d.getMonth()+1).padStart(2,'0'),String(d.getDate()).padStart(2,'0')].join('-')};
const asText=v=>v===null||v===undefined?'':String(v);
const esc=v=>asText(v).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const randomId=()=>('FK'+Date.now().toString(36)+Math.random().toString(36).slice(2,7)).toUpperCase();
const BANDS=[{key:'low',min:0,max:20,label:'Kabul Edilebilir',short:'< 20',action:'Mevcut tedbirleri sürdür, izlemeyi devam ettir.'},{key:'possible',min:20,max:70,label:'Olası Risk',short:'20 – <70',action:'İzle ve gerekiyorsa eylem planına al.'},{key:'important',min:70,max:200,label:'Önemli Risk',short:'70 – <200',action:'İyileştirici önlem planla ve takip et.'},{key:'high',min:200,max:400.00001,label:'Yüksek Risk',short:'200 – 400',action:'Kısa vadede önlem al; işin devamını değerlendir.'},{key:'critical',min:400.00001,max:100000,label:'Çok Yüksek Risk',short:'> 400',action:'Acil tedbir al; faaliyetin durdurulmasını değerlendir.'}];
const OPTS={p:[[0.1,'0,1 · Neredeyse imkânsız'],[0.2,'0,2 · Pratikte beklenmez'],[0.5,'0,5 · Zayıf ihtimal'],[1,'1 · Düşük ihtimal'],[3,'3 · Nadir ama mümkün'],[6,'6 · Kuvvetle muhtemel'],[10,'10 · Beklenen']],f:[[0.5,'0,5 · Çok seyrek / yılda bir'],[1,'1 · Seyrek / yılda birkaç kez'],[2,'2 · Ara sıra / ayda bir'],[3,'3 · Zaman zaman / haftada bir'],[6,'6 · Sık / günde bir veya birkaç'],[10,'10 · Sürekli / saatlik veya daha sık']],s:[[1,'1 · Önemsiz sonuç'],[3,'3 · Küçük hasar / ilk yardım'],[7,'7 · Önemli yaralanma'],[15,'15 · Çok ciddi yaralanma'],[40,'40 · Ölüm / ağır kayıp'],[100,'100 · Birden çok ölüm / felaket']]};
const LABELS={p:'Olasılık (O)',f:'Frekans (F)',s:'Şiddet (Ş)'};
const statusName={open:'Açık',inprogress:'Devam Ediyor',done:'Tamamlandı'};
const initMeta=()=>({company:'',facility:'',prepared:'',date:'',revision:''});
const emptyFactors=()=>({p:null,f:null,s:null});
let state={meta:initMeta(),records:[]};let working={before:emptyFactors(),after:emptyFactors()};let editingId=null;let viewMode='single';let toastHandle;
const valid=(key,v)=>OPTS[key].some(x=>x[0]===Number(v));
const factorsValid=o=>o&&['p','f','s'].every(k=>valid(k,o[k]));
const score=o=>factorsValid(o)?Math.round(Number(o.p)*Number(o.f)*Number(o.s)*1000)/1000:null;
const bandFor=n=>n===null?null:n<20?BANDS[0]:n<70?BANDS[1]:n<200?BANDS[2]:n<=400?BANDS[3]:BANDS[4];
const fmt=n=>n===null?'—':Number(n).toLocaleString('tr-TR',{maximumFractionDigits:3});
const isEmptyString=v=>asText(v).trim()==='';
function recordNormalize(raw){if(!raw||typeof raw!=='object'||(raw.method&&raw.method!=='fine-kinney'))return null;
const pick=side=>{const src=raw[side]||{};const out={};for(const k of ['p','f','s'])out[k]=isEmptyString(src[k])?null:(valid(k,src[k])?Number(src[k]):null);return out};
const result={id:asText(raw.id)||randomId(),method:'fine-kinney',unit:asText(raw.unit),activity:asText(raw.activity),hazard:asText(raw.hazard),risk:asText(raw.risk),affected:asText(raw.affected),existing:asText(raw.existing),action:asText(raw.action),responsible:asText(raw.responsible),deadline:asText(raw.deadline),status:statusName[raw.status]?raw.status:'open',verified:Boolean(raw.verified)&&raw.status==='done',notes:asText(raw.notes),created:asText(raw.created)||today(),before:pick('before'),after:pick('after')};
if(!factorsValid(result.before)||!factorsValid(result.after))return null;return result;
}
function resetData(){state={meta:initMeta(),records:[]};working={before:emptyFactors(),after:emptyFactors()};editingId=null;viewMode='single';}
function toast(msg){clearTimeout(toastHandle);$('toast').textContent=msg;$('toast').classList.add('show');toastHandle=setTimeout(()=>$('toast').classList.remove('show'),3700)}
/* Risk UI: yalnızca logo ve başlık tercihi kalıcıdır; form ve kayıtlar yenilenince boş kalır. */
const BRAND_KEY='riskui-finekinney-brand-v1';
const BRAND_DEFAULT={title:'İSG RİSK ANALİZİ – FINE-KINNEY',subtitle:'(Olasılık × Frekans × Şiddet · Risk Değerlendirme ve Aksiyon Takip Formu)',org:'OLGUN BAŞKANLIĞI',logo:''};
let brand={...BRAND_DEFAULT};
function storeBrand(){try{localStorage.setItem(BRAND_KEY,JSON.stringify(brand))}catch(e){toast('Başlık bilgileri bu tarayıcıda saklanamadı.')}}
function initializeBrand(){try{const old=JSON.parse(localStorage.getItem(BRAND_KEY)||'null');if(old&&typeof old==='object')for(const k of Object.keys(BRAND_DEFAULT))if(typeof old[k]==='string')brand[k]=old[k]}catch(e){}
$('brandTitle').textContent=brand.title;$('brandSubtitle').textContent=brand.subtitle;$('brandOrg').textContent=brand.org;setLogo(brand.logo);
setupFive($('logoTrigger'),()=>$('logoChooser').click());for(const [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']])setupFive($(id),()=>editBrand($(id),key));
$('logoChooser').addEventListener('change',e=>{const file=e.target.files?.[0];if(!file)return;if(!file.type.startsWith('image/')||file.size>1200000){alert('1,2 MB altında bir görsel seçin.');e.target.value='';return}const reader=new FileReader();reader.onload=()=>{brand.logo=String(reader.result||'');setLogo(brand.logo);storeBrand();toast('Kurum logosu kaydedildi.')};reader.readAsDataURL(file);e.target.value=''})
}
function setLogo(data){
const img=$('brandLogo'),fallback=$('logoFallback');
img.onload=()=>{img.hidden=false;fallback.hidden=true;syncPrintableLogo()};
img.onerror=()=>{img.hidden=true;fallback.hidden=false};
// Olgun UI: Aynı klasörde logo.jpg varsa kendiliğinden kullanılır.
// Kullanıcının daha önce seçtiği logo bunun önüne geçer.
img.src=(data&&/^data:image\//.test(data))?data:'logo.jpg';
}
function setupFive(el,cb){let clicks=0,last=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;const now=Date.now();clicks=now-last>1150?1:clicks+1;last=now;if(clicks>=5){clicks=0;cb()}})}
function editBrand(el,key){el.contentEditable='true';el.focus();const r=document.createRange(),s=window.getSelection();r.selectNodeContents(el);s.removeAllRanges();s.addRange(r);let closed=false;
const save=()=>{if(closed)return;closed=true;el.contentEditable='false';brand[key]=el.textContent.trim()||BRAND_DEFAULT[key];el.textContent=brand[key];storeBrand();el.removeEventListener('blur',save);el.removeEventListener('keydown',onKey)};
const onKey=e=>{if(e.key==='Enter'){e.preventDefault();el.blur()}else if(e.key==='Escape'){e.preventDefault();el.textContent=brand[key];el.blur()}};el.addEventListener('blur',save);el.addEventListener('keydown',onKey)
}
function renderMeta(){for(const el of document.querySelectorAll('[data-meta]'))el.value=state.meta[el.dataset.meta]||''}
function factorHTML(side){return ['p','f','s'].map(key=>`<div><label for="factor-${side}-${key}">${LABELS[key]}</label><select data-side="${side}" data-factor="${key}" id="factor-${side}-${key}" required><option value="">Seçiniz</option>${OPTS[key].map(([v,label])=>`<option value="${v}" ${working[side][key]===v?'selected':''}>${esc(label)}</option>`).join('')}</select></div>`).join('')}
function renderFactors(){$('beforeFields').innerHTML=factorHTML('before');$('afterFields').innerHTML=factorHTML('after');renderScore()}
function renderScore(){for(const side of ['before','after']){const v=score(working[side]),b=bandFor(v);$(''+side+'Score').textContent=fmt(v);const el=$(''+side+'Level');el.textContent=b?b.label:'Değerlendirilmedi';el.className='risk-tag '+(b?'risk-'+b.key:'risk-na')}
$('afterTitle').textContent=$('verified').checked&&$('status').value==='done'?'Kalan risk doğrulandı':'Planlanan seviye';renderBands()}
function renderBands(){const counts=BANDS.map(()=>0);for(const r of state.records){const v=score(r.before),b=bandFor(v);if(b)counts[BANDS.indexOf(b)]++}
const mx=Math.max(1,...counts);const a=bandFor(score(working.before)),z=bandFor(score(working.after)),dist=viewMode==='distribution';
$('modeSingle').classList.toggle('active',!dist);$('modeDistribution').classList.toggle('active',dist);$('modeSingle').setAttribute('aria-pressed',String(!dist));$('modeDistribution').setAttribute('aria-pressed',String(dist));
$('bandVisual').innerHTML=BANDS.map((b,i)=>`<div class="band" data-band="${b.key}" data-current="${!dist&&a?.key===b.key?'yes':'no'}" data-target="${!dist&&z?.key===b.key?'yes':'no'}"><div class="band-name">${b.label}</div><div class="band-bar"><div class="band-fill" style="width:${dist?counts[i]/mx*100:100}%"></div></div><div class="band-count">${dist?counts[i]+' kayıt':b.short}</div></div>`).join('');
const summary=$('guideSummary');if(dist)summary.textContent=`Toplam ${state.records.length} risk kaydı · Bantlarda gösterilen sayılar mevcut risklerin dağılımıdır; ortalama değildir.`;
else if(a&&z)summary.innerHTML=`Mevcut: <strong>${fmt(score(working.before))} · ${a.label}</strong> → Hedef: <strong>${fmt(score(working.after))} · ${z.label}</strong>`;
else summary.textContent='Mevcut ve hedef riskin Olasılık, Frekans, Şiddet değerlerini seçin.';
}
function setView(v){viewMode=v==='distribution'?'distribution':'single';renderBands()}
function clearForm(){editingId=null;$('riskForm').reset();$('status').value='';$('verified').checked=false;working={before:emptyFactors(),after:emptyFactors()};$('formTitleText').textContent='Yeni Risk Kaydı';$('saveBtn').textContent='+ Riski Kaydet';$('formInfo').textContent='Yeni kayıt oluşturuluyor.';renderFactors()}
function valuesFromForm(){const v=id=>$(id).value.trim();return recordNormalize({id:editingId||randomId(),method:'fine-kinney',unit:v('unit'),activity:v('activity'),hazard:v('hazard'),risk:v('risk'),affected:v('affected'),existing:v('existing'),action:v('action'),responsible:v('responsible'),deadline:v('deadline'),status:v('status'),verified:$('verified').checked,notes:v('notes'),created:editingId?(state.records.find(r=>r.id===editingId)?.created||today()):today(),before:working.before,after:working.after})}
function startEdit(id){const r=state.records.find(x=>x.id===id);if(!r)return;editingId=r.id;for(const k of ['unit','activity','hazard','risk','affected','existing','action','responsible','deadline','status','notes'])$(k).value=r[k]||'';$('verified').checked=r.verified;working={before:{...r.before},after:{...r.after}};renderFactors();$('formTitleText').textContent='Risk Kaydını Düzenle';$('saveBtn').textContent='✓ Değişiklikleri Kaydet';$('formInfo').textContent='Kayıt düzenleniyor.';setView('single');$('formTitle').scrollIntoView({block:'start',behavior:'smooth'})}
function copyRecord(id){const r=state.records.find(x=>x.id===id);if(!r)return;const c=structuredClone(r);c.id=randomId();c.created=today();c.verified=false;if(c.status==='done')c.status='open';state.records.push(c);renderDashboard();toast('Risk kaydı çoğaltıldı.')}
function deleteRecord(id){if(!confirm('Bu risk kaydını silmek istiyor musunuz?'))return;state.records=state.records.filter(x=>x.id!==id);if(editingId===id)clearForm();renderDashboard();toast('Risk kaydı silindi.')}
function renderDashboard(){const q=$('searchInput').value.trim().toLocaleLowerCase('tr-TR'),filter=$('statusFilter').value,t=today();$('statTotal').textContent=state.records.length;$('statHigh').textContent=state.records.filter(r=>!r.verified&&(score(r.before)>=200)).length;$('statOverdue').textContent=state.records.filter(r=>!r.verified&&r.deadline&&r.deadline<t).length;$('statVerified').textContent=state.records.filter(r=>r.verified).length;
const selected=state.records.filter(r=>(!filter||r.status===filter)&&(!q||[r.unit,r.activity,r.hazard,r.risk,r.existing,r.action,r.responsible,r.notes].some(v=>asText(v).toLocaleLowerCase('tr-TR').includes(q))));$('recordCount').textContent=`(${selected.length}/${state.records.length})`;$('emptyState').hidden=selected.length>0;
$('riskRows').innerHTML=selected.map((r,i)=>{const v=score(r.before),n=score(r.after),b=bandFor(v),a=bandFor(n);return `<tr><td>${i+1}</td><td><b>${esc(r.unit)}</b><div class="cellsub">${esc(r.activity)}</div></td><td><b class="ellips">${esc(r.hazard)}</b><div class="cellsub ellips">${esc(r.risk)}</div></td><td>${fmt(r.before.p)}×${fmt(r.before.f)}×${fmt(r.before.s)} = <b>${fmt(v)}</b><br><span class="risk-tag risk-${b.key}">${b.label}</span></td><td>${fmt(r.after.p)}×${fmt(r.after.f)}×${fmt(r.after.s)} = <b>${fmt(n)}</b><br><span class="risk-tag risk-${a.key}">${esc(r.verified?'Doğrulandı · ':'Hedef · ')}${a.label}</span></td><td class="ellips">${esc(r.action)}</td><td>${esc(r.responsible||'—')}<div class="cellsub">${esc(r.deadline||'—')}</div></td><td>${esc(statusName[r.status])}</td><td><div style="display:flex;gap:4px;flex-wrap:wrap"><button class="btn btn-sm" data-act="edit" data-id="${esc(r.id)}">Düzenle</button><button class="btn btn-sm" data-act="copy" data-id="${esc(r.id)}">Kopyala</button><button class="btn btn-sm btn-danger" data-act="delete" data-id="${esc(r.id)}">Sil</button></div></td></tr>`}).join('');renderBands()}
function baseFilename(){return 'ISG_Fine_Kinney_Risk_Analizi_'+(state.meta.date||today())}
function downloadBlob(blob,name){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),3000)}
function xmlEsc(v){return asText(v).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])).replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g,'')}
function colLetter(num){let res='';for(let n=num;n>0;n=Math.floor((n-1)/26))res=String.fromCharCode(65+(n-1)%26)+res;return res}
function sheetXml(rows,{width=18,autoFilter=true}={}){
const maxCols=Math.max(1,...rows.map(r=>r.length));const cols=Array.from({length:maxCols},(_,i)=>{let w=width;if(i===0)w=17;if(i===5||i===6||i===8||i===9||i===26)w=42;if(i===3||i===4||i===10)w=24;return `<col min="${i+1}" max="${i+1}" width="${w}" customWidth="1"/>`}).join('');
const sheetRows=rows.map((row,i)=>`<row r="${i+1}">${row.map((value,j)=>{const c=colLetter(j+1)+(i+1),st=i===0?' s="1"':'';if(typeof value==='number'&&Number.isFinite(value))return `<c r="${c}"${st}><v>${value}</v></c>`;return `<c r="${c}" t="inlineStr"${st}><is><t xml:space="preserve">${xmlEsc(value)}</t></is></c>`}).join('')}</row>`).join('');
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews><sheetFormatPr defaultRowHeight="16"/><cols>${cols}</cols><sheetData>${sheetRows}</sheetData>${autoFilter?`<autoFilter ref="A1:${colLetter(maxCols)}${Math.max(1,rows.length)}"/>`:''}</worksheet>`
}
const CRC_TABLE=(()=>{const result=[];for(let i=0;i<256;i++){let x=i;for(let j=0;j<8;j++)x=(x&1)?(0xedb88320^(x>>>1)):(x>>>1);result[i]=x>>>0}return result})();
function crc32(data){let crc=0xffffffff;for(const byte of data)crc=CRC_TABLE[(crc^byte)&255]^(crc>>>8);return (crc^0xffffffff)>>>0}
function zipCreate(entries){
const enc=new TextEncoder(),pieces=[],centers=[];let offset=0;
const u16=(view,pos,val)=>view.setUint16(pos,val,true),u32=(view,pos,val)=>view.setUint32(pos,val>>>0,true);
function append(piece){pieces.push(piece);offset+=piece.length}
for(const [filename,text] of entries){const name=enc.encode(filename),data=enc.encode(text),crc=crc32(data),local=new Uint8Array(30+name.length),dv=new DataView(local.buffer),begin=offset;
u32(dv,0,0x04034b50);u16(dv,4,20);u16(dv,6,0x0800);u16(dv,8,0);u16(dv,10,0);u16(dv,12,0);u32(dv,14,crc);u32(dv,18,data.length);u32(dv,22,data.length);u16(dv,26,name.length);u16(dv,28,0);local.set(name,30);append(local);append(data);
const cent=new Uint8Array(46+name.length),cv=new DataView(cent.buffer);u32(cv,0,0x02014b50);u16(cv,4,20);u16(cv,6,20);u16(cv,8,0x0800);u16(cv,10,0);u16(cv,12,0);u16(cv,14,0);u32(cv,16,crc);u32(cv,20,data.length);u32(cv,24,data.length);u16(cv,28,name.length);u16(cv,30,0);u16(cv,32,0);u16(cv,34,0);u16(cv,36,0);u32(cv,38,0);u32(cv,42,begin);cent.set(name,46);centers.push(cent)
}
const start=offset;for(const c of centers)append(c);const end=new Uint8Array(22),e=new DataView(end.buffer);u32(e,0,0x06054b50);u16(e,4,0);u16(e,6,0);u16(e,8,centers.length);u16(e,10,centers.length);u32(e,12,offset-start);u32(e,16,start);u16(e,20,0);append(end);return new Blob(pieces,{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
}
function workbookFiles(rows,summary){const types=`<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>`;
const rels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
const workbook=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Risk Kayıtları" sheetId="1" r:id="rId1"/><sheet name="Rapor Bilgileri" sheetId="2" r:id="rId2"/></sheets></workbook>`;
const wbRels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>`;
const styles=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="10"/><name val="Calibri"/></font><font><b/><sz val="10"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF0369A1"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
return [['[Content_Types].xml',types],['_rels/.rels',rels],['xl/workbook.xml',workbook],['xl/_rels/workbook.xml.rels',wbRels],['xl/styles.xml',styles],['xl/worksheets/sheet1.xml',sheetXml(rows)],['xl/worksheets/sheet2.xml',sheetXml(summary,{width:36,autoFilter:false})]]}
const EXCEL_COLUMNS=['Kayıt ID','Bölüm','Faaliyet','Tehlike','Olası Zarar / Risk','Etkilenenler','Mevcut Önlemler','Önce Olasılık','Önce Frekans','Önce Şiddet','Önce Risk Puanı','Önce Risk Seviyesi','Planlanan Tedbir','Sonra Olasılık','Sonra Frekans','Sonra Şiddet','Sonra Risk Puanı','Sonra Risk Seviyesi','Sorumlu','Hedef Tarih','Durum','Doğrulama','Notlar','Kayıt Tarihi'];
function recordToExcelRow(r){return [r.id,r.unit,r.activity,r.hazard,r.risk,r.affected,r.existing,r.before.p,r.before.f,r.before.s,score(r.before),bandFor(score(r.before)).label,r.action,r.after.p,r.after.f,r.after.s,score(r.after),bandFor(score(r.after)).label,r.responsible,r.deadline,statusName[r.status],r.verified?'Evet':'Hayır',r.notes,r.created]}
function exportXlsx(){const m=state.meta,rows=[EXCEL_COLUMNS,...state.records.map(recordToExcelRow)],summary=[['Alan','Değer'],['Yöntem','Fine-Kinney'],['İşyeri',m.company],['Tesis / Birim',m.facility],['Hazırlayan',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon',m.revision],['Toplam Risk',state.records.length],['Yüksek ve Çok Yüksek / Açık',state.records.filter(r=>!r.verified&&score(r.before)>=200).length],['Doğrulanan Risk',state.records.filter(r=>r.verified).length],['Risk Formülü','Olasılık × Frekans × Şiddet'],['Not','Risk eşikleri örnek uygulamadır; kurumun onaylı metodolojisi geçerlidir.']];downloadBlob(zipCreate(workbookFiles(rows,summary)),baseFilename()+'.xlsx');toast('Excel dışarı aktarıldı: '+state.records.length+' kayıt.')}
async function unzipEntries(arrayBuffer){const bytes=new Uint8Array(arrayBuffer),view=new DataView(arrayBuffer);let end=-1;for(let i=bytes.length-22;i>=Math.max(0,bytes.length-65557);i--)if(view.getUint32(i,true)===0x06054b50){end=i;break}if(end<0)throw new Error('Geçerli Excel ZIP yapısı bulunamadı.');const count=view.getUint16(end+10,true),base=view.getUint32(end+16,true),decoder=new TextDecoder(),entries={};let cursor=base;
for(let i=0;i<count;i++){if(view.getUint32(cursor,true)!==0x02014b50)throw new Error('Excel dosyası merkezi dizini okunamadı.');const method=view.getUint16(cursor+10,true),compressed=view.getUint32(cursor+20,true),nameLength=view.getUint16(cursor+28,true),extra=view.getUint16(cursor+30,true),comment=view.getUint16(cursor+32,true),localOffset=view.getUint32(cursor+42,true),name=decoder.decode(bytes.subarray(cursor+46,cursor+46+nameLength));if(view.getUint32(localOffset,true)!==0x04034b50)throw new Error('Excel dosyası veri bölümü bozuk.');const start=localOffset+30+view.getUint16(localOffset+26,true)+view.getUint16(localOffset+28,true);const content=bytes.slice(start,start+compressed);if(method===0)entries[name]=decoder.decode(content);else if(method===8){if(typeof DecompressionStream!=='function')throw new Error('Bu tarayıcı sıkıştırılmış Excel içeri aktarmayı desteklemiyor. Güncel bir Chrome veya Edge tarayıcısı deneyin.');try{const stream=new Blob([content]).stream().pipeThrough(new DecompressionStream('deflate-raw'));entries[name]=decoder.decode(await new Response(stream).arrayBuffer())}catch(e){throw new Error('Excel sıkıştırması açılamadı: '+e.message)}}else throw new Error('Excel dosyasında desteklenmeyen sıkıştırma türü.');cursor+=46+nameLength+extra+comment;
}return entries}
function parseXml(source){const doc=new DOMParser().parseFromString(source,'application/xml');if(doc.querySelector('parsererror'))throw new Error('Excel XML içeriği okunamadı.');return doc}
function parseXlsxSheet(source,shared=[]){if(!source)return [];const doc=parseXml(source),rows=[];for(const row of doc.getElementsByTagNameNS('*','row')){const cells=[];for(const c of row.getElementsByTagNameNS('*','c')){const ref=c.getAttribute('r')||'';let index=0;for(const letter of (ref.match(/^[A-Z]+/)||[''])[0])index=index*26+letter.charCodeAt(0)-64;index=Math.max(0,index-1);const type=c.getAttribute('t');let value='';if(type==='inlineStr')value=Array.from(c.getElementsByTagNameNS('*','t')).map(n=>n.textContent).join('');else{value=c.getElementsByTagNameNS('*','v')[0]?.textContent||'';if(type==='s')value=shared[Number(value)]||''}cells[index]=value}rows.push(cells)}return rows}
function normalizeDate(value){const str=asText(value);if(/^\d{4}-\d{2}-\d{2}$/.test(str))return str;if(/^\d{2}\.\d{2}\.\d{4}$/.test(str)){const a=str.split('.');return a[2]+'-'+a[1]+'-'+a[0]}const n=Number(value);if(Number.isFinite(n)&&n>=1&&n<100000)return new Date(Date.UTC(1899,11,30)+Math.floor(n)*86400000).toISOString().slice(0,10);return ''}
function fromExcelRow(row,idx){const read=k=>row[idx.get(k)]??'',statusRaw=read('Durum');const status=Object.keys(statusName).find(k=>statusName[k]===statusRaw)||(Object.hasOwn(statusName,statusRaw)?statusRaw:'open');return recordNormalize({id:read('Kayıt ID'),method:'fine-kinney',unit:read('Bölüm'),activity:read('Faaliyet'),hazard:read('Tehlike'),risk:read('Olası Zarar / Risk'),affected:read('Etkilenenler'),existing:read('Mevcut Önlemler'),action:read('Planlanan Tedbir'),responsible:read('Sorumlu'),deadline:normalizeDate(read('Hedef Tarih')),status,verified:read('Doğrulama')==='Evet',before:{p:read('Önce Olasılık'),f:read('Önce Frekans'),s:read('Önce Şiddet')},after:{p:read('Sonra Olasılık'),f:read('Sonra Frekans'),s:read('Sonra Şiddet')},notes:read('Notlar'),created:normalizeDate(read('Kayıt Tarihi'))||today()})}
async function importData(file){if(!file.name.toLowerCase().endsWith('.xlsx'))throw new Error('Yalnızca .xlsx dosyası seçin.');const entries=await unzipEntries(await file.arrayBuffer());const strings=[];
if(entries['xl/sharedStrings.xml']){const doc=parseXml(entries['xl/sharedStrings.xml']);for(const item of doc.getElementsByTagNameNS('*','si'))strings.push(Array.from(item.getElementsByTagNameNS('*','t')).map(t=>t.textContent).join(''))}
const rows=parseXlsxSheet(entries['xl/worksheets/sheet1.xml'],strings);if(!rows.length)throw new Error('Excel risk kayıtları sayfası bulunamadı.');const headers=new Map(rows[0].map((v,i)=>[asText(v).trim(),i]));const required=['Kayıt ID','Tehlike','Bölüm','Faaliyet','Önce Olasılık','Önce Frekans','Önce Şiddet','Sonra Olasılık','Sonra Frekans','Sonra Şiddet'];if(!required.every(k=>headers.has(k)))throw new Error('Excel Fine-Kinney şablonuyla uyumsuz (O/F/Ş sütunları eksik).');
const dataRows=rows.slice(1).filter(r=>r.some(v=>asText(v).trim()));const records=dataRows.map(r=>fromExcelRow(r,headers)).filter(Boolean);if(dataRows.length!==records.length)throw new Error('Excel içinde '+(dataRows.length-records.length)+' kayıt geçersiz: Olasılık/Frekans/Şiddet ölçeklerini kontrol edin.');const info=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings),infoMap=new Map(info.slice(1).map(r=>[r[0],r[1]]));const meta=initMeta();for(const [key,label] of Object.entries({company:'İşyeri',facility:'Tesis / Birim',prepared:'Hazırlayan',date:'Değerlendirme Tarihi',revision:'Revizyon'}))if(infoMap.has(label))meta[key]=asText(infoMap.get(label));return {meta,records}}
async function performImport(file){if(!file)return;try{const incoming=await importData(file);if(!incoming.records.length)throw new Error('Dosyada risk kaydı bulunmuyor.');if(state.records.length&&!confirm('Mevcut '+state.records.length+' riskin yerine '+incoming.records.length+' kayıt yüklenecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;state=incoming;renderMeta();clearForm();renderDashboard();setView('distribution');toast(incoming.records.length+' risk kaydı Excel’den yüklendi.')}catch(e){console.error(e);alert('Excel içeri aktarma başarısız: '+e.message)}finally{$('importFile').value=''}}
/* PDF/Yazdır: Ekrandaki iki Fine-Kinney grafiğinin SVG karşılığı; renkler PDF'de vektördür. */
function renderPrintCharts(before,after){
const counts=BANDS.map(()=>0);
for(const r of state.records){const b=bandFor(score(r.before));if(b)counts[BANDS.indexOf(b)]++}
const biggest=Math.max(1,...counts),bNow=bandFor(score(before)),bTarget=bandFor(score(after));
const colors={low:'#73cf93',possible:'#eed572',important:'#f7b359',high:'#f28b59',critical:'#e97575'};
const svgBase=(kind)=>{
const isCount=kind==='distribution';
const rows=BANDS.map((b,i)=>{
const y=8+i*28,filled=isCount?Math.max(0,(counts[i]/biggest)*244):244;
const haveBefore=!isCount&&bNow?.key===b.key,haveTarget=!isCount&&bTarget?.key===b.key;
const marker=haveBefore?`<rect x="146" y="${y+1}" width="244" height="22" rx="2" fill="none" stroke="#125ba9" stroke-width="3"/>`:'';
const target=haveTarget?`<rect x="150" y="${y+4}" width="236" height="16" rx="1" fill="none" stroke="#8651b4" stroke-width="2" stroke-dasharray="5 3"/>`:'';
const label=isCount?counts[i]+' kayıt':b.short;
return `<text x="6" y="${y+16}" font-family="Arial,sans-serif" font-size="11" font-weight="600" fill="#25364b">${esc(b.label)}</text>`+
`<rect x="146" y="${y+1}" width="244" height="22" rx="2" fill="#eff3f6" stroke="#cbd5e1" stroke-width="0.8"/>`+
(filled?`<rect x="147" y="${y+2}" width="${Math.max(0,filled-2)}" height="20" rx="1" fill="${colors[b.key]}"/>`:'')+
marker+target+
`<text x="404" y="${y+16}" font-family="Arial,sans-serif" font-size="11" font-weight="700" fill="#334155">${esc(label)}</text>`;
}).join('');
return `<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${isCount?'Genel risk dağılımı':'Mevcut ve hedef risk düzeyleri'}" viewBox="0 0 520 150" preserveAspectRatio="xMidYMid meet">${rows}</svg>`;
};
const current=factorsValid(before)?`${fmt(score(before))} · ${esc(bNow.label)}`:'Henüz mevcut risk seçilmedi';
const target=factorsValid(after)?`${fmt(score(after))} · ${esc(bTarget.label)}`:'Henüz hedef risk seçilmedi';
return `<section class="print-section print-chart-section"><h3>Fine-Kinney · Risk Grafikleri</h3><div class="print-chart-layout">`+
`<div class="print-chart-box"><h4>Tekil Risk · Mevcut / Hedef</h4>${svgBase('single')}<div class="print-chart-key"><span><i class="mark"></i>Mevcut</span><span><i class="mark target"></i>Hedef</span></div><div class="print-chart-foot">Mevcut: <b>${current}</b> → Hedef: <b>${target}</b></div></div>`+
`<div class="print-chart-box"><h4>Genel Risk Dağılımı · Mevcut Risk Kayıtları</h4>${svgBase('distribution')}<div class="print-chart-foot">Toplam <b>${state.records.length}</b> risk kaydı. Çubuk uzunluğu ve sayı, her seviyedeki kayıt adedini gösterir; ortalama değildir.</div></div>`+
`</div></section>`;
}
/* Risk UI baskı logo çözümü:
Ekrandaki etkin logoyu (aynı klasördeki logo.jpg veya kullanıcı yüklemesi)
doğrudan PDF başlığına taşır. Yüklenmiş yerel logo mümkünse
veri-URL'sine dönüştürülür; böylece yazdırmada görünür ve tekrar eden
sayfa başlıklarında kaynak yükleme sorunları yaşanmaz. */
function getPrintableLogoSource(){
const live=$('brandLogo');
if(!live || live.hidden || !live.complete || live.naturalWidth<=0)return '';
// Kullanıcının yerel olarak seçtiği logo zaten HTML içine gömülebilir durumda.
if(brand.logo && /^data:image\//i.test(brand.logo))return brand.logo;
// logo.jpg için tarayıcı güvenlik izinleri elveriyorsa görseli rapora göm.
try{
const max=800,ratio=Math.min(1,max/Math.max(live.naturalWidth,live.naturalHeight));
const canvas=document.createElement('canvas');
canvas.width=Math.max(1,Math.round(live.naturalWidth*ratio));
canvas.height=Math.max(1,Math.round(live.naturalHeight*ratio));
const context=canvas.getContext('2d');
if(context){context.drawImage(live,0,0,canvas.width,canvas.height);return canvas.toDataURL('image/png')}
}catch(err){console.info('Logo veri URL olarak eklenemedi, orijinal logo kullanılıyor.',err)}
// file:// güvenlik kısıtlamaları nedeniyle canvas kullanılamazsa
// ekranda başarıyla açılmış olan aynı resmin mutlak adresini kullan.
return live.currentSrc||live.src||'';
}
function syncPrintableLogo(){
const target=$('printBrandLogo'),fallback=$('printLogoFallback');
if(!target||!fallback)return;
const url=getPrintableLogoSource();
if(url){
target.onload=()=>{target.hidden=false;fallback.hidden=true};
target.onerror=()=>{target.hidden=true;fallback.hidden=false};
if(target.src!==url)target.src=url;
target.hidden=false;fallback.hidden=true;
}else{
target.removeAttribute('src');target.hidden=true;fallback.hidden=false;
}
}
function renderPrint(){const m=state.meta;
// Aynı baskı başlığını tekrar kullanmak, beforeprint sırasında logo yüklemesini sıfırlamaz.
const host=$('printHeader');
if(!host.querySelector('#printBrandLogo')){
host.innerHTML=`<header class="print-header"><div class="print-logo-box"><img id="printBrandLogo" alt="Kurum logosu" hidden><b id="printLogoFallback">LOGO</b></div><div><h1></h1><p></p></div><div class="print-org"></div></header>`;
}
host.querySelector('.print-header h1').textContent=brand.title;
host.querySelector('.print-header p').textContent=brand.subtitle;
host.querySelector('.print-org').textContent=brand.org;
syncPrintableLogo();
const metaFields=[['İşyeri / Firma',m.company],['Tesis / Birim',m.facility],['Hazırlayan / Ekip',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon / Belge No.',m.revision]],pField=([k,v])=>`<div class="print-field"><b>${esc(k)}</b>${esc(v||'—')}</div>`;
const currentForm=editingId||['unit','activity','hazard','risk','action'].some(id=>$(id).value.trim())||factorsValid(working.before)||factorsValid(working.after);
const record=state.records.length&&!currentForm?state.records[state.records.length-1]:null;
const fieldVal=k=>record?record[k]:$(k).value;
const before=record?record.before:working.before,after=record?record.after:working.after, verified=record?record.verified:$('verified').checked;
const fields=[['Bölüm / Alan',fieldVal('unit')],['Faaliyet / İş Adımı',fieldVal('activity')],['Etkilenebilecek Kişiler',fieldVal('affected')],['Tehlike Kaynağı',fieldVal('hazard')],['Risk / Olası Zarar',fieldVal('risk')],['Mevcut Önlemler',fieldVal('existing')],['Alınacak Tedbir',fieldVal('action')],['Sorumlu Kişi',fieldVal('responsible')],['Termin',fieldVal('deadline')],['Aksiyon Durumu',record?statusName[record.status]:statusName[$('status').value]||''],['Ek Açıklamalar',fieldVal('notes')],['Kalan Risk Doğrulandı',verified?'Evet':'Hayır']];
const fval=(o,k)=>o[k]==null?'—':fmt(o[k]),showScore=o=>factorsValid(o)?fmt(score(o))+' · '+bandFor(score(o)).label:'—';
const rows=state.records.map((r,i)=>`<tr><td>${i+1}</td><td>${esc(r.unit)}<br>${esc(r.activity)}</td><td><b>Tehlike:</b> ${esc(r.hazard)}<br><b>Risk:</b> ${esc(r.risk)}<br><b>Mevcut:</b> ${esc(r.existing||'—')}</td><td>${fval(r.before,'p')}×${fval(r.before,'f')}×${fval(r.before,'s')}<br><b>${showScore(r.before)}</b></td><td>${fval(r.after,'p')}×${fval(r.after,'f')}×${fval(r.after,'s')}<br><b>${showScore(r.after)}</b></td><td>${esc(r.action)}<br>${esc(r.responsible||'—')} · ${esc(r.deadline||'—')}<br>${esc(statusName[r.status])}${r.verified?' · Doğrulandı':''}</td></tr>`).join('')||'<tr><td colspan="6">Kayıt yok.</td></tr>';
$('printReport').innerHTML=`<section class="print-section print-no-break"><h3>01 · İşyeri ve Değerlendirme Bilgileri</h3><div class="print-grid">${metaFields.map(pField).join('')}</div></section><section class="print-section print-no-break"><h3>02 · Fine-Kinney Risk Değerlendirmesi</h3><div class="print-summary"><div>MEVCUT RİSK<b>${fmt(score(before))}</b>${factorsValid(before)?esc(bandFor(score(before)).label):'—'}</div><div>HEDEF / KALAN<b>${fmt(score(after))}</b>${factorsValid(after)?esc(bandFor(score(after)).label):'—'}</div><div>OLASILIK (O)<b>${fval(before,'p')} → ${fval(after,'p')}</b></div><div>FREKANS (F)<b>${fval(before,'f')} → ${fval(after,'f')}</b></div><div>ŞİDDET (Ş)<b>${fval(before,'s')} → ${fval(after,'s')}</b></div></div><div class="print-grid">${fields.map(pField).join('')}</div><div class="print-note">Hedef risk, önlem uygulanıp etkinliği doğrulanana kadar gerçekleşmiş kalan risk değildir.</div></section>${renderPrintCharts(before,after)}<section class="print-section"><h3>03 · Risk Kayıtları ve Aksiyon Takibi (${state.records.length})</h3><table class="print-table"><thead><tr><th style="width:4%">No.</th><th style="width:16%">Bölüm / Faaliyet</th><th style="width:25%">Tehlike / Risk / Kontrol</th><th style="width:14%">Önce O×Fת</th><th style="width:14%">Sonra O×Fת</th><th style="width:27%">Tedbir / Sorumlu / Durum</th></tr></thead><tbody>${rows}</tbody></table></section><div class="print-note">Fine-Kinney sınıflandırması örnek yöntem aralıklarına göre uygulanmıştır; işyeri risk değerlendirme ekibince doğrulanmalıdır.</div>`;
}
function printWithLogoReady(){
renderPrint();
const logo=$('printBrandLogo');
if(!logo||logo.hidden||(logo.complete&&logo.naturalWidth>0)){
window.print();return;
}
let started=false;
const openPrint=()=>{
if(started)return;
started=true;
logo.removeEventListener('load',openPrint);
logo.removeEventListener('error',openPrint);
window.print();
};
logo.addEventListener('load',openPrint,{once:true});
logo.addEventListener('error',openPrint,{once:true});
setTimeout(openPrint,1500);
}
function bind(){document.querySelectorAll('[data-meta]').forEach(el=>el.addEventListener('input',()=>state.meta[el.dataset.meta]=el.value));
for(const id of ['beforeFields','afterFields'])$(id).addEventListener('change',e=>{const el=e.target;if(!el.matches('select[data-factor]'))return;working[el.dataset.side][el.dataset.factor]=el.value===''?null:Number(el.value);renderScore()});
$('status').addEventListener('change',()=>{if($('status').value!=='done')$('verified').checked=false;renderScore()});$('verified').addEventListener('change',()=>{if($('verified').checked&&$('status').value!=='done'){$('verified').checked=false;alert('Doğrulama için aksiyon durumu “Tamamlandı” seçilmelidir.')}renderScore()});
$('modeSingle').addEventListener('click',()=>setView('single'));$('modeDistribution').addEventListener('click',()=>setView('distribution'));
$('riskForm').addEventListener('submit',e=>{e.preventDefault();if(!$('riskForm').checkValidity()){$('riskForm').reportValidity();return}if(!factorsValid(working.before)||!factorsValid(working.after)){alert('Mevcut ve hedef risk için Olasılık, Frekans ve Şiddet seçimlerinin tamamını yapın.');return}if($('verified').checked&&$('status').value!=='done'){alert('Kalan risk doğrulaması yalnızca tamamlanan aksiyonlarda seçilebilir.');return}const r=valuesFromForm();if(!r){alert('Risk faktörlerinin tamamını geçerli Fine-Kinney değerlerinden seçin.');return}const wasEditing=Boolean(editingId);if(wasEditing){const ix=state.records.findIndex(x=>x.id===editingId);if(ix<0)return;state.records[ix]=r}else state.records.push(r);clearForm();renderDashboard();setView('distribution');toast(wasEditing?'Risk kaydı güncellendi.':'Risk kaydı eklendi.')});
$('newBtn').addEventListener('click',()=>{clearForm();setView('single')});$('cancelBtn').addEventListener('click',()=>{clearForm();setView('single')});
$('riskRows').addEventListener('click',e=>{const b=e.target.closest('button[data-act][data-id]');if(!b)return;const {act,id}=b.dataset;if(act==='edit')startEdit(id);if(act==='copy')copyRecord(id);if(act==='delete')deleteRecord(id)});
for(const id of ['searchInput','statusFilter'])$(id).addEventListener('input',renderDashboard);
$('clearBtn').addEventListener('click',()=>{if(!state.records.length)return toast('Silinecek kayıt bulunmuyor.');if(!confirm('Tüm '+state.records.length+' risk kaydı silinecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;state.records=[];clearForm();renderDashboard();toast('Tüm risk kayıtları silindi.')});
$('importBtn').addEventListener('click',()=>$('importFile').click());$('importFile').addEventListener('change',e=>performImport(e.target.files?.[0]));$('exportBtn').addEventListener('click',exportXlsx);$('pdfBtn').addEventListener('click',printWithLogoReady);window.addEventListener('beforeprint',renderPrint)
}
function resetSessionOnOpen(){resetData();$('riskForm').reset();$('searchInput').value='';$('statusFilter').value='';$('importFile').value='';document.querySelectorAll('[data-meta]').forEach(el=>el.value='');renderMeta();clearForm();renderDashboard()}
initializeBrand();bind();resetSessionOnOpen();window.addEventListener('pageshow',e=>{if(e.persisted)resetSessionOnOpen()});
})();
</script>
</body></html>