İSG Risk Analizi – FMEA (Hata Türleri ve Etkileri Analizi)

İş sağlığı ve güvenliğinde risklerin önceden belirlenmesi, iş kazalarının ve üretim kaynaklı tehlikelerin önlenmesi açısından büyük önem taşır. Özellikle fabrikalarda, atölyelerde, makine ve ekipmanlarla yürütülen faaliyetlerde meydana gelebilecek hataların sistematik olarak değerlendirilmesi gerekir.

İSG Risk Analizi – FMEA masaüstü HTML uygulaması, makine, ekipman ve üretim süreçlerinde ortaya çıkabilecek hata türlerini, bu hataların nedenlerini, olası etkilerini ve mevcut kontrol önlemlerini değerlendirmek amacıyla hazırlanmış dinamik bir risk analizi aracıdır.

Uygulama; risk puanlarını otomatik hesaplar, hata türlerini önceliklendirir, alınacak önlemlerin takibini sağlar ve değerlendirme sonuçlarının Excel veya PDF olarak raporlanmasına imkân tanır.

FMEA Nedir?

FMEA, İngilizce Failure Mode and Effects Analysis ifadesinin kısaltmasıdır. Türkçede Hata Türleri ve Etkileri Analizi olarak adlandırılır.

Bu yöntem; bir sistem, makine, ekipman veya süreçte hangi hataların meydana gelebileceğini, hataların nasıl sonuçlar doğurabileceğini, hangi nedenlerle oluşabileceğini ve mevcut kontrollerle ne ölçüde tespit edilebileceğini inceler.

FMEA'nın temel amacı, hatalar gerçekleşmeden önce riskleri ortaya koymak ve iyileştirme faaliyetlerine öncelik vermektir.

Risk Öncelik Sayısı (RÖS) Nedir?

FMEA yönteminde riskin sayısal değerlendirilmesi için üç faktör kullanılır:

Şiddet (Ş): Hatanın meydana gelmesi durumunda ortaya çıkabilecek sonucun ciddiyetini ifade eder.

Oluşma (O): Hatanın meydana gelme olasılığını gösterir.

Tespit (T): Hatanın etkisi ortaya çıkmadan önce mevcut kontrollerle fark edilememe derecesini ifade eder. Tespit puanının yüksek olması, hatanın tespit edilmesinin zor olduğunu gösterir.

Her faktör 1 ile 10 arasında puanlanır.

RÖS = Şiddet × Oluşma × Tespit

Örneğin, bir makinede oluşabilecek hata için Şiddet 8, Oluşma 6 ve Tespit 7 olarak belirlendiğinde:

RÖS = 8 × 6 × 7 = 336

Bu sonuç, hata türünün iyileştirme açısından öncelikli olarak değerlendirilmesi gerektiğini gösterebilir.

Ancak FMEA'da bütün işletmeler için geçerli tek bir RÖS kabul sınırı yoktur. Özellikle yüksek şiddetli hata türleri, toplam RÖS düşük olsa bile ayrıca değerlendirilmelidir.

FMEA HTML Uygulaması Ne İşe Yarar?

  • Uygulama, FMEA değerlendirmesinin farklı aşamalarını tek bir formda birleştirir.
  • İşletmedeki süreçler ve ekipmanlar için hata kayıtları oluşturulabilir. Her hata türünün etkisi, nedeni, mevcut önleme ve tespit kontrolleri ayrı ayrı kaydedilebilir.
  • Mevcut risk ile iyileştirme sonrası hedeflenen risk hesaplanarak karşılaştırılabilir. Böylece önlemlerin risk üzerindeki beklenen etkisi görülebilir.
  • Uygulama ayrıca sorumlu kişilerin, hedef tamamlanma tarihlerinin ve aksiyon durumlarının takip edilmesini sağlar.
  • Risk grafikleri sayesinde tek bir hata türünün değerlendirmesi ve kayıtlı hataların genel öncelik dağılımı görsel olarak incelenebilir.

FMEA HTML Formu Nasıl Doldurulur?

1. İşyeri ve Değerlendirme Bilgileri

İlk bölümde değerlendirmenin hangi işletme ve üretim alanı için yapıldığı belirtilir.

İşyeri / Firma alanına kuruluşun adı, Tesis / Üretim Alanı bölümüne değerlendirmenin yapılacağı bölüm, Hazırlayan / Ekip alanına çalışmayı gerçekleştiren kişiler yazılır.

Değerlendirme tarihi ve revizyon/belge numarası da girilerek çalışmanın hangi tarihe ve sürüme ait olduğu kayıt altına alınır.

2. Süreç ve Hata Türünün Tanımlanması

Bu bölümde değerlendirilecek faaliyet ve hata ayrıntılı şekilde tanımlanır.

Bölüm / Süreç: Örneğin Pres Atölyesi.

Makine / Ekipman: Örneğin Hidrolik Pres Makinesi.

Faaliyet / Fonksiyon: Örneğin Metal parçaların preslenmesi.

Potansiyel Hata Türü: Makinenin beklenmedik şekilde çalışması.

Hatanın Potansiyel Etkisi: Operatörün hareketli parçalar arasında sıkışması ve ciddi yaralanması.

Hatanın Potansiyel Nedeni: Kumanda sisteminde veya güvenlik sensöründe oluşabilecek arıza.

Mevcut Önleme Kontrolleri: Bakım faaliyetleri, koruyucu sistemler ve çalışma talimatları gibi hatanın oluşmasını engellemeye yönelik tedbirler.

Mevcut Tespit Kontrolleri: Sensör testleri, otomatik arıza uyarıları ve periyodik kontroller gibi hatayı tespit etmeye yönelik yöntemler.

Bu alanlar doldurulurken genel ifadeler yerine somut, anlaşılır ve sahada doğrulanabilir bilgiler kullanılmalıdır.

3. Mevcut FMEA Risk Puanının Hesaplanması

Hata tanımlandıktan sonra Mevcut Değerlendirme bölümünden Şiddet, Oluşma ve Tespit puanları seçilir.

Uygulama seçilen değerleri otomatik çarparak mevcut RÖS sonucunu hesaplar.

Sonuç, risk öncelik grubuyla birlikte gösterilir. Kullanıcıların ayrıca hesap makinesi kullanmasına gerek kalmaz.

4. İyileştirme Önlemlerinin Belirlenmesi

İyileştirme ve Aksiyon Takibi bölümünde hatanın önlenmesi veya daha erken tespit edilmesi için yapılması gereken çalışmalar belirlenir.

Önerilen / Alınacak Önlem alanında uygulanacak tedbir açıklanır. Örneğin pres makinesine ilave güvenlik kilidi ve arıza algılama sistemi kurulması planlanabilir.

Sorumlu Kişi / Birim bölümüne tedbiri gerçekleştirecek kişi veya birim yazılır. Hedef Tamamlanma Tarihi alanında ise önlemin ne zaman tamamlanması gerektiği belirtilir.

Aksiyon durumu Açık, Devam Ediyor veya Tamamlandı olarak takip edilebilir.

5. Hedef / Kalan Riskin Değerlendirilmesi

Planlanan önlemlerin uygulanmasıyla riskin hangi seviyeye düşmesinin beklendiği Hedef / Kalan Risk alanında değerlendirilir.

Bu bölümde Şiddet, Oluşma ve Tespit değerleri yeniden seçilerek hedef RÖS hesaplanır. Hedef değerlendirme isteğe bağlıdır; ancak kullanılacaksa üç puanın da girilmesi gerekir.

Örneğin mevcut RÖS değeri 336 olan bir hata için önlemler sonrasında Şiddet 8, Oluşma 2 ve Tespit 2 olarak öngörülebilir.

Hedef RÖS = 8 × 2 × 2 = 32

Bu örnekte hatanın olası sonucu aynı ciddiyette kalmasına rağmen, oluşma ihtimalinin azaltılması ve tespit yeteneğinin geliştirilmesi sayesinde hedef RÖS düşmektedir.

Hedef puanların belirlenmiş olması, önlemlerin gerçekten uygulandığı anlamına gelmez.

6. Önlem Etkinliğinin Doğrulanması

Planlanan çalışmalar tamamlandıktan sonra uygulanan kontrollerin etkinliği değerlendirilir.

Doğrulama Kanıtı / Ek Açıklama alanına test sonuçları, kontrol tutanakları, ölçüm bilgileri veya ilgili kanıtların açıklamaları yazılabilir.

Aksiyon durumu Tamamlandı olarak seçildiğinde ve hedef puanlar eksiksiz girildiğinde, önlemin uygulandığı ve kalan riskin doğrulandığı kutucuk işaretlenebilir.

Bu işlem yalnızca gerçek doğrulama sonrasında yapılmalıdır.

7. FMEA Kaydının Oluşturulması ve Takibi

Bilgiler tamamlandıktan sonra FMEA Kaydı Ekle butonuna basılır.

