@extends('layouts.app')

@section('title', '중대재해처벌법 실무 점검')

@section('content')
<div id="root"></div>
@endsection

@push('scripts')
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
@include('partials.react-cdn')
<script type="text/babel">
@verbatim
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// --- 점검 데이터 ---
const initialAuditData = [
    // Category 1: 경영방침 및 목표
    { id: "c1-1", category: "경영방침 및 목표", title: "최고경영자 명의의 안전보건 경영방침 수립", desc: "형식적인 선언을 넘어, 회사 홈페이지/게시판 등에 공표되어 모든 종사자가 인지하고 있는가?" },
    { id: "c1-2", category: "경영방침 및 목표", title: "구체적이고 측정 가능한 안전보건 목표 설정", desc: "단순 '사고 예방'이 아닌, '중대재해 0건', '위험성평가 실시율 100%' 등 정량적 목표가 설정되었는가?" },
    { id: "c1-3", category: "경영방침 및 목표", title: "안전보건 경영방침의 정기적 평가 및 개정", desc: "최소 연 1회 이상 경영방침과 목표의 달성도를 평가하고 필요한 경우 개선하여 반영하고 있는가?" },
    { id: "c1-4", category: "경영방침 및 목표", title: "전사적 자원 배분 의지 표명", desc: "안전 목표 달성을 위해 인력, 예산, 시설 등 필요한 자원을 제공하겠다는 명시적 약속이 포함되어 있는가?" },
    { id: "c1-5", category: "경영방침 및 목표", title: "협력업체(하청) 포함 방침 적용", desc: "수급인 및 파견근로자를 포함한 모든 종사자에게 동일한 안전보건 방침이 적용됨을 명시하고 있는가?" },

    // Category 2: 전담 조직 및 인력
    { id: "c2-1", category: "전담 조직 및 인력", title: "안전보건 전담 조직(CSO 등) 구성", desc: "경영책임자를 보좌하여 안전보건 업무를 총괄하는 전담 조직이 실질적으로 운영되고 있는가?" },
    { id: "c2-2", category: "전담 조직 및 인력", title: "전담 조직의 실질적 권한 부여", desc: "해당 조직이 타 부서에 안전 조치를 요구하고 관철할 수 있는 권한(작업중지권 등)을 가지고 있는가?" },
    { id: "c2-3", category: "전담 조직 및 인력", title: "안전/보건관리자 법정 인원 선임", desc: "사업장 규모 및 특성에 맞는 법정 자격 요건을 갖춘 안전관리자 및 보건관리자를 선임하였는가?" },
    { id: "c2-4", category: "전담 조직 및 인력", title: "안전보건 인력의 겸직 금지 준수", desc: "선임된 전문 인력이 생산, 총무 등 다른 업무를 겸직하여 안전 업무를 소홀히 하지 않는가?" },
    { id: "c2-5", category: "전담 조직 및 인력", title: "안전보건 관리감독자 지정 및 업무 부여", desc: "현장 관리감독자에게 안전보건 업무를 명확히 부여하고, 이를 수행할 시간적 여유를 보장하는가?" },
    { id: "c2-6", category: "전담 조직 및 인력", title: "관리감독자 업무 수행 평가", desc: "관리감독자가 부여된 안전보건 임무(순회점검 등)를 충실히 수행하고 있는지 반기 1회 이상 평가하는가?" },

    // Category 3: 예산 편성 및 집행
    { id: "c3-1", category: "예산 편성 및 집행", title: "안전보건 전용 예산 편성", desc: "재해 예방에 필요한 예산(시설 개선, 장비 구입, 교육비 등)이 다른 예산과 독립적으로 편성되었는가?" },
    { id: "c3-2", category: "예산 편성 및 집행", title: "위험성평가 결과에 따른 예산 반영", desc: "위험성평가 및 현장 점검에서 도출된 개선 필요 사항을 차기 예산에 즉각적으로 반영하고 있는가?" },
    { id: "c3-3", category: "예산 편성 및 집행", title: "계획 대비 예산 집행률 관리", desc: "편성된 안전 예산이 타 용도로 전용되지 않고 목적에 맞게 100% 정상 집행되고 있는가?" },
    { id: "c3-4", category: "예산 편성 및 집행", title: "예산 집행 내역 증빙 관리", desc: "예산 집행 내역(영수증, 기안서 등)이 명확히 문서화되어 보관되고 있는가? (수사 대비 핵심)" },
    { id: "c3-5", category: "예산 편성 및 집행", title: "협력업체 안전보건 지원 예산", desc: "도급을 준 하청업체의 안전보건 확보를 위해 필요한 예산(안전장비 지원 등)을 배정하였는가?" },

    // Category 4: 유해·위험요인 확인 및 개선
    { id: "c4-1", category: "유해·위험요인 확인 및 개선", title: "정기 위험성평가 실시 (반기 1회 이상)", desc: "법적 기준에 따라 사업장 내 모든 작업에 대해 위험성평가를 정기적으로 실시하고 있는가?" },
    { id: "c4-2", category: "유해·위험요인 확인 및 개선", title: "수시 위험성평가 체계 작동", desc: "기계기구 도입, 작업 방법 변경 등 변경 발생 시 즉각적으로 수시 위험성평가를 실시하는가?" },
    { id: "c4-3", category: "유해·위험요인 확인 및 개선", title: "위험성평가 과정에 현장 근로자 참여", desc: "관리자 단독이 아닌, 실제 해당 작업을 수행하는 근로자가 평가 과정에 직접 참여하여 의견을 내는가?" },
    { id: "c4-4", category: "유해·위험요인 확인 및 개선", title: "아차사고(Near Miss) 발굴 및 분석", desc: "실제 사고로 이어지지 않은 '아차사고' 사례를 적극적으로 수집하고 원인을 분석하여 대책을 수립하는가?" },
    { id: "c4-5", category: "유해·위험요인 확인 및 개선", title: "허용 불가 위험에 대한 즉각 조치", desc: "위험성평가 결과 '허용 불가' 등급으로 판정된 위험요인에 대해 작업 중지 또는 즉각적인 개선 조치를 취하는가?" },
    { id: "c4-6", category: "유해·위험요인 확인 및 개선", title: "개선 대책의 효과성 검증", desc: "위험요인 개선 조치 완료 후, 해당 조치가 실제로 위험을 낮췄는지 재평가하여 검증하는가?" },
    { id: "c4-7", category: "유해·위험요인 확인 및 개선", title: "안전보건 관계 법령 의무이행 점검", desc: "산업안전보건법 등 관련 법령에서 요구하는 안전/보건 조치가 실제 이행되고 있는지 반기 1회 이상 점검하는가?" },

    // Category 5: 종사자 의견 청취 및 대응
    { id: "c5-1", category: "종사자 의견 청취 및 대응", title: "안전보건 의견 청취 절차 마련", desc: "산업안전보건위원회 외에도 모바일 앱, 제안함, 익명 게시판 등 종사자가 쉽게 의견을 낼 수 있는 창구가 있는가?" },
    { id: "c5-2", category: "종사자 의견 청취 및 대응", title: "접수된 의견의 실질적 검토 및 반영", desc: "종사자가 제안한 의견을 경영책임자 등에게 보고하고, 타당한 경우 실제 개선 조치로 이어지는가?" },
    { id: "c5-3", category: "종사자 의견 청취 및 대응", title: "의견 반영 결과 피드백 제공", desc: "의견을 낸 종사자에게 검토 결과 및 조치 계획을 명확하게 회신(피드백) 해주고 있는가?" },
    { id: "c5-4", category: "종사자 의견 청취 및 대응", title: "급박한 위험 시 작업중지권 보장", desc: "종사자가 위험을 인지하고 작업을 중지했을 때, 이를 이유로 인사상 불이익을 주지 않는 규정이 실효성 있게 작동하는가?" },
    { id: "c5-5", category: "종사자 의견 청취 및 대응", title: "재난 대응 매뉴얼 수립 및 훈련", desc: "중대재해 발생 또는 발생 급박 상황에 대비한 매뉴얼이 구비되어 있고, 반기 1회 이상 모의 훈련을 실시하는가?" },

    // Category 6: 도급·용역·위탁 안전 확보
    { id: "c6-1", category: "도급·용역·위탁 안전 확보", title: "수급인 선정 시 안전 역량 평가 기준 마련", desc: "하청, 용역, 위탁 업체 선정 시 최저가가 아닌 '안전보건관리 수준'을 필수 평가 항목으로 반영하는가?" },
    { id: "c6-2", category: "도급·용역·위탁 안전 확보", title: "수급인의 안전보건 조치 비용(적정 단가) 보장", desc: "수급인이 안전하게 작업할 수 있도록 적정한 공사비(용역비)와 공사 기간을 보장하고 있는가?" },
    { id: "c6-3", category: "도급·용역·위탁 안전 확보", title: "도급인-수급인 간 안전보건 협의체 운영", desc: "원청과 하청이 참여하는 안전보건 협의체를 정기적으로(월 1회 이상) 개최하여 소통하고 있는가?" },
    { id: "c6-4", category: "도급·용역·위탁 안전 확보", title: "합동 안전보건 점검 실시", desc: "도급인(원청) 주관으로 수급인(하청)과 함께 정기적인 현장 합동 안전 점검을 실시하고 있는가?" },
    { id: "c6-5", category: "도급·용역·위탁 안전 확보", title: "수급인 근로자에 대한 안전 교육 지원", desc: "하청 근로자가 유해/위험 작업에 투입되기 전 충분한 안전 교육을 받도록 원청이 지원하거나 확인하는가?" },
    { id: "c6-6", category: "도급·용역·위탁 안전 확보", title: "불법 파견 및 편법 고용 방지 체계", desc: "실질적 지휘·명령을 하면서도 형식적 도급계약을 맺어 안전 책임을 회피하는 사례가 없는지 점검하는가?" },
];

const CATEGORY_COLORS = ['#3B82F6','#8B5CF6','#F59E0B','#EF4444','#10B981','#EC4899'];

// --- 토글 버튼 ---
function StatusToggle({ status, onChange }) {
    const opts = [
        { value: 'pass', label: '양호', active: 'bg-green-100 text-green-700 shadow-sm', inactive: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100' },
        { value: 'fail', label: '미흡', active: 'bg-red-100 text-red-700 shadow-sm', inactive: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100' },
        { value: 'pending', label: '대기', active: 'bg-slate-300 text-slate-700 shadow-sm', inactive: 'text-slate-400 hover:text-slate-600 hover:bg-slate-100' },
    ];
    return (
        <div style={{ display: 'flex', gap: '2px', border: '1px solid #E2E8F0', borderRadius: '8px', padding: '3px', backgroundColor: '#F8FAFC', flexShrink: 0 }}>
            {opts.map(o => (
                <button key={o.value} onClick={() => onChange(o.value)}
                    className={`px-3 py-1.5 text-sm font-medium rounded-md transition-all ${status === o.value ? o.active : o.inactive}`}
                >{o.label}</button>
            ))}
        </div>
    );
}

// --- 체크리스트 항목 ---
function CheckItem({ item, index, status, onStatusChange }) {
    return (
        <div style={{ padding: '14px 18px', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: '16px', borderBottom: '1px solid #F1F5F9' }}>
            <div style={{ flex: 1 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
                    <span style={{ fontSize: '11px', fontWeight: 600, color: '#94A3B8' }}>항목 {index + 1}</span>
                    <span style={{ fontSize: '14px', fontWeight: 600, color: '#1E293B' }}>{item.title}</span>
                </div>
                <p style={{ fontSize: '13px', color: '#64748B', lineHeight: 1.6 }}>{item.desc}</p>
            </div>
            <StatusToggle status={status} onChange={onStatusChange} />
        </div>
    );
}

// --- 카테고리 섹션 ---
function CategorySection({ name, index, items, statuses, onStatusChange }) {
    const passed = items.filter(i => statuses[i.id] === 'pass').length;
    const total = items.length;
    const allDone = passed === total;

    return (
        <div style={{ backgroundColor: '#FFF', borderRadius: '12px', border: '1px solid #E2E8F0', overflow: 'hidden', boxShadow: '0 1px 3px rgba(0,0,0,0.05)' }}>
            <div style={{ backgroundColor: '#F8FAFC', borderBottom: '1px solid #E2E8F0', padding: '14px 18px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
                    <div style={{
                        width: '30px', height: '30px', borderRadius: '50%',
                        backgroundColor: CATEGORY_COLORS[index] + '20', color: CATEGORY_COLORS[index],
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: '13px', fontWeight: 700,
                    }}>{index + 1}</div>
                    <span style={{ fontSize: '16px', fontWeight: 700, color: '#1E293B' }}>{name}</span>
                </div>
                <span style={{ fontSize: '13px', fontWeight: allDone ? 700 : 500, color: allDone ? '#16A34A' : '#64748B' }}>
                    {passed} / {total} 완료
                </span>
            </div>
            <div>
                {items.map((item, idx) => (
                    <CheckItem key={item.id} item={item} index={idx} status={statuses[item.id]}
                        onStatusChange={(val) => onStatusChange(item.id, val)} />
                ))}
            </div>
        </div>
    );
}

// --- 도넛 차트 ---
function DonutChart({ passed, failed, pending, total }) {
    const canvasRef = useRef(null);
    const chartRef = useRef(null);

    useEffect(() => {
        if (chartRef.current) chartRef.current.destroy();
        chartRef.current = new Chart(canvasRef.current, {
            type: 'doughnut',
            data: {
                labels: ['양호', '미흡', '대기'],
                datasets: [{ data: [passed, failed, pending], backgroundColor: ['#22c55e','#ef4444','#e2e8f0'], borderWidth: 0, hoverOffset: 4 }]
            },
            options: {
                responsive: true, maintainAspectRatio: false, cutout: '70%',
                plugins: {
                    legend: { position: 'bottom', labels: { padding: 16, usePointStyle: true, boxWidth: 8, font: { size: 12 } } },
                    tooltip: { callbacks: { label: (ctx) => ` ${ctx.label}: ${ctx.raw}항목 (${Math.round((ctx.raw/total)*100)}%)` } }
                }
            }
        });
        return () => { if (chartRef.current) chartRef.current.destroy(); };
    }, [passed, failed, pending]);

    return <div style={{ position: 'relative', height: '240px' }}><canvas ref={canvasRef}></canvas></div>;
}

// --- 바 차트 ---
function BarChart({ categories, categoryRates }) {
    const canvasRef = useRef(null);
    const chartRef = useRef(null);

    useEffect(() => {
        if (chartRef.current) chartRef.current.destroy();
        const labels = categories.map(c => c.length > 10 ? c.substring(0, 10) + '...' : c);
        chartRef.current = new Chart(canvasRef.current, {
            type: 'bar',
            data: {
                labels,
                datasets: [{ label: '달성률 (%)', data: categoryRates, backgroundColor: CATEGORY_COLORS, borderRadius: 4, barPercentage: 0.6 }]
            },
            options: {
                indexAxis: 'y', responsive: true, maintainAspectRatio: false,
                scales: {
                    x: { beginAtZero: true, max: 100, grid: { color: '#f1f5f9' }, ticks: { callback: v => v + '%' } },
                    y: { grid: { display: false }, ticks: { font: { size: 11 } } }
                },
                plugins: { legend: { display: false }, tooltip: { callbacks: { label: ctx => ` 달성률: ${ctx.raw}%` } } }
            }
        });
        return () => { if (chartRef.current) chartRef.current.destroy(); };
    }, [categoryRates]);

    return <div style={{ position: 'relative', height: '260px' }}><canvas ref={canvasRef}></canvas></div>;
}

// --- 메인 앱 ---
function App() {
    const [statuses, setStatuses] = useState(() => {
        const init = {};
        initialAuditData.forEach(i => { init[i.id] = 'pending'; });
        return init;
    });

    const categories = useMemo(() => [...new Set(initialAuditData.map(i => i.category))], []);
    const total = initialAuditData.length;

    const stats = useMemo(() => {
        const passed = Object.values(statuses).filter(s => s === 'pass').length;
        const failed = Object.values(statuses).filter(s => s === 'fail').length;
        const pending = Object.values(statuses).filter(s => s === 'pending').length;
        const percent = Math.round((passed / total) * 100) || 0;

        const catRates = categories.map(cat => {
            const items = initialAuditData.filter(i => i.category === cat);
            const p = items.filter(i => statuses[i.id] === 'pass').length;
            return Math.round((p / items.length) * 100);
        });

        return { passed, failed, pending, percent, catRates };
    }, [statuses]);

    const handleChange = useCallback((id, val) => {
        setStatuses(prev => ({ ...prev, [id]: val }));
    }, []);

    const setAll = useCallback((val) => {
        setStatuses(prev => {
            const next = { ...prev };
            Object.keys(next).forEach(k => { next[k] = val; });
            return next;
        });
    }, []);

    const statusLabel = stats.percent === 100 ? { text: '준비 완료', cls: 'bg-green-100 text-green-700' }
        : stats.percent > 70 ? { text: '양호 수준', cls: 'bg-blue-100 text-blue-700' }
        : stats.percent > 30 ? { text: '개선 필요', cls: 'bg-yellow-100 text-yellow-700' }
        : { text: '위험 수준', cls: 'bg-red-100 text-red-700' };

    return (
        <div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 64px)', overflow: 'hidden' }}>
            {/* 헤더 */}
            <div style={{ padding: '16px 24px', borderBottom: '1px solid #E2E8F0', backgroundColor: '#FFF', flexShrink: 0, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                    <h1 style={{ fontSize: '20px', fontWeight: 800, color: '#0F172A', margin: 0 }}>중대재해처벌법 실무 점검</h1>
                    <p style={{ fontSize: '13px', color: '#64748B', margin: '2px 0 0' }}>서류 중심이 아닌 "실제 작동 여부"를 검증하는 6대 영역 심층 진단 도구</p>
                </div>
                <div style={{ textAlign: 'right' }}>
                    <div style={{ fontSize: '11px', fontWeight: 600, color: '#94A3B8', letterSpacing: '0.05em', marginBottom: '2px' }}>총 점검 진행률</div>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: '4px', justifyContent: 'flex-end' }}>
                        <span style={{ fontSize: '28px', fontWeight: 800, color: '#2563EB' }}>{stats.percent}</span>
                        <span style={{ fontSize: '16px', fontWeight: 500, color: '#94A3B8' }}>%</span>
                    </div>
                </div>
            </div>

            {/* 2컬럼 레이아웃 */}
            <div style={{ display: 'flex', flex: 1, overflow: 'hidden' }}>
                {/* 왼쪽: 대시보드 */}
                <div style={{ width: '408px', flexShrink: 0, borderRight: '1px solid #E2E8F0', backgroundColor: '#F8FAFC', overflowY: 'auto', padding: '20px', display: 'flex', flexDirection: 'column', gap: '16px' }}>
                    {/* 진단 개요 */}
                    <div style={{ backgroundColor: '#FFF', borderRadius: '12px', border: '1px solid #E2E8F0', padding: '18px' }}>
                        <h2 style={{ fontSize: '16px', fontWeight: 700, color: '#1E293B', marginBottom: '8px' }}>진단 개요</h2>
                        <p style={{ fontSize: '13px', color: '#64748B', lineHeight: 1.6, marginBottom: '12px' }}>
                            검찰 조사 시 핵심 쟁점이 되는 {total}개 항목을 6대 카테고리로 재구성했습니다.
                            실제 현장에서 제도가 <strong>'작동'</strong>하고 있는지 평가하십시오.
                        </p>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
                            <span style={{ fontSize: '13px', fontWeight: 600, color: '#475569' }}>이행 완료도</span>
                            <span className={`text-xs font-bold px-2.5 py-1 rounded-full ${statusLabel.cls}`}>{statusLabel.text}</span>
                        </div>
                        <DonutChart passed={stats.passed} failed={stats.failed} pending={stats.pending} total={total} />
                    </div>

                    {/* 영역별 현황 */}
                    <div style={{ backgroundColor: '#FFF', borderRadius: '12px', border: '1px solid #E2E8F0', padding: '18px', flex: 1 }}>
                        <h2 style={{ fontSize: '16px', fontWeight: 700, color: '#1E293B', marginBottom: '12px' }}>영역별 이행 현황</h2>
                        <BarChart categories={categories} categoryRates={stats.catRates} />
                    </div>

                    <div style={{ fontSize: '11px', color: '#94A3B8', borderTop: '1px solid #E2E8F0', paddingTop: '12px' }}>
                        * 본 대시보드는 자가 진단용이며, 법적 책임을 면제하지 않습니다.
                    </div>
                </div>

                {/* 오른쪽: 체크리스트 */}
                <div style={{ flex: 1, overflowY: 'auto', padding: '24px', backgroundColor: '#FFF' }}>
                    <div>
                        <div style={{ marginBottom: '24px' }}>
                            <h2 style={{ fontSize: '20px', fontWeight: 700, color: '#0F172A', marginBottom: '8px' }}>심층 점검 체크리스트</h2>
                            <p style={{ fontSize: '13px', color: '#64748B', lineHeight: 1.6 }}>
                                각 항목에 대해 현재 기업의 실태를 객관적으로 평가해 주십시오.
                                <strong> '양호'</strong>는 단순 문서 구비가 아닌 실질적 이행과 증빙이 가능한 상태를 의미합니다.
                            </p>
                        </div>

                        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px', marginBottom: '16px' }}>
                            <button onClick={() => setAll('pass')}
                                className="text-xs px-3 py-1.5 rounded bg-green-50 text-green-700 hover:bg-green-100 border border-green-200 transition-colors">
                                전체 양호 처리
                            </button>
                            <button onClick={() => setAll('pending')}
                                className="text-xs px-3 py-1.5 rounded bg-slate-100 text-slate-600 hover:bg-slate-200 border border-slate-200 transition-colors">
                                초기화
                            </button>
                        </div>

                        <div style={{ display: 'flex', flexDirection: 'column', gap: '20px' }}>
                            {categories.map((cat, idx) => (
                                <CategorySection
                                    key={cat}
                                    name={cat}
                                    index={idx}
                                    items={initialAuditData.filter(i => i.category === cat)}
                                    statuses={statuses}
                                    onStatusChange={handleChange}
                                />
                            ))}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
@endverbatim
</script>
@endpush