Kaydedilen hata türü, FMEA Hata Kayıtları ve Aksiyon Takibi tablosunda görüntülenir.

Bu bölümden mevcut kayıtlar düzenlenebilir, benzer değerlendirmeler için kopyalanabilir veya gerektiğinde silinebilir.

Arama ve durum filtreleri kullanılarak belirli süreçler veya tamamlanmamış aksiyonlar kolayca bulunabilir.

Risk Grafiklerinin Kullanımı

Uygulamada iki farklı grafik görünümü bulunmaktadır.

Tekil FMEA, değerlendirmesi yapılan hata türünün mevcut RÖS değerini ve varsa hedef RÖS değerini karşılaştırır.

Genel RÖS Dağılımı, kayıtlı hata türlerinin farklı öncelik gruplarına göre dağılımını gösterir.

Uygulamada İzleme (1–39), Dikkat (40–99), Öncelikli (100–199) ve Kritik (200–1000) şeklinde örnek gruplar bulunmaktadır. Bu sınırlar evrensel FMEA standartları olarak kabul edilmemeli; işletmenin değerlendirme kriterlerine göre yorumlanmalıdır.

Excel İçeri ve Dışarı Aktarma

Uygulamanın önemli özelliklerinden biri, risk kayıtlarının Excel dosyalarıyla yönetilebilmesidir.

Excel İçeri Aktar butonuyla uygulamanın beklediği şablona uygun .xlsx dosyaları yüklenebilir. İçe aktarılan hata türleri, değerlendirme puanları, işyeri bilgileri ve aksiyon kayıtları formda görüntülenir.

Excel Dışarı Aktar butonuyla oluşturulan kayıtlar Excel dosyasına aktarılabilir.

Bu özellik, FMEA çalışmalarının arşivlenmesini, kayıtların düzenlenmesini ve daha sonra yeniden uygulamaya yüklenmesini kolaylaştırır.

Örnek Excel dosyasındaki dikey görünüm sayfası, her kaydın alanlarını alt alta okuyabilmek için hazırlanmıştır. HTML uygulamasına veri aktarımı ise uyumlu kayıt ve rapor bilgileri sayfaları üzerinden gerçekleştirilir.

PDF Raporlama ve Yazdırma

PDF / Yazdır butonuyla FMEA değerlendirmesi raporlanabilir.

Oluşturulan raporda işyeri bilgileri, risk grafikleri, hata kayıtları, mevcut ve hedef RÖS sonuçları ile aksiyon detayları yer alır.

Kurum logosu ve başlık bilgileri de raporun üst kısmında gösterilir. Çok sayfalı raporlarda başlık alanı tekrarlanır.

Böylece değerlendirme sonuçları dijital olarak saklanabilir, yazdırılabilir veya ilgili kişilerle paylaşılabilir.

Kurumsal Başlık ve Logo Kullanımı

HTML uygulamasının başlık alanı kuruma göre düzenlenebilir.

HTML dosyasıyla aynı klasörde bulunan logo.jpg dosyası otomatik olarak kullanılabilir. Logo alanına art arda beş kez tıklanarak farklı bir logo seçilebilir.

Ana başlık, alt başlık ve sağ taraftaki kurum adı da beş tıklama yöntemiyle değiştirilebilir.

Bu düzenlemeler, uygulamanın farklı kuruluşlarda kullanılabilmesini sağlar.

Çevrimdışı Çalışma ve Veri Güvenliği

Uygulama, bilgisayara kurulum gerektirmeyen bağımsız bir HTML dosyasıdır. Chrome veya Edge gibi bir tarayıcıyla internet bağlantısı olmadan açılabilir.

Form bilgileri ve FMEA kayıtları, uygulama açıkken işlenir. Sayfa yenilendiğinde veya uygulama yeniden açıldığında risk kayıtları boş başlar.

Bu nedenle, çalışmayı kapatmadan veya sayfayı yenilemeden önce mutlaka Excel Dışarı Aktar işlemiyle kayıtların yedeği alınmalıdır.

Kurumsal logo ve başlık tercihleri ise tarayıcının yerel kayıt alanında saklanabilir.

Kimler Kullanabilir?

FMEA HTML uygulaması; iş güvenliği uzmanları, üretim mühendisleri, bakım ekipleri, kalite uzmanları, proses mühendisleri ve risk değerlendirme ekipleri tarafından kullanılabilir.

Başta fabrikalar, atölyeler, makine parkurları ve üretim tesisleri olmak üzere, hata türlerinin sistematik olarak incelenmesi gereken süreçlerde yardımcı bir değerlendirme aracı olarak kullanılabilir.

Sonuç

İSG Risk Analizi – FMEA masaüstü HTML uygulaması, olası hata türlerinin tanımlanmasını, etkilerinin değerlendirilmesini, risklerin önceliklendirilmesini ve alınacak önlemlerin takip edilmesini tek bir dijital ortamda birleştirir.

Otomatik RÖS hesaplama, görsel grafikler, Excel veri aktarımı ve PDF raporlama özellikleri sayesinde risk değerlendirme çalışmalarının daha düzenli, izlenebilir ve raporlanabilir şekilde yürütülmesini destekler.

Temel amacı, hatalar ortaya çıkmadan önce tehlikeleri belirlemek ve önleyici faaliyetlerin planlanmasına yardımcı olmaktır.

Önemli not: Uygulama, yetkin kişiler tarafından yürütülen saha incelemelerinin ve mevzuata uygun risk değerlendirmelerinin yerine geçmez. FMEA sonuçları, işletmenin faaliyetleri, teknik şartları ve kabul edilmiş değerlendirme kriterleriyle birlikte ele alınmalıdır.

Bilgisayarda kullanmak için;
Bir Not defteri açarak, aşağıdaki (Mavi satırlar) html kodu içine kaydedin. Not defteri uzantısını .txt yerine ".html" olarak değiştirip kaydedin. Ör: "İSG Risk Analizi – FMEA.html" sayfayı kapatıp tekrar açtığınızda, web browser üzerinden sayfanız açılacaktır. Tüm kurum ve işletmelerde kullanabilecek bir formdur.

Sol tarafta görünen logo için, html dosyanız nerede ise, masaüstü veya oluşturulan bir klasör. Logonuzda aynı yerde olursa forma yansır. Logo ismi "logo.jpg" olmalı. Çift tıklayarak açtığınızda, browser üzerinde form açılışı gerçekleşir. Veya alanına 5 kez peş peşe tıklayarak çalışma esnasında logo değişimi de yapılabilir.

Başlık, başlığın altındaki, başlığın sağındaki ve alttaki yazıları, not defteri içinde "Ctrl + F" tuşları ile kelime yazarak bulabilir, ilgili metinleri kendinize göre uyarlayarak kaydederek kullanabilirsiniz. Başlık alanındaki "OLGUN BAŞKANLIĞI" alanına 5 kez peş peşe tıklayarak; Departman, Müdürlük veya Başkanlığınıza ait bilgiyi manuel girebilirsiniz.

Formun boş hali;

Formun dolu hali:

PDF / Yazdır

Dışarı Aktarılan Excel Verisi:


+ Projenin Android telefonlar için mobil uygulamaya dönüştürülmüş hali aşağıdaki paylaşımdadır. "FMEA.apk" indir.

HTML Kod:
<!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 - FMEA</title>
<style>
:root{--bg:#f8fafc;--card:#fff;--line:#cbd5e1;--text:#17283b;--muted:#63758a;--blue:#0284c7;--deep:#075985;--green:#166534}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;background:#edf3f8;color:var(--text);font:13px/1.42 'Segoe UI',Arial,sans-serif}
button,input,select,textarea{font:inherit}
button{cursor:pointer}
input,select,textarea{width:100%;min-height:35px;padding:7px 9px;color:#20344c;border:1px solid #cbd5e1;border-radius:5px;background:#fff;outline:none}
textarea{resize:vertical;min-height:60px}
input:focus,textarea:focus,select:focus{border-color:#0284c7;box-shadow:0 0 0 2px #0284c71a}
label{display:block;margin:0 0 4px;color:#334155;font-size:11.5px;font-weight:700}
label.required:after{content:' *';color:#b91c1c}
small,.hint{font-size:10.5px;color:var(--muted)}
.olgun-frame{margin:8px;border:2px solid #8196a9;border-radius:7px;background:#f8fafc;min-height:calc(100vh - 16px);box-shadow:0 5px 19px #223d5511}
.top-header{position:sticky;top:0;z-index:20;background:#fff;border-top:4px solid var(--blue);border-bottom:1px solid #bdd0dd;border-radius:4px 4px 0 0}
.header-inner{min-height:78px;padding:9px 18px;display:flex;align-items:center;gap:13px}
.brand-logo{flex:0 0 64px;height:57px;display:grid;place-items:center;border:1px solid #ccd5df;border-radius:5px;background:#f8fafc;color:var(--deep);font-weight:800;font-size:12px;padding:3px}
.brand-logo img{height:100%;width:100%;object-fit:contain}
.brand-titles{min-width:0;flex:1}
.brand-titles h1{margin:0;font-size:19px;color:var(--deep);line-height:1.2}
.brand-titles p{margin:3px 0 0;font-size:12px;color:#475569}
.brand-org{max-width:40%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#21425d;font-size:16px;font-weight:800;text-align:right;margin-left:auto}
.top-header [contenteditable=true]{outline:2px dashed var(--blue);outline-offset:2px}
.wrap{padding:12px 17px 5px;display:grid;gap:10px}
.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:9px}
.metric{background:#fff;border:1px solid var(--line);border-top:3px solid var(--blue);border-radius:6px;padding:9px 12px}
.metric small{display:block;color:#64748b;font-size:10px;font-weight:800}
.metric b{display:block;font-size:23px;color:#0c4265;line-height:1.1;margin-top:4px}
.card{min-width:0;border:1px solid var(--line);border-radius:6px;background:#fff;box-shadow:0 2px 8px #25425b08}
.card-head{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:9px 12px;background:#eaf5fc;border-left:4px solid var(--blue);border-bottom:1px solid #b9d4e6;border-radius:5px 5px 0 0}
.card-head h2{margin:0;display:flex;align-items:center;gap:8px;color:#075985;font-size:14px}
.index{display:inline-grid;place-items:center;font-size:10px;color:#075985;background:#d2eafb;padding:4px 6px;border-radius:3px}
.card-body{padding:11px 13px}
.grid{display:grid;gap:9px}
.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{display:flex;align-items:center;gap:7px;font-size:12px;font-weight:800;color:#075985;margin:12px 0 8px;padding:0 0 5px;border-bottom:1px solid #dcecf5}
.strip:first-child{margin-top:0}
.strip em{font-style:normal;font-size:10px;display:inline-grid;place-items:center;height:22px;width:22px;border:1px solid #b8daed;background:#eaf6fc;border-radius:4px}
.method-lock{min-height:35px;background:#eef8ff;border:1px solid #b9def7;border-radius:5px;color:#075985;font-weight:800;padding:8px 10px}
.assessment{display:grid;grid-template-columns:minmax(350px,.87fr) minmax(470px,1.13fr);gap:12px}
.score-stack{display:grid;gap:8px}
.score-card{min-width:0;border:1px solid #cbd5e1;border-top:3px solid #125ba9;border-radius:6px;overflow:hidden}
.score-card.after{border-top-color:#8651b4}
.score-head{display:flex;justify-content:space-between;align-items:center;padding:6px 10px;background:#f3f8fb;border-bottom:1px solid #e0e9f1}
.score-head strong{font-size:12px}.score-head span{font-size:10px;color:#64748b}
.factors{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px;padding:8px}
.score-result{display:flex;justify-content:space-between;align-items:center;padding:6px 9px;background:#f7fafc;border-top:1px solid #e8edf1}
.score-result small{display:block;font-size:10px}.score-result b{font-size:20px;line-height:1.12}
.tag{display:inline-block;padding:3px 6px;border-radius:4px;font-size:10px;font-weight:800;text-align:center;border:1px solid #cbd5e1;background:#e2e8f0;color:#475569}
.tag.izleme{color:#166534;background:#ddf7e4;border-color:#a7e0bc}.tag.dikkat{color:#805b14;background:#fff5d2;border-color:#e5cc79}
.tag.oncelikli{color:#9e4512;background:#ffe0c5;border-color:#e5a074}.tag.kritik{color:#a51d30;background:#ffdee2;border-color:#ec9eaa}
.graph-card{border:1px solid #acc5d7;border-radius:6px;min-width:0;overflow:hidden}
.graph-head{background:#e9f4fc;border-bottom:1px solid #cbdde8;padding:8px 10px;display:flex;align-items:center;justify-content:space-between;gap:8px}
.graph-head strong{font-size:12px;color:#0b4669}
.tabs{display:flex;gap:4px;flex-wrap:wrap}
.tabs button{padding:5px 7px;border:1px solid #bfd6e7;border-radius:4px;background:white;font-size:11px;font-weight:700;color:#315d76}
.tabs button.active{background:#dcedfb;color:#075985;border-color:#73acd0}
.graph-body{padding:9px 10px}
.graph-body svg{width:100%;height:auto;display:block;max-height:225px}
.graph-summary{background:#f7fafc;border:1px solid #e2e8f0;padding:5px 7px;border-radius:4px;font-size:11px;color:#475569}
.guidance{font-size:10.5px;color:#6b5b37;background:#fff9e9;border:1px solid #ebdbac;padding:7px 9px;border-radius:5px}
.rubric{border:1px solid #d7e3ec;border-radius:5px;background:#f8fafc;margin-top:9px}
.rubric summary{padding:7px 9px;font-size:11px;color:#075985;font-weight:800;cursor:pointer}
.rubric .inner{padding:1px 10px 9px;font-size:11px;color:#485f73}
.rubric table{border-collapse:collapse;width:100%}.rubric th,.rubric td{border:1px solid #dae5ee;padding:5px;text-align:left;vertical-align:top}.rubric th{background:#eaf5fc;color:#075985}
.checkbox-label{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:650;color:#334155}
.checkline{border:1px solid #d9e7ef;background:#f7fbfd;border-radius:5px;padding:9px 10px;margin-top:9px}
.checkline input{width:17px;height:17px;min-height:0;accent-color:#0284c7}
.actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:11px}
.grow{flex:1}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:5px;min-height:34px;padding:7px 10px;background:#fff;border:1px solid #cbd5e1;color:#334155;border-radius:5px;font-weight:700;white-space:nowrap}
.btn:hover{background:#f1f5f9}
.btn-primary{background:#0284c7;border-color:#0284c7;color:#fff}.btn-primary:hover{background:#0369a1}
.btn-sm{min-height:27px;padding:4px 7px;font-size:11px}
.btn-blue{background:#e8f4ff;border-color:#b7d8fb;color:#075985}.btn-green{background:#eaf8ef;border-color:#b9e2c6;color:#166534}.btn-grey{background:#f1f3f5;border-color:#d6dbe1}
.btn-red{color:#b42332;border-color:#f0bac3}
.filters{display:flex;gap:7px;flex-wrap:wrap}.filters input{max-width:190px}.filters select{max-width:156px}
.tablebox{overflow-x:auto}.tablebox table{border-collapse:collapse;width:100%;min-width:1200px}
.tablebox th{white-space:nowrap;background:#eef5f9;color:#475569;border-bottom:1px solid #d6e2ea;padding:7px;text-align:left;font-size:10.5px}
.tablebox td{padding:7px;border-bottom:1px solid #e9eff4;vertical-align:top;font-size:10.5px;overflow-wrap:anywhere}
.tablebox tr:hover{background:#f7fbfe}.sub{color:#64748b;font-size:10px;margin-top:2px}
.empty{text-align:center;padding:20px;color:#64748b}.table-bottom{padding:8px 12px}
.bottom-actions{padding:3px 17px}.bottom-panel{display:flex;gap:9px;flex-wrap:wrap;width:max-content;max-width:100%;background:#fff;border:1px solid #b9cbd9;border-radius:6px;padding:8px 10px}
.risk-footer{margin:3px 17px 0;border-top:1px solid #dbe3eb;padding:5px 0;white-space:nowrap;overflow-x:auto;color:#475569;font-size:10.5px}
.toast{position:fixed;bottom:18px;right:18px;background:#143d54;color:#fff;z-index:99;opacity:0;pointer-events:none;padding:10px 12px;border-radius:6px;transition:opacity .2s}
.toast.show{opacity:1}
.sr-only{position:absolute!important;width:1px!important;height:1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important}
#printPages,#printFrame{display:none}
@media(max-width:1080px){.assessment{grid-template-columns: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))}.score-stack{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:720px){.olgun-frame{margin:3px}.header-inner{padding:8px 10px;flex-wrap:wrap}.brand-logo{flex-basis:50px;height:50px}.brand-titles h1{font-size:15px}.brand-titles p{font-size:10px}.brand-org{max-width:100%;width:100%;font-size:11px;white-space:normal}.wrap{padding:9px}.g5,.g4,.g3,.g2{grid-template-columns:1fr}.span2,.span3{grid-column:auto}.score-stack{grid-template-columns:1fr}.factors{grid-template-columns:1fr 1fr 1fr}.filters input,.filters select{max-width:none;flex:1}.bottom-actions{padding:3px 9px}.bottom-panel{width:100%}.bottom-panel .btn{flex:1 1 125px;white-space:normal}.risk-footer{margin:2px 9px 0;font-size:10px}}
@media(max-width:420px){.factors{grid-template-columns:1fr}.metrics{grid-template-columns:1fr 1fr}.metric b{font-size:19px}}
@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;font-size:9px!important;-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
 #olgunFrame,.toast{display:none!important}
 #printPages{display:table!important;width:100%!important;table-layout:fixed;border-collapse:separate;border-spacing:0}
 #printPages>thead{display:table-header-group!important}#printPages>tbody{display:table-row-group!important}
 #printPages>thead>tr>td{padding:0 0 8px}#printPages>tbody>tr>td{padding:0 8px 8px}
 #printFrame{display:block!important;position:fixed;inset:0;border:1.4px solid #7f95a9;pointer-events:none}
 .print-header{display:flex;align-items:center;gap:10px;background:#fff;border-top:4px solid #0284c7;border-bottom:1px solid #9fb2c2;min-height:65px;padding:6px 12px}
 .print-header img{width:52px;height:49px;object-fit:contain;flex:none}
 .print-header h1{font-size:17px;color:#075985;margin:0}.print-header p{font-size:10px;color:#475569;margin:2px 0 0}
 .print-org{margin-left:auto;font-size:13px;font-weight:800;color:#1e3a57;text-align:right}
 .print-section{border:1px solid #b5c9d8;margin-bottom:8px;border-radius:4px;break-inside:auto}
 .print-section h3{font-size:11px;padding:5px 8px;margin:0;border-left:4px solid #0284c7;background:#eaf5fc;color:#075985;break-after:avoid}
 .print-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px;padding:8px}
 .print-field{border:1px solid #dbe3eb;border-radius:3px;padding:3px 5px;min-height:25px;font-size:9px;overflow-wrap:anywhere}
 .print-field b{display:block;font-size:8px;color:#64748b;margin-bottom:2px}
 .print-charts{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:8px;break-inside:avoid!important}
 .print-chart{border:1px solid #cbd5e1;border-radius:4px;padding:7px;background:white}
 .print-chart h4{margin:0 0 2px;color:#075985;font-size:10px}.print-chart svg{width:100%;height:auto;max-height:125px;display:block}
 .print-table{width:100%;table-layout:fixed;border-collapse:collapse}.print-table th,.print-table td{font-size:8px;border:1px solid #dbe3eb;padding:5px 5px;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-detail{padding:5px 7px;border-bottom:1px solid #dce8f1;break-inside:avoid}.print-detail:last-child{border-bottom:0}
 .print-detail-head{display:flex;justify-content:space-between;gap:10px;color:#075985;font-weight:800;font-size:10px;padding:3px 0 5px}
 .print-detail-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:4px}
 .print-note{font-size:8.5px;color:#475569;padding:6px 8px}
 .print-charts,.print-charts *{print-color-adjust:exact!important;-webkit-print-color-adjust:exact!important}
}
</style>
</head>
<body>
<div id="olgunFrame" class="olgun-frame">
<header class="top-header"><div class="header-inner">
 <button id="logoTrigger" type="button" class="brand-logo" title="Logo değiştirmek için 5 kez tıklayın"><img id="brandLogo" alt="Kurum logosu" hidden><span id="logoFallback">LOGO</span></button>
 <div class="brand-titles"><h1 id="brandTitle" title="Düzenlemek için 5 kez tıklayın">İSG RİSK ANALİZİ – FMEA</h1><p id="brandSubtitle" title="Düzenlemek için 5 kez tıklayın">(Hata Türleri ve Etkileri Analizi · Makine, Ekipman ve Üretim Süreçleri)</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" accept="image/png,image/jpeg,image/webp,image/gif" class="sr-only"></header>
<main class="wrap">
 <section class="metrics" aria-label="FMEA risk özeti">
  <div class="metric"><small>TOPLAM HATA KAYDI</small><b id="mTotal">0</b></div>
  <div class="metric"><small>ÖNCELİKLİ / KRİTİK · AÇIK</small><b id="mHigh">0</b></div>
  <div class="metric"><small>HEDEF TARİHİ GEÇEN</small><b id="mOverdue">0</b></div>
  <div class="metric"><small>ÖNLEMİ DOĞRULANAN</small><b id="mVerified">0</b></div>
 </section>
 <section class="card"><div class="card-head"><h2><span class="index">01</span>İşyeri ve Değerlendirme Bilgileri</h2><span class="hint">Form alanları her yeni açılışta boş başlar.</span></div>
  <div class="card-body grid g5">
   <div><label for="company">İşyeri / Firma</label><input id="company" data-meta="company" placeholder="Örn. ABC Makine Sanayi"></div>
   <div><label for="facility">Tesis / Üretim Alanı</label><input id="facility" data-meta="facility" placeholder="Örn. Montaj Hattı"></div>
   <div><label for="prepared">Hazırlayan / Ekip</label><input id="prepared" data-meta="prepared" placeholder="FMEA çalışma ekibi"></div>
   <div><label for="assessDate">Değerlendirme Tarihi</label><input id="assessDate" data-meta="date" type="date"></div>
   <div><label for="revision">Revizyon / Belge No.</label><input id="revision" data-meta="revision" placeholder="Örn. FMEA-2026 / Rev.00"></div>
  </div>
 </section>
 <section class="card"><div class="card-head"><h2><span class="index">02</span><span id="formHeading">Yeni FMEA Hata Kaydı</span></h2><button type="button" id="newBtn" class="btn btn-sm">+ Yeni Kayıt</button></div>
  <div class="card-body"><form id="fmeaForm" novalidate autocomplete="off">
   <div class="strip"><em>01</em> Süreç ve Hata Türü</div>
   <div class="grid g4">
    <div><label>Değerlendirme Yöntemi</label><div class="method-lock">FMEA &nbsp;·&nbsp; Ş × O × T</div></div>
    <div><label for="process" class="required">Bölüm / Süreç</label><input id="process" required placeholder="Örn. Talaşlı İmalat"></div>
    <div><label for="equipment">Makine / Ekipman</label><input id="equipment" placeholder="Örn. CNC-05"></div>
    <div><label for="function" class="required">Faaliyet / Fonksiyon</label><input id="function" required placeholder="Örn. Mil tornalama"></div>
   </div>
   <div class="grid g3" style="margin-top:9px">
    <div><label for="failure" class="required">Potansiyel Hata Türü</label><textarea id="failure" required placeholder="Örn. Mil çapının tolerans dışına çıkması"></textarea></div>
    <div><label for="effect" class="required">Hatanın Potansiyel Etkisi</label><textarea id="effect" required placeholder="Örn. Montaj uygunsuzluğu, parça reddi"></textarea></div>
    <div><label for="cause" class="required">Hatanın Potansiyel Nedeni</label><textarea id="cause" required placeholder="Örn. Takım aşınması, yanlış ofset"></textarea></div>
   </div>
   <div class="grid g2" style="margin-top:9px">
    <div><label for="prevention">Mevcut Önleme Kontrolleri</label><textarea id="prevention" placeholder="Örn. Takım ömrü limiti, operatör eğitimi"></textarea></div>
    <div><label for="detection">Mevcut Tespit Kontrolleri</label><textarea id="detection" placeholder="Örn. İlk parça onayı, periyodik ölçüm"></textarea></div>
   </div>
   <div class="strip"><em>02</em> FMEA Risk Öncelik Sayısı (RÖS / RPN)</div>
   <div class="assessment">
    <div class="score-stack">
     <section class="score-card"><div class="score-head"><strong>● MEVCUT DEĞERLENDİRME</strong><span>Önlem öncesi</span></div><div class="factors" id="beforeFactors"></div><div class="score-result"><div><small>Mevcut RÖS</small><b id="beforeScore">—</b></div><span id="beforeLevel" class="tag">Değerlendirilmedi</span></div></section>
     <section class="score-card after"><div class="score-head"><strong>◆ HEDEF / KALAN RİSK</strong><span id="afterCaption">Planlanan seviye · isteğe bağlı</span></div><div class="factors" id="afterFactors"></div><div class="score-result"><div><small>Hedef / kalan RÖS</small><b id="afterScore">—</b></div><span id="afterLevel" class="tag">Değerlendirilmedi</span></div></section>
    </div>
    <section class="graph-card"><div class="graph-head"><strong>▦ FMEA Risk Analizi Grafiği</strong><div class="tabs"><button type="button" id="tabSingle" class="active">Tekil FMEA</button><button type="button" id="tabDist">Genel RÖS Dağılımı</button></div></div>
     <div class="graph-body"><div id="chart" aria-label="FMEA risk grafiği"></div><div id="chartSummary" class="graph-summary"></div></div></section>
   </div>
   <div class="guidance" style="margin-top:9px"><b>Önemli:</b> RÖS = Şiddet × Oluşma × Tespit Edilebilirlik. Tespit puanında 1 = kolay ve güvenilir tespit, 10 = tespit edilmesi çok güç. Aşağıdaki risk grupları örnek eşiklerdir; resmî/evrensel FMEA eşiği değildir. <b>Şiddeti yüksek hatalar RÖS düşük olsa da öncelikle ele alınmalıdır.</b></div>
   <details class="rubric"><summary>Ş, O ve T puanları nasıl seçilir? (1–10 değerlendirme kılavuzu)</summary><div class="inner"><table><thead><tr><th>Puan</th><th>Şiddet (Ş)</th><th>Oluşma Olasılığı (O)</th><th>Tespit Edilebilirlik (T)</th></tr></thead><tbody>
    <tr><td>1–2</td><td>İhmal edilebilir / düşük etki</td><td>Çok seyrek hata</td><td>Otomatik veya çok yüksek güvenilirlikte tespit</td></tr>
    <tr><td>3–4</td><td>Hafif / sınırlı etki</td><td>Seyrek hata</td><td>İyi tespit olanağı</td></tr>
    <tr><td>5–6</td><td>Orta düzeyde kalite, duruş veya iş etkisi</td><td>Orta olasılık</td><td>Orta düzey tespit yeteneği</td></tr>
    <tr><td>7–8</td><td>Ciddi kalite, üretim veya güvenlik etkisi</td><td>Sık / yüksek olasılık</td><td>Zayıf tespit yeteneği</td></tr>
    <tr><td>9–10</td><td>Çok ağır sonuç / tehlikeli etki</td><td>Çok sık / neredeyse kaçınılmaz</td><td>Çok zor veya pratikte tespit edilemeyen hata</td></tr>
   </tbody></table><div class="hint" style="margin-top:5px">Bu puan açıklamaları yönlendiricidir. Kurumun/ürünün onaylı FMEA puanlama tabloları esas alınmalıdır. AIAG-VDA Action Priority bu örnek RÖS gruplarıyla eşdeğer değildir.</div></div></details>
   <div class="strip"><em>03</em> İyileştirme ve Aksiyon Takibi</div>
   <div class="grid g4">
    <div class="span2"><label for="action">Önerilen / Alınacak Önlem</label><textarea id="action" placeholder="Örn. Otomatik takım aşınma uyarısı, proses içi ölçüm"></textarea></div>
    <div><label for="owner">Sorumlu Kişi / Birim</label><input id="owner" placeholder="Örn. Üretim Mühendisi"></div>
    <div><label for="due">Hedef Tamamlanma Tarihi</label><input id="due" 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="progress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div>
    <div class="span3"><label for="evidence">Doğrulama Kanıtı / Ek Açıklama</label><input id="evidence" placeholder="Örn. Yeni ölçüm raporu, bakım kaydı, test sonucu"></div>
   </div>
   <div class="checkline"><label class="checkbox-label"><input id="verified" type="checkbox"> Önlem uygulandı, etkinliği kontrol edildi ve <b>kalan risk doğrulandı</b></label><small>İşaretlemek için durum “Tamamlandı” ve hedef Ş/O/T puanları dolu olmalıdır.</small></div>
   <div class="actions"><button id="saveBtn" type="submit" class="btn btn-primary">+ FMEA Kaydı Ekle</button><button id="cancelBtn" type="button" class="btn">Vazgeç / Yeni Kayıt</button><span class="grow"></span><span id="formInfo" class="hint">Yeni kayıt oluşturuluyor.</span></div>
  </form></div>
 </section>
 <section class="card"><div class="card-head"><h2><span class="index">03</span>FMEA Hata Kayıtları ve Aksiyon Takibi <span id="recordCount" class="hint">(0)</span></h2>
  <div class="filters"><input id="search" type="search" placeholder="Hata kayıtlarında ara..." aria-label="Hata kayıtlarında ara"><select id="filter" aria-label="Aksiyon durumuna göre filtrele"><option value="">Tüm durumlar</option><option value="open">Açık</option><option value="progress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div></div>
  <div class="tablebox"><table><thead><tr><th>No.</th><th>Süreç / Makine</th><th>Hata Türü / Etki</th><th>Hata Nedeni</th><th>Mevcut Ş×O×T / RÖS</th><th>Hedef Ş×O×T / RÖS</th><th>Önerilen Önlem</th><th>Sorumlu / Hedef</th><th>Durum</th><th>İşlemler</th></tr></thead><tbody id="recordRows"></tbody></table><div id="empty" class="empty">Henüz FMEA hata kaydı bulunmuyor.</div></div>
  <div class="table-bottom"><button type="button" id="clearBtn" class="btn btn-sm btn-red">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 id="importBtn" type="button" class="btn btn-blue">⬆ Excel İçeri Aktar</button>
 <button id="exportBtn" type="button" class="btn btn-green">⬇ Excel Dışarı Aktar</button>
 <button id="printBtn" type="button" class="btn btn-grey">▣ PDF / Yazdır</button>
 <input id="importFile" type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" class="sr-only">
</div></div>
<footer class="risk-footer">Temel özelliği: Hata Türleri ve Etkileri Analizi (FMEA) &nbsp;&nbsp;&nbsp; Hesaplama: Şiddet × Oluşma × Tespit Edilebilirlik = RÖS &nbsp;&nbsp;&nbsp; Kullanım alanı: Makine, ekipman ve üretim süreçleri &nbsp;&nbsp;&nbsp; Ölçekler: Ş/O/T = 1–10</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="printFrame"></div>
<div id="toast" class="toast" aria-live="polite"></div>
<script>
'use strict';
(()=>{
const $=id=>document.getElementById(id);
const esc=v=>String(v??'').replace(/[&<>"']/g,x=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[x]));
const text=v=>String(v??'');
const today=()=>{const d=new Date();return [d.getFullYear(),String(d.getMonth()+1).padStart(2,'0'),String(d.getDate()).padStart(2,'0')].join('-')};
const uid=()=>('FMEA'+Date.now().toString(36)+Math.random().toString(36).slice(2,7)).toUpperCase();
const META_KEYS=['company','facility','prepared','date','revision'];
const fields=['process','equipment','function','failure','effect','cause','prevention','detection','action','owner','due','status','evidence'];
const groups=[{key:'izleme',min:1,short:'1–39',title:'İzleme',color:'#73cf93'}, {key:'dikkat',min:40,short:'40–99',title:'Dikkat',color:'#eed572'}, {key:'oncelikli',min:100,short:'100–199',title:'Öncelikli',color:'#f7b359'}, {key:'kritik',min:200,short:'200–1000',title:'Kritik',color:'#e97575'}];
const statusText={open:'Açık',progress:'Devam Ediyor',done:'Tamamlandı'};
const factorLabel={s:'Şiddet (Ş)',o:'Oluşma (O)',d:'Tespit (T)'};
const emptyFactor=()=>({s:null,o:null,d:null});
const initialMeta=()=>({company:'',facility:'',prepared:'',date:'',revision:''});
let data={meta:initialMeta(),records:[]},working={before:emptyFactor(),after:emptyFactor()},editingId=null,mode='single',toastTimer;
const chosen=(k,v)=>v!==''&&v!==null&&v!==undefined&&Number.isInteger(Number(v))&&Number(v)>=1&&Number(v)<=10;
const complete=o=>o&&['s','o','d'].every(k=>chosen(k,o[k]));
const anyFactor=o=>o&&['s','o','d'].some(k=>o[k]!==null&&o[k]!==undefined&&o[k]!=='');
const rpn=o=>complete(o)?Number(o.s)*Number(o.o)*Number(o.d):null;
const groupOf=n=>n===null?null:groups[n<40?0:n<100?1:n<200?2:3];
const fmt=n=>n===null?'—':Number(n).toLocaleString('tr-TR');
function message(t){clearTimeout(toastTimer);$('toast').textContent=t;$('toast').classList.add('show');toastTimer=setTimeout(()=>$('toast').classList.remove('show'),3400)}
function normFactor(raw,optional=false){const v=raw&&typeof raw==='object'?raw:{};const out={s:null,o:null,d:null};for(const k of ['s','o','d'])if(v[k]!==null&&v[k]!==undefined&&v[k]!=='')out[k]=chosen(k,v[k])?Number(v[k]):NaN;return complete(out)?out:(optional&&!anyFactor(out)?emptyFactor():null)}
function normalizeRecord(raw){if(!raw||typeof raw!=='object'||Array.isArray(raw))return null;
 const before=normFactor(raw.before),after=normFactor(raw.after,true);
 if(!before||!after)return null;
 const record={id:text(raw.id)||uid(),method:'FMEA',before,after};
 for(const k of fields)record[k]=text(raw[k]);
 record.status=statusText[record.status]?record.status:'open';record.verified=Boolean(raw.verified)&&record.status==='done'&&complete(after);
 record.created=text(raw.created)||today();return record;
}
/* Yalnızca Olgun UI logo ve başlık seçimleri kalıcıdır; risk kayıtları her açılışta sıfırdır. */
const BRAND_KEY='risk-ui-fmea-desktop-brand-v1';
const BRAND_DEFAULT={title:'İSG RİSK ANALİZİ – FMEA',subtitle:'(Hata Türleri ve Etkileri Analizi · Makine, Ekipman ve Üretim Süreçleri)',org:'OLGUN BAŞKANLIĞI',logo:''};
let brand={...BRAND_DEFAULT};
function saveBrand(){try{localStorage.setItem(BRAND_KEY,JSON.stringify(brand))}catch(e){message('Başlık bilgileri bu tarayıcıda saklanamadı.')}}
function setLogo(src){const img=$('brandLogo'),fallback=$('logoFallback');img.onload=()=>{img.hidden=false;fallback.hidden=true;};img.onerror=()=>{img.hidden=true;fallback.hidden=false;};img.src=(src&&/^data:image\//.test(src))?src:'logo.jpg';}
function setFive(el,callback){let count=0,last=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;const n=Date.now();count=n-last>1150?1:count+1;last=n;if(count>=5){count=0;callback()}})}
function setEditable(el,key){el.contentEditable='true';el.focus();const selection=window.getSelection(),range=document.createRange();range.selectNodeContents(el);selection.removeAllRanges();selection.addRange(range);
 let done=false;const cleanup=()=>{if(done)return;done=true;el.contentEditable='false';brand[key]=el.textContent.trim()||BRAND_DEFAULT[key];el.textContent=brand[key];saveBrand();el.removeEventListener('blur',cleanup);el.removeEventListener('keydown',keyHandler)};
 const keyHandler=e=>{if(e.key==='Enter'){e.preventDefault();el.blur()}if(e.key==='Escape'){el.textContent=brand[key];el.blur()}};
 el.addEventListener('blur',cleanup);el.addEventListener('keydown',keyHandler);
}
function initializeBrand(){try{const st=JSON.parse(localStorage.getItem(BRAND_KEY)||'null');if(st&&typeof st==='object')for(const k of Object.keys(BRAND_DEFAULT))if(typeof st[k]==='string')brand[k]=st[k]}catch(e){}
 for(const [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']]){$(id).textContent=brand[key];setFive($(id),()=>setEditable($(id),key))}
 setLogo(brand.logo);setFive($('logoTrigger'),()=>$('logoChooser').click());
 $('logoChooser').addEventListener('change',e=>{const file=e.target.files?.[0];if(!file)return;if(!file.type.startsWith('image/')||file.size>1250000){alert('Lütfen 1,25 MB altında bir PNG/JPG/WebP/GIF seçin.');e.target.value='';return}const fr=new FileReader();fr.onload=()=>{brand.logo=String(fr.result||'');setLogo(brand.logo);saveBrand();message('Kurum logosu kaydedildi.')};fr.readAsDataURL(file);e.target.value=''});
}
function renderMeta(){for(const el of document.querySelectorAll('[data-meta]'))el.value=data.meta[el.dataset.meta]||''}
function factorMarkup(side){return ['s','o','d'].map(k=>`<div><label for="f-${side}-${k}">${factorLabel[k]}</label><select id="f-${side}-${k}" data-side="${side}" data-factor="${k}" ${side==='before'?'required':''}><option value="">Seçiniz</option>${Array.from({length:10},(_,i)=>{const v=i+1;const extra=k==='d'?(v===1?' · Çok kolay tespit':v===10?' · Çok zor tespit':''):k==='s'?(v===1?' · Çok hafif':v===10?' · Çok ağır':''):(v===1?' · Çok seyrek':v===10?' · Çok sık':'');return `<option value="${v}" ${working[side][k]===v?'selected':''}>${v}${extra}</option>`}).join('')}</select></div>`).join('')}
function showFactors(){$('beforeFactors').innerHTML=factorMarkup('before');$('afterFactors').innerHTML=factorMarkup('after');showScore()}
function showScore(){for(const side of ['before','after']){const v=rpn(working[side]),group=groupOf(v);$(side+'Score').textContent=fmt(v);$(side+'Level').textContent=group?group.title:'Değerlendirilmedi';$(side+'Level').className='tag '+(group?group.key:'')}
 $('afterCaption').textContent=$('verified').checked&&$('status').value==='done'?'Kalan risk doğrulandı':'Planlanan seviye · isteğe bağlı';drawChart();}
function chartSingleSvg(before,after){const current=rpn(before),target=rpn(after),max=Math.max(100,current||0,target||0),W=620;
 const bars=[{name:'Mevcut RÖS',value:current,color:'#125ba9',y:49},{name:'Hedef RÖS',value:target,color:'#8651b4',y:115}];
 const base=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} 205" role="img" aria-label="Mevcut ve hedef RÖS karşılaştırması" style="font-family:Segoe UI,Arial,sans-serif"><text x="8" y="18" font-size="12" font-weight="700" fill="#27425d">Mevcut / Hedef Risk Öncelik Sayısı (RÖS)</text>`;
 const items=bars.map(b=>{const width=b.value===null?0:Math.round(b.value/max*380);return `<text x="8" y="${b.y+15}" font-size="12" fill="#334155">${b.name}</text><rect x="132" y="${b.y}" width="380" height="25" rx="3" fill="#edf2f7" stroke="#dce5ee"/><rect x="132" y="${b.y}" width="${width}" height="25" rx="3" fill="${b.color}"/><text x="525" y="${b.y+17}" font-size="15" font-weight="800" fill="#1f3651">${b.value===null?'—':b.value}</text>`}).join('');
 const footer=`<text x="132" y="180" font-size="11" fill="#475569">Ş / O / T · Mevcut: ${complete(before)?before.s+' / '+before.o+' / '+before.d:'—'} · Hedef: ${complete(after)?after.s+' / '+after.o+' / '+after.d:'—'}</text><text x="132" y="199" font-size="9.5" fill="#64748b">Çubukların ölçeği: 0–${max} puan (RÖS aralığı 1–1000)</text></svg>`;
 return base+items+footer;
}
function chartDistSvg(){const counts=groups.map(()=>0);for(const r of data.records){const g=groupOf(rpn(r.before));if(g)counts[groups.indexOf(g)]++}const top=Math.max(1,...counts);
 const rows=groups.map((g,i)=>{const y=35+i*36,w=Math.round(counts[i]/top*325);return `<text x="6" y="${y+17}" font-size="12" fill="#334155" font-weight="700">${g.title}</text><rect x="113" y="${y}" width="325" height="24" rx="3" fill="#eef2f7" stroke="#e0e8f0"/><rect x="113" y="${y}" width="${w}" height="24" rx="3" fill="${g.color}"/><text x="449" y="${y+17}" fill="#1f3651" font-size="14" font-weight="800">${counts[i]} kayıt</text>`}).join('');
 return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 620 200" role="img" aria-label="Genel risk öncelik sayısı dağılımı" style="font-family:Segoe UI,Arial,sans-serif"><text x="6" y="17" font-size="12" font-weight="800" fill="#27425d">Kayıtların Mevcut RÖS Değerlerine Göre Dağılımı</text>${rows}<text x="113" y="195" fill="#64748b" font-size="10">Toplam ${data.records.length} kayıt · Örnek kurumsal RÖS aralıkları</text></svg>`;
}
function drawChart(){$('tabSingle').classList.toggle('active',mode==='single');$('tabDist').classList.toggle('active',mode==='distribution');$('chart').innerHTML=mode==='single'?chartSingleSvg(working.before,working.after):chartDistSvg();const b=rpn(working.before),a=rpn(working.after);
 $('chartSummary').textContent=mode==='single'?(b===null?'Şiddet, oluşma ve tespit değerlerini seçerek mevcut RÖS puanını hesaplayın.':'Mevcut: '+fmt(b)+' · '+groupOf(b).title+'  →  Hedef: '+(a===null?'Henüz belirlenmedi':fmt(a)+' · '+groupOf(a).title)):'Toplam '+data.records.length+' hata kaydı. Grafik mevcut RÖS dağılımını gösterir; ortalama değildir.'}
function clearForm(){editingId=null;$('fmeaForm').reset();working={before:emptyFactor(),after:emptyFactor()};$('status').value='';$('verified').checked=false;$('formHeading').textContent='Yeni FMEA Hata Kaydı';$('saveBtn').textContent='+ FMEA Kaydı Ekle';$('formInfo').textContent='Yeni kayıt oluşturuluyor.';showFactors()}
function formValues(){const raw={id:editingId||uid(),created:editingId?(data.records.find(r=>r.id===editingId)?.created||today()):today(),before:{...working.before},after:{...working.after},verified:$('verified').checked};for(const k of fields)raw[k]=$(k).value.trim();return normalizeRecord(raw)}
function startEdit(id){const r=data.records.find(x=>x.id===id);if(!r)return;editingId=id;for(const k of fields)$(k).value=r[k]||'';$('verified').checked=r.verified;working={before:{...r.before},after:{...r.after}};showFactors();mode='single';drawChart();$('formHeading').textContent='FMEA Kaydını Düzenle';$('saveBtn').textContent='✓ Değişiklikleri Kaydet';$('formInfo').textContent='Kayıt düzenleniyor.';document.getElementById('formHeading').scrollIntoView({behavior:'smooth',block:'start'})}
function renderTable(){const filter=$('filter').value,q=$('search').value.toLocaleLowerCase('tr-TR').trim();const todayStr=today();const filtered=data.records.filter(r=>(!filter||r.status===filter)&&(!q||[r.process,r.equipment,r.function,r.failure,r.effect,r.cause,r.action,r.owner].some(x=>x.toLocaleLowerCase('tr-TR').includes(q))));
 $('mTotal').textContent=data.records.length;$('mHigh').textContent=data.records.filter(r=>!r.verified&&rpn(r.before)>=100).length;$('mOverdue').textContent=data.records.filter(r=>!r.verified&&r.due&&r.due<todayStr).length;$('mVerified').textContent=data.records.filter(r=>r.verified).length;
 $('recordCount').textContent=`(${filtered.length}/${data.records.length})`;$('empty').hidden=filtered.length>0;
 $('recordRows').innerHTML=filtered.map((r,i)=>{const b=rpn(r.before),a=rpn(r.after),g=groupOf(b),h=groupOf(a);return `<tr><td>${i+1}</td><td><b>${esc(r.process)}</b><div class="sub">${esc(r.equipment)} · ${esc(r.function)}</div></td><td><b>${esc(r.failure)}</b><div class="sub">${esc(r.effect)}</div></td><td>${esc(r.cause)}</td><td>${r.before.s}×${r.before.o}×${r.before.d}=<b>${b}</b><div><span class="tag ${g.key}">${g.title}</span></div></td><td>${a===null?'—':r.after.s+'×'+r.after.o+'×'+r.after.d+'=<b>'+a+'</b>'}<div>${h?`<span class="tag ${h.key}">${r.verified?'Doğrulandı':'Hedef'} · ${h.title}</span>`:''}</div></td><td>${esc(r.action)}</td><td>${esc(r.owner)}<div class="sub">${esc(r.due)}</div></td><td>${esc(statusText[r.status])}</td><td><div style="display:flex;gap:4px;flex-wrap:wrap"><button type="button" class="btn btn-sm" data-act="edit" data-id="${esc(r.id)}">Düzenle</button><button type="button" class="btn btn-sm" data-act="copy" data-id="${esc(r.id)}">Kopyala</button><button type="button" class="btn btn-sm btn-red" data-act="del" data-id="${esc(r.id)}">Sil</button></div></td></tr>`}).join('');drawChart();}
function validateForm(){for(const k of ['process','function','failure','effect','cause'])if(!$(k).value.trim()){alert('Zorunlu alan: '+({process:'Bölüm / Süreç',function:'Faaliyet / Fonksiyon',failure:'Hata Türü',effect:'Hatanın Etkisi',cause:'Hatanın Nedeni'}[k]));$(k).focus();return false}
 if(!complete(working.before)){alert('Mevcut FMEA için Şiddet, Oluşma ve Tespit puanlarını (1–10) seçin.');return false}
 if(anyFactor(working.after)&&!complete(working.after)){alert('Hedef FMEA için Ş, O ve T puanlarının üçünü de seçin veya tamamını boş bırakın.');return false}
 if($('verified').checked&&($('status').value!=='done'||!complete(working.after))){alert('Doğrulama için aksiyon durumu Tamamlandı olmalı ve hedef Ş/O/T puanları eksiksiz girilmelidir.');return false}
 return true}
function bind(){for(const m of document.querySelectorAll('[data-meta]'))m.addEventListener('input',()=>{data.meta[m.dataset.meta]=m.value});
 $('fmeaForm').addEventListener('change',e=>{const node=e.target;if(node.dataset.side){working[node.dataset.side][node.dataset.factor]=node.value?Number(node.value):null;showScore()}if(node.id==='status'||node.id==='verified')showScore()});
 $('fmeaForm').addEventListener('submit',e=>{e.preventDefault();if(!validateForm())return;const record=formValues();if(!record){alert('FMEA kaydı geçersiz. Puanları kontrol edin.');return}const idx=data.records.findIndex(r=>r.id===editingId);if(idx>=0)data.records[idx]=record;else data.records.push(record);const wasEdit=idx>=0;clearForm();renderTable();mode='distribution';drawChart();message(wasEdit?'FMEA kaydı güncellendi.':'FMEA hata kaydı eklendi.')});
 for(const b of ['newBtn','cancelBtn'])$(b).addEventListener('click',()=>{clearForm();mode='single';drawChart()});
 $('tabSingle').addEventListener('click',()=>{mode='single';drawChart()});$('tabDist').addEventListener('click',()=>{mode='distribution';drawChart()});
 $('recordRows').addEventListener('click',e=>{const b=e.target.closest('button[data-act]');if(!b)return;const r=data.records.find(x=>x.id===b.dataset.id);if(!r)return;const action=b.dataset.act;if(action==='edit')startEdit(r.id);if(action==='copy'){const c=normalizeRecord({...r,id:uid(),created:today(),verified:false,status:r.status==='done'?'open':r.status});data.records.push(c);renderTable();message('FMEA kaydı çoğaltıldı.')}if(action==='del'&&confirm('Bu FMEA kaydı silinsin mi?')){data.records=data.records.filter(x=>x.id!==r.id);if(editingId===r.id)clearForm();renderTable();message('FMEA kaydı silindi.')}});
 for(const k of ['search','filter'])$(k).addEventListener('input',renderTable);
 $('clearBtn').addEventListener('click',()=>{if(!data.records.length)return message('Silinecek FMEA kaydı bulunmuyor.');if(!confirm(data.records.length+' FMEA kaydı silinecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;data.records=[];clearForm();renderTable();message('Tüm kayıtlar silindi.')});
 $('exportBtn').addEventListener('click',exportExcel);$('importBtn').addEventListener('click',()=>$('importFile').click());$('importFile').addEventListener('change',e=>importExcel(e.target.files?.[0]));
 $('printBtn').addEventListener('click',printNow);window.addEventListener('beforeprint',buildPrint);
}

const asText=text;
function xmlEsc(v){return asText(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[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})]]}


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 ''}

/* Excel gerçek .xlsx biçiminde, kütüphane/CDN olmadan oluşturulur. */
const COLS=['Kayıt ID','Bölüm / Süreç','Makine / Ekipman','Faaliyet / Fonksiyon','Hata Türü','Hata Etkisi','Hata Nedeni','Önleme Kontrolleri','Tespit Kontrolleri','Mevcut Şiddet','Mevcut Oluşma','Mevcut Tespit','Mevcut RÖS','Mevcut Öncelik','Önerilen Önlem','Hedef Şiddet','Hedef Oluşma','Hedef Tespit','Hedef RÖS','Hedef Öncelik','Sorumlu','Hedef Tarih','Durum','Doğrulama','Kanıt / Not','Kayıt Tarihi'];
function recordRow(r){return [r.id,r.process,r.equipment,r.function,r.failure,r.effect,r.cause,r.prevention,r.detection,r.before.s,r.before.o,r.before.d,rpn(r.before),groupOf(rpn(r.before))?.title||'',r.action,r.after.s??'',r.after.o??'',r.after.d??'',rpn(r.after)??'',groupOf(rpn(r.after))?.title||'',r.owner,r.due,statusText[r.status]||'Açık',r.verified?'Evet':'Hayır',r.evidence,r.created]}
function excelFilename(){return 'ISG_FMEA_Risk_Analizi_'+(data.meta.date||today())+'.xlsx'}
function downloadFile(blob,name){const url=URL.createObjectURL(blob),el=document.createElement('a');el.href=url;el.download=name;document.body.append(el);el.click();el.remove();setTimeout(()=>URL.revokeObjectURL(url),5000)}
function exportExcel(){const m=data.meta,rows=[COLS,...data.records.map(recordRow)];const summary=[['Alan','Değer'],['Yöntem','FMEA'],['İşyeri',m.company],['Tesis / Üretim Alanı',m.facility],['Hazırlayan',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon',m.revision],['Toplam Hata Kaydı',data.records.length],['Açık Öncelikli / Kritik',data.records.filter(r=>!r.verified&&rpn(r.before)>=100).length],['Doğrulanan',data.records.filter(r=>r.verified).length],['Formül','RÖS = Şiddet × Oluşma × Tespit'],['Uyarı','RÖS örnek eşikleri evrensel FMEA veya AIAG-VDA AP sınıflaması değildir.']];try{downloadFile(zipCreate(workbookFiles(rows,summary)),excelFilename());message(data.records.length+' FMEA kaydı Excel dosyasına aktarıldı.')}catch(e){alert('Excel oluşturulamadı: '+e.message)}}
function fromRow(row,header){const get=k=>row[header.get(k)]??'';const rawStatus=get('Durum');const status=Object.keys(statusText).find(k=>statusText[k]===rawStatus)||(statusText[rawStatus]?rawStatus:'open');return normalizeRecord({id:get('Kayıt ID'),process:get('Bölüm / Süreç'),equipment:get('Makine / Ekipman'),function:get('Faaliyet / Fonksiyon'),failure:get('Hata Türü'),effect:get('Hata Etkisi'),cause:get('Hata Nedeni'),prevention:get('Önleme Kontrolleri'),detection:get('Tespit Kontrolleri'),before:{s:get('Mevcut Şiddet'),o:get('Mevcut Oluşma'),d:get('Mevcut Tespit')},after:{s:get('Hedef Şiddet'),o:get('Hedef Oluşma'),d:get('Hedef Tespit')},action:get('Önerilen Önlem'),owner:get('Sorumlu'),due:normalizeDate(get('Hedef Tarih')),status,verified:get('Doğrulama')==='Evet',evidence:get('Kanıt / Not'),created:normalizeDate(get('Kayıt Tarihi'))||today()})}
async function readExcel(file){if(!/\.xlsx$/i.test(file.name))throw new Error('Yalnızca .xlsx dosyaları desteklenir.');if(file.size>15*1024*1024)throw new Error('Excel dosyası 15 MB sınırını aşıyor.');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('FMEA kayıt sayfası bulunamadı.');const header=new Map(rows[0].map((v,i)=>[text(v).trim(),i]));const required=['Kayıt ID','Bölüm / Süreç','Faaliyet / Fonksiyon','Hata Türü','Hata Etkisi','Hata Nedeni','Mevcut Şiddet','Mevcut Oluşma','Mevcut Tespit','Hedef Şiddet','Hedef Oluşma','Hedef Tespit'];if(required.some(k=>!header.has(k)))throw new Error('Excel dosyası FMEA şablonuyla uyumsuz veya gerekli sütunlar eksik.');const nonempty=rows.slice(1).filter(r=>r.some(v=>text(v).trim()));const records=nonempty.map(r=>fromRow(r,header));if(records.some(r=>!r))throw new Error('Geçersiz Ş/O/T puanları veya tamamlanmamış hedef puanları olan kayıtlar var.');const ids=new Set();for(const r of records){if(ids.has(r.id))r.id=uid();ids.add(r.id)}
 const info=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings),lookup=new Map(info.slice(1).map(r=>[r[0],r[1]]));const meta=initialMeta();for(const [k,label] of Object.entries({company:'İşyeri',facility:'Tesis / Üretim Alanı',prepared:'Hazırlayan',date:'Değerlendirme Tarihi',revision:'Revizyon'}))if(lookup.has(label))meta[k]=text(lookup.get(label));return {meta,records};
}
async function importExcel(file){if(!file)return;try{const result=await readExcel(file);if(data.records.length&&!confirm('Mevcut '+data.records.length+' FMEA kaydı yerine '+result.records.length+' kayıt yüklenecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;data=result;renderMeta();clearForm();renderTable();mode='distribution';drawChart();message(result.records.length+' FMEA kaydı Excel’den yüklendi.')}catch(e){console.error(e);alert('Excel içeri aktarma başarısız: '+e.message)}finally{$('importFile').value=''}}
function field(label,v){return `<div class="print-field"><b>${esc(label)}</b>${esc(v||'—')}</div>`}
function recordDetails(r,i){const after=rpn(r.after);return `<div class="print-detail"><div class="print-detail-head"><span>${i+1}. ${esc(r.process)} / ${esc(r.equipment)} – ${esc(r.failure)}</span><span>RÖS ${rpn(r.before)} → ${after??'—'} · ${esc(statusText[r.status])} ${r.verified?'· Doğrulandı':''}</span></div><div class="print-detail-grid">${field('Faaliyet / Fonksiyon',r.function)}${field('Hata Etkisi',r.effect)}${field('Hata Nedeni',r.cause)}${field('Mevcut Önleme Kontrolleri',r.prevention)}${field('Mevcut Tespit Kontrolleri',r.detection)}${field('Önerilen Önlem / Aksiyon',r.action)}${field('Sorumlu / Termin',(r.owner||'—')+' / '+(r.due||'—'))}${field('Ş × O × T (Mevcut)',r.before.s+' × '+r.before.o+' × '+r.before.d+' = '+rpn(r.before))}${field('Ş × O × T (Hedef)',after===null?'—':r.after.s+' × '+r.after.o+' × '+r.after.d+' = '+after)}${field('Doğrulama Kanıtı / Not',r.evidence)}</div></div>`}
function logoPrintSrc(){const img=$('brandLogo');if(img&&!img.hidden&&img.complete&&img.naturalWidth>0)return brand.logo&&/^data:image\//.test(brand.logo)?brand.logo:(img.currentSrc||img.src);return ''}
function buildPrint(){const src=logoPrintSrc();const logo=src?`<img src="${esc(src)}" alt="Kurum logosu">`:`<span style="color:#64748b;font-size:10px">LOGO</span>`;
 $('printHeader').innerHTML=`<div class="print-header">${logo}<div style="min-width:0"><h1>${esc(brand.title)}</h1><p>${esc(brand.subtitle)}</p></div><div class="print-org">${esc(brand.org)}</div></div>`;
 const m=data.meta;
 let current=working.before,target=working.after;
 if(rpn(current)===null&&data.records.length){current=data.records[0].before;target=data.records[0].after}
 const overview=`<div class="print-section"><h3>01 · İşyeri ve Değerlendirme Bilgileri</h3><div class="print-grid">${field('İşyeri / Firma',m.company)}${field('Tesis / Alan',m.facility)}${field('Hazırlayan',m.prepared)}${field('Değerlendirme Tarihi',m.date)}${field('Revizyon',m.revision)}</div></div>`;
 const chartSection=`<div class="print-section" style="break-inside:avoid"><h3>02 · FMEA Grafik Analizi</h3><div class="print-charts"><div class="print-chart"><h4>Mevcut / Hedef RÖS</h4>${chartSingleSvg(current,target)}</div><div class="print-chart"><h4>Genel Hata Öncelik Dağılımı</h4>${chartDistSvg()}</div></div><div class="print-note">Örnek RÖS aralıkları: İzleme 1–39, Dikkat 40–99, Öncelikli 100–199, Kritik 200–1000. Bunlar resmî veya evrensel FMEA eşikleri değildir.</div></div>`;
 const rows=data.records.map((r,i)=>`<tr><td>${i+1}</td><td>${esc(r.process)} / ${esc(r.equipment)}</td><td>${esc(r.failure)}</td><td>${esc(r.effect)}</td><td>${esc(r.cause)}</td><td>${r.before.s}×${r.before.o}×${r.before.d}=${rpn(r.before)}</td><td>${rpn(r.after)===null?'—':r.after.s+'×'+r.after.o+'×'+r.after.d+'='+rpn(r.after)}</td><td>${esc(r.owner)}<br>${esc(r.due)}</td><td>${esc(statusText[r.status])}</td></tr>`).join('');
 const table=`<div class="print-section"><h3>03 · FMEA Kayıt Özeti (${data.records.length})</h3>${data.records.length?`<table class="print-table"><colgroup><col style="width:4%"><col style="width:15%"><col style="width:14%"><col style="width:16%"><col style="width:15%"><col style="width:10%"><col style="width:10%"><col style="width:10%"><col style="width:6%"></colgroup><thead><tr><th>No</th><th>Süreç / Makine</th><th>Hata Türü</th><th>Etki</th><th>Neden</th><th>Mevcut RÖS</th><th>Hedef RÖS</th><th>Sorumlu / Tarih</th><th>Durum</th></tr></thead><tbody>${rows}</tbody></table>`:'<div class="print-note">Henüz kaydedilmiş FMEA hata kaydı bulunmuyor.</div>'}</div>`;
 const details=data.records.length?`<div class="print-section"><h3>04 · Hata Kayıtları / Kontrol ve Aksiyon Detayları</h3>${data.records.map(recordDetails).join('')}</div>`:'';
 const note=`<div class="print-note">FMEA = Hata Türleri ve Etkileri Analizi. RÖS = Şiddet × Oluşma × Tespit Edilebilirlik. Şiddeti yüksek hata türleri, düşük RÖS olsa dahi ayrıca değerlendirilmelidir. Kurumsal yöntem ve onay süreçlerinin yerine geçmez.</div>`;
 $('printReport').innerHTML=overview+chartSection+table+details+note;
}
async function printNow(){buildPrint();const pics=[...$('printHeader').querySelectorAll('img')];await Promise.all(pics.map(img=>img.complete?Promise.resolve():new Promise(resolve=>{img.addEventListener('load',resolve,{once:true});img.addEventListener('error',resolve,{once:true});setTimeout(resolve,1800)})));window.print()}
function resetStart(){data={meta:initialMeta(),records:[]};working={before:emptyFactor(),after:emptyFactor()};editingId=null;mode='single';$('search').value='';$('filter').value='';$('importFile').value='';renderMeta();clearForm();renderTable()}
initializeBrand();bind();resetStart();window.addEventListener('pageshow',e=>{if(e.persisted)resetStart()});
})();
</script>
</body>
</html>