エアコン故障診断アプリ
<PRE>
import { useState, useEffect, useRef } from "react";
// ── Error Code Database ──
const ERROR_CODES = [
// H系(室内機・通信・センサー系)
{ code: "H00", category: "info", location: "システム", summary: "異常の履歴なし(正常)", cause: "異常の履歴がない時の表示", parts: [], check: "故障ではありません。正常状態です。", severity: 0, canReset: false, repairCost: "" },
{ code: "H11", category: "communication", location: "室内外通信", summary: "室内外通信異常", cause: "室内外基板間の通信エラー。配線断線・ヒューズ溶断・ノイズ等", parts: ["室内制御基板", "室外制御基板", "内外連絡線", "ヒューズ"], check: "①内外連絡線の接続確認 ②ヒューズ断線確認 ③基板コネクタ抜き差し ④電源リセット(10秒以上OFF→ON)", severity: 2, canReset: true, repairCost: "約26,000円〜" },
{ code: "H12", category: "system", location: "室外機", summary: "総合能力ランク異常", cause: "室内ユニット接続合計能力ランクに異常値検出", parts: ["接続配線", "室内外基板"], check: "接続機種の合計能力と配線状態を確認", severity: 2, canReset: true, repairCost: "要見積" },
{ code: "H13", category: "system", location: "室外機", summary: "分岐能力ランク異常", cause: "室内能力ランク(kW)が規定範囲を超えた", parts: ["接続配線", "室内外基板"], check: "接続機種の合計能力と適応機種を確認", severity: 2, canReset: true, repairCost: "要見積" },
{ code: "H14", category: "sensor", location: "室内機", summary: "室内吸込温センサー異常", cause: "室内吸込センサ系不良。断線・ショート・コネクタ接触不良", parts: ["室内吸込温度センサー", "室内制御基板"], check: "①センサーの抵抗値測定(常温25℃で約10kΩ) ②コネクタ接続確認 ③断線・ショート確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H15", category: "sensor", location: "室外機", summary: "室外圧縮機温センサー異常", cause: "室外吐出センサ系不良。断線・ショート・コネクタ接触不良", parts: ["室外吐出温度センサー", "圧縮機温度センサー", "室外制御基板"], check: "①センサーの抵抗値測定 ②コネクタ接続確認 ③室外基板点検", severity: 2, canReset: true, repairCost: "約18,000円〜" },
{ code: "H16", category: "sensor", location: "室外機", summary: "室外CT断線異常", cause: "CT(カレントトランス)断線。室外CT系不良・基板不良・冷媒ガス漏れ", parts: ["CT(カレントトランス)", "室外制御基板"], check: "①CT断線確認 ②冷媒ガス漏れ点検 ③室外基板点検", severity: 3, canReset: true, repairCost: "約20,000円〜" },
{ code: "H17", category: "sensor", location: "室外機", summary: "室外吸入温センサー異常", cause: "室外吸入温センサーの温度異常検知。断線・ショート", parts: ["室外吸入温度センサー", "室外制御基板"], check: "①センサー抵抗値測定 ②コネクタ接続確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H18", category: "sensor", location: "室外機", summary: "室外飽和温センサー異常", cause: "室外飽和温センサーの温度異常検知。断線・ショート", parts: ["室外飽和温度センサー", "室外制御基板"], check: "①センサー抵抗値測定 ②コネクタ接続確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H19", category: "motor", location: "室内機", summary: "室内ファンモーターロック異常", cause: "ファンモーターの回転異常(高回転・低回転)検知", parts: ["室内ファンモーター", "室内制御基板"], check: "①ファン回転確認(異物混入チェック) ②モーターコネクタ接続確認 ③モーター抵抗値測定 ④基板点検", severity: 2, canReset: true, repairCost: "約20,000円〜" },
{ code: "H20", category: "heater", location: "室内機", summary: "室内ヒーター制御回路異常", cause: "室内ヒーターOFF時に一定値以上の電流検出。SSRショート", parts: ["室内制御基板"], check: "①制御基板(電源部)のSSR点検 ②基板交換", severity: 3, canReset: true, repairCost: "約25,000円〜" },
{ code: "H21", category: "drain", location: "室内機", summary: "フロートスイッチ動作異常", cause: "フロートスイッチ連続断線状態。ドレンポンプ不良", parts: ["ドレンポンプ", "フロートスイッチ", "室内制御基板"], check: "①ドレンパンの水抜き ②ドレン排水経路つまり確認 ③ポンプ動作確認 ④フロートスイッチ点検", severity: 2, canReset: true, repairCost: "約18,000円〜" },
{ code: "H23", category: "sensor", location: "室内機", summary: "室内熱交換器温センサ1異常", cause: "室内熱交換器温度センサ1の異常検知", parts: ["室内熱交換器温度センサー1", "室内制御基板"], check: "①センサー抵抗値測定 ②コネクタ接続確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H24", category: "sensor", location: "室内機", summary: "室内熱交換器温センサ2異常", cause: "室内熱交換器温度センサ2の異常検知", parts: ["室内熱交換器温度センサー2", "室内制御基板"], check: "①センサー抵抗値測定 ②コネクタ接続確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H25", category: "other", location: "室内機", summary: "空気清浄異常", cause: "空気清浄機能の異常検知", parts: ["空気清浄ユニット", "室内制御基板"], check: "空気清浄ユニットの点検・清掃", severity: 1, canReset: true, repairCost: "約12,000円〜" },
{ code: "H27", category: "sensor", location: "室外機", summary: "外気温センサ異常", cause: "外気温センサの異常検知", parts: ["外気温センサー", "室外制御基板"], check: "①センサー抵抗値測定 ②コネクタ確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H28", category: "sensor", location: "室外機", summary: "室外熱交換器温センサー異常", cause: "室外熱交換器温センサーの異常", parts: ["室外熱交換器温度センサー", "室外制御基板"], check: "①センサー抵抗値測定 ②コネクタ確認", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H50", category: "motor", location: "室外機", summary: "室外ファンモーター異常", cause: "室外ファンモーターの回転異常", parts: ["室外ファンモーター", "室外制御基板"], check: "①ファン回転確認 ②異物混入チェック ③モーター点検", severity: 3, canReset: true, repairCost: "約25,000円〜" },
{ code: "H51", category: "other", location: "室外機", summary: "室外機ノズル詰まり異常", cause: "ノズルの詰まりによる異常", parts: ["ノズル", "室外機部品"], check: "ノズル清掃・点検", severity: 2, canReset: true, repairCost: "約15,000円〜" },
{ code: "H52", category: "other", location: "室外機", summary: "室外機外機故障", cause: "室外機内部の異常", parts: ["室外機各部品"], check: "室外機全体点検", severity: 3, canReset: true, repairCost: "要見積" },
{ code: "H56", category: "motor", location: "室外機", summary: "室外ファンモーターロック異常", cause: "室外ファンモーターのロック検知。異物混入やベアリング劣化", parts: ["室外ファンモーター", "室外制御基板"], check: "①異物除去 ②ベアリング確認 ③モーター交換検討", severity: 3, canReset: true, repairCost: "約25,000円〜" },
{ code: "H59", category: "compressor", location: "室外機", summary: "圧縮機位置検出異常", cause: "圧縮機の位置検出センサー異常", parts: ["圧縮機", "室外制御基板"], check: "基板・圧縮機点検", severity: 3, canReset: true, repairCost: "要見積" },
{ code: "H86", category: "communication", location: "室外機", summary: "室外機通信異常", cause: "室外機内部通信エラー", parts: ["室外制御基板"], check: "室外基板点検", severity: 2, canReset: true, repairCost: "約25,000円〜" },
{ code: "H96", category: "sensor", location: "室外機", summary: "室外機センサー異常", cause: "室外機内センサーの異常検知", parts: ["各種センサー", "室外制御基板"], check: "室外機センサー全点検", severity: 2, canReset: true, repairCost: "約18,000円〜" },
{ code: "H97", category: "compressor", location: "室外機", summary: "圧縮機モーター異常", cause: "圧縮機モーターの異常検知", parts: ["圧縮機", "室外制御基板"], check: "圧縮機・基板点検(要専門業者)", severity: 4, canReset: false, repairCost: "約80,000円〜" },
// F系(冷媒回路・電気系統系)
{ code: "F11", category: "valve", location: "室外機", summary: "冷暖切換異常(四方弁)", cause: "四方弁本体・コイル不良。基板不良", parts: ["四方弁", "四方弁コイル", "室外制御基板"], check: "①四方弁動作確認 ②コイル導通確認 ③基板点検", severity: 3, canReset: true, repairCost: "約70,000円〜" },
{ code: "F16", category: "valve", location: "室内機", summary: "冷房除湿切換異常", cause: "室内二方弁・コイル不良・室内制御基板不良", parts: ["室内二方弁", "二方弁コイル", "室内制御基板"], check: "①二方弁動作確認 ②コイル導通確認 ③基板点検", severity: 3, canReset: true, repairCost: "要見積" },
{ code: "F90", category: "electrical", location: "室外機", summary: "PFC保護異常", cause: "室外制御基板不良", parts: ["室外制御基板"], check: "室外制御基板の点検・交換", severity: 3, canReset: true, repairCost: "約30,000円〜" },
{ code: "F91", category: "refrigerant", location: "システム", summary: "冷凍サイクル異常", cause: "冷媒漏れ・三方弁開け忘れ・配管閉塞", parts: ["冷媒配管", "三方弁", "冷媒"], check: "⚠️再運転禁止⚠️ 冷媒漏れ検査(リークテスト)・三方弁確認・配管点検", severity: 4, canReset: false, repairCost: "約30,000円〜" },
{ code: "F93", category: "compressor", location: "室外機", summary: "圧縮機回転不良", cause: "室外制御基板不良・三方弁開け忘れ", parts: ["室外制御基板", "三方弁", "圧縮機"], check: "①三方弁開確認 ②基板点検 ③圧縮機点検", severity: 3, canReset: true, repairCost: "約35,000円〜" },
{ code: "F95", category: "protection", location: "室外機", summary: "冷房異常時高圧保護", cause: "熱交換器温センサー不良・放熱不良", parts: ["室外熱交換器温センサー", "室外熱交換器"], check: "①フィルター清掃 ②室外機の放熱障害物除去 ③熱交換器清掃 ④センサー点検", severity: 3, canReset: true, repairCost: "約20,000円〜" },
{ code: "F96", category: "protection", location: "室外機", summary: "トランジスタモジュール温度過昇保護", cause: "内外接続電線不良・室内外制御基板不良・室外AC回路部品不良", parts: ["内外接続電線", "室内制御基板", "室外制御基板"], check: "①内外接続電線点検 ②室内外基板点検", severity: 3, canReset: true, repairCost: "約30,000円〜" },
{ code: "F97", category: "protection", location: "室外機", summary: "圧縮機温度過昇保護", cause: "冷媒量少・放熱妨害・過負荷条件", parts: ["冷媒", "室外熱交換器", "圧縮機"], check: "①室外機周囲の通風確認 ②冷媒量確認 ③負荷条件確認", severity: 3, canReset: true, repairCost: "約25,000円〜" },
{ code: "F98", category: "protection", location: "室外機", summary: "総合電流保護", cause: "冷媒量過大・放熱妨害", parts: ["冷媒", "室外熱交換器"], check: "⚠️再運転禁止⚠️ 冷媒量点検・放熱障害確認", severity: 4, canReset: false, repairCost: "要見積" },
{ code: "F99", category: "compressor", location: "室外機", summary: "DCピーク動作異常", cause: "圧縮機不良・パワートランジスタ不良・室外制御基板不良", parts: ["圧縮機", "パワートランジスタ", "室外制御基板"], check: "⚠️再運転禁止⚠️ 圧縮機・インバーター基板の点検(要専門業者)", severity: 4, canReset: false, repairCost: "約105,000円〜" },
];
// ── Symptom-based diagnosis flows ──
const SYMPTOMS = [
{ id: "no_cool", label: "冷えない", icon: "🌡️", possibleCodes: ["F91", "F95", "F97", "H15", "H16", "H28", "F11"], checks: ["フィルター目詰まり確認", "室外機周囲の通風確認(前面50cm以上)", "リモコン設定温度確認", "室外機ファン回転確認", "配管の霜付き確認"] },
{ id: "no_heat", label: "暖まらない", icon: "❄️", possibleCodes: ["F11", "F91", "F95", "H15", "H28"], checks: ["フィルター目詰まり確認", "室外機の霜付き確認(デフロスト)", "リモコン設定温度確認", "四方弁動作音確認", "室外機ファン回転確認"] },
{ id: "no_wind", label: "風が出ない", icon: "💨", possibleCodes: ["H19", "H50", "H56"], checks: ["室内機ファン回転音確認", "フィルター清掃", "ルーバー動作確認", "モーター異音確認"] },
{ id: "water_leak", label: "水漏れ", icon: "💧", possibleCodes: ["H21"], checks: ["ドレンホース詰まり確認", "ドレンパン水量確認", "ドレンホース勾配確認", "結露量の確認(湿度・温度差)"] },
{ id: "noise", label: "異音がする", icon: "🔊", possibleCodes: ["H19", "H50", "H56", "F93", "H59"], checks: ["ファン異物混入確認", "配管振動確認", "コンプレッサー異音確認", "室外機設置状態確認(ガタつき)"] },
{ id: "stops", label: "すぐ停止する", icon: "⏹️", possibleCodes: ["F99", "F98", "F97", "F90", "H11", "H16"], checks: ["電源電圧確認(AC100V ±10%)", "室外機の放熱確認", "ブレーカー容量確認(15A以上)", "エラーコード確認"] },
{ id: "timer_blink", label: "タイマーランプ点滅", icon: "🔴", possibleCodes: ["H11", "H14", "H19", "F99"], checks: ["リモコン「お知らせ」ボタンで診断コード確認", "本体パネルを開けて診断コード確認", "エラーコードを記録して対照表と照合"] },
{ id: "smell", label: "臭いがする", icon: "👃", possibleCodes: [], checks: ["フィルター清掃", "熱交換器の汚れ確認", "ドレンパンのカビ確認", "長時間未使用時は暖房運転で乾燥"] },
];
const SEVERITY_MAP = {
0: { label: "正常", color: "#22c55e", bg: "#052e16" },
1: { label: "軽度", color: "#84cc16", bg: "#1a2e05" },
2: { label: "中度", color: "#eab308", bg: "#2e2505" },
3: { label: "重度", color: "#f97316", bg: "#2e1505" },
4: { label: "危険", color: "#ef4444", bg: "#2e0505" },
};
const CATEGORY_LABELS = {
info: "情報", communication: "通信", system: "システム", sensor: "センサー",
motor: "モーター", heater: "ヒーター", drain: "排水", other: "その他",
valve: "弁", electrical: "電気系統", refrigerant: "冷媒", compressor: "圧縮機",
protection: "保護",
};
// ── Main App ──
export default function DiagnosticSystem() {
const [mode, setMode] = useState("home"); // home, code, symptom, result, log
const [searchCode, setSearchCode] = useState("");
const [selectedResult, setSelectedResult] = useState(null);
const [selectedSymptom, setSelectedSymptom] = useState(null);
const [diagnosticLog, setDiagnosticLog] = useState([]);
const [logNote, setLogNote] = useState("");
const [currentTime, setCurrentTime] = useState(new Date());
const [showSpec, setShowSpec] = useState(false);
const inputRef = useRef(null);
useEffect(() => {
const t = setInterval(() => setCurrentTime(new Date()), 1000);
return () => clearInterval(t);
}, []);
const handleCodeSearch = () => {
const code = searchCode.toUpperCase().trim();
const found = ERROR_CODES.find(e => e.code === code);
if (found) {
setSelectedResult(found);
setMode("result");
}
};
const handleCodeKeyDown = (e) => {
if (e.key === "Enter") handleCodeSearch();
};
const addToLog = (entry) => {
setDiagnosticLog(prev => [{
...entry,
timestamp: new Date().toLocaleString("ja-JP"),
note: logNote,
id: Date.now()
}, ...prev]);
setLogNote("");
};
const matchedCodes = searchCode.length >= 1
? ERROR_CODES.filter(e => e.code.toLowerCase().includes(searchCode.toLowerCase()))
: [];
return (
<div style={{
minHeight: "100vh",
background: "#0a0a0a",
color: "#e0e0e0",
fontFamily: "'JetBrains Mono', 'Consolas', 'SF Mono', monospace",
position: "relative",
overflow: "hidden",
}}>
{/* Scan line effect */}
<div style={{
position: "fixed", top: 0, left: 0, right: 0, bottom: 0,
background: "repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,255,136,0.015) 2px, rgba(0,255,136,0.015) 4px)",
pointerEvents: "none", zIndex: 100,
}} />
{/* Header */}
<header style={{
background: "linear-gradient(180deg, #0f1a14 0%, #0a0f0c 100%)",
borderBottom: "1px solid #1a3a28",
padding: "12px 20px",
display: "flex", alignItems: "center", justifyContent: "space-between",
position: "sticky", top: 0, zIndex: 50,
}}>
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<div style={{
width: 10, height: 10, borderRadius: "50%",
background: "#00ff88", boxShadow: "0 0 8px #00ff88",
animation: "pulse 2s infinite",
}} />
<div>
<div style={{ fontSize: 13, color: "#00ff88", letterSpacing: 3, fontWeight: 700 }}>
PANASONIC AC DIAGNOSTIC SYSTEM
</div>
<div style={{ fontSize: 10, color: "#4a7a5a", letterSpacing: 1 }}>
CS-223CF-W | エオリア Fシリーズ 2023 | 2.2kW | R32冷媒
</div>
</div>
</div>
<div style={{ fontSize: 11, color: "#3a6a4a", textAlign: "right" }}>
<div>{currentTime.toLocaleDateString("ja-JP")}</div>
<div style={{ color: "#00ff88", fontSize: 14, fontWeight: 700 }}>
{currentTime.toLocaleTimeString("ja-JP")}
</div>
</div>
</header>
{/* Navigation */}
<nav style={{
display: "flex", gap: 0,
background: "#080c0a",
borderBottom: "1px solid #1a3a28",
}}>
{[
{ id: "home", label: "🏠 ダッシュボード" },
{ id: "code", label: "🔍 コード検索" },
{ id: "symptom", label: "🩺 症状診断" },
{ id: "log", label: "📋 診断ログ" },
].map(tab => (
<button key={tab.id} onClick={() => setMode(tab.id)} style={{
flex: 1, padding: "10px 8px", border: "none",
background: mode === tab.id ? "#0f2a1a" : "transparent",
color: mode === tab.id ? "#00ff88" : "#4a7a5a",
borderBottom: mode === tab.id ? "2px solid #00ff88" : "2px solid transparent",
fontSize: 12, cursor: "pointer", fontFamily: "inherit",
transition: "all 0.2s",
}}>
{tab.label}
</button>
))}
</nav>
<main style={{ padding: "16px 20px", maxWidth: 800, margin: "0 auto" }}>
{/* ═══ HOME ═══ */}
{mode === "home" && (
<div>
<div style={{
background: "linear-gradient(135deg, #0a1f14 0%, #0f1a14 100%)",
border: "1px solid #1a3a28", borderRadius: 8, padding: 20, marginBottom: 16,
}}>
<div style={{ fontSize: 14, color: "#00ff88", marginBottom: 12, fontWeight: 700 }}>
▸ 機器情報 — CS-223CF-W
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "8px 20px", fontSize: 12 }}>
{[
["品番", "CS-223CF-W"],
["シリーズ", "エオリア Fシリーズ"],
["冷房能力", "2.2kW (0.5〜2.8)"],
["暖房能力", "2.2kW (0.4〜3.9)"],
["電源", "単相100V 15A"],
["冷媒", "R32"],
["配管径", "液2分・ガス3分"],
["最大配管長", "15m(高低差12m)"],
].map(([k, v]) => (
<div key={k} style={{ display: "flex", justifyContent: "space-between" }}>
<span style={{ color: "#4a7a5a" }}>{k}:</span>
<span style={{ color: "#b0d0c0" }}>{v}</span>
</div>
))}
</div>
<button onClick={() => setShowSpec(!showSpec)} style={{
marginTop: 12, background: "transparent", border: "1px solid #2a4a38",
color: "#00ff88", padding: "6px 12px", borderRadius: 4,
cursor: "pointer", fontSize: 11, fontFamily: "inherit",
}}>
{showSpec ? "▾ 詳細仕様を閉じる" : "▸ 詳細仕様を表示"}
</button>
{showSpec && (
<div style={{ marginTop: 12, padding: 12, background: "#060d09", borderRadius: 4, fontSize: 11 }}>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "6px 20px" }}>
{[
["室内機寸法", "高285×幅780×奥197mm"],
["室外機寸法", "高539×幅675×奥240mm"],
["室内機質量", "8.0kg"],
["室外機質量", "21.0kg"],
["運転音(室内)", "冷房43dB / 暖房44dB"],
["運転音(室外)", "冷房48dB / 暖房46dB"],
["APF", "5.8"],
["室外機型番", "CU-223CF"],
].map(([k, v]) => (
<div key={k} style={{ display: "flex", justifyContent: "space-between" }}>
<span style={{ color: "#3a5a48" }}>{k}:</span>
<span style={{ color: "#8ab09a" }}>{v}</span>
</div>
))}
</div>
</div>
)}
</div>
{/* Quick actions */}
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 16 }}>
<button onClick={() => { setMode("code"); setTimeout(() => inputRef.current?.focus(), 100); }}
style={quickBtnStyle}>
<span style={{ fontSize: 28 }}>🔍</span>
<span style={{ fontSize: 13, fontWeight: 700 }}>エラーコード検索</span>
<span style={{ fontSize: 10, color: "#4a7a5a" }}>H**/F**コードを入力</span>
</button>
<button onClick={() => setMode("symptom")} style={quickBtnStyle}>
<span style={{ fontSize: 28 }}>🩺</span>
<span style={{ fontSize: 13, fontWeight: 700 }}>症状から診断</span>
<span style={{ fontSize: 10, color: "#4a7a5a" }}>症状を選んで原因特定</span>
</button>
</div>
{/* Diagnosis procedure */}
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 16,
}}>
<div style={{ fontSize: 13, color: "#00ff88", marginBottom: 12, fontWeight: 700 }}>
▸ CS-223CF-W 診断コード確認手順
</div>
<div style={{ fontSize: 11, lineHeight: 1.8, color: "#8ab09a" }}>
<div style={{ marginBottom: 8, color: "#eab308", fontWeight: 700, fontSize: 12 }}>
※ 2023年以前モデル — お知らせボタンなしの場合
</div>
{[
"1. エアコンの電源プラグを抜く",
"2. 室内機の前面パネルを両手で持ち上げて開く",
"3. 電源プラグを差し込む",
"4. 本体内部 右側の診断コード表示部を確認",
"5. アルファベット+2桁数字のコードを記録",
"6. 確認後、パネルを閉じる",
].map((step, i) => (
<div key={i} style={{ paddingLeft: 8, borderLeft: "2px solid #1a3a28", marginBottom: 4 }}>
{step}
</div>
))}
<div style={{ marginTop: 12, color: "#eab308", fontSize: 11 }}>
⚠️ 感電注意:パネル開閉時は必ず電源プラグを抜いてから作業してください
</div>
</div>
</div>
</div>
)}
{/* ═══ CODE SEARCH ═══ */}
{mode === "code" && (
<div>
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 20, marginBottom: 16,
}}>
<div style={{ fontSize: 13, color: "#00ff88", marginBottom: 12, fontWeight: 700 }}>
▸ エラーコード入力
</div>
<div style={{ display: "flex", gap: 8 }}>
<input
ref={inputRef}
type="text"
value={searchCode}
onChange={e => setSearchCode(e.target.value)}
onKeyDown={handleCodeKeyDown}
placeholder="例: H11, F99..."
style={{
flex: 1, background: "#060d09", border: "1px solid #2a4a38",
color: "#00ff88", padding: "10px 14px", borderRadius: 4,
fontSize: 16, fontFamily: "inherit", outline: "none",
letterSpacing: 2,
}}
autoFocus
/>
<button onClick={handleCodeSearch} style={{
background: "#00ff88", color: "#0a0a0a", border: "none",
padding: "10px 20px", borderRadius: 4, fontWeight: 700,
cursor: "pointer", fontFamily: "inherit", fontSize: 13,
}}>
検索
</button>
</div>
</div>
{/* Search suggestions */}
{searchCode.length >= 1 && matchedCodes.length > 0 && (
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, overflow: "hidden",
}}>
<div style={{ padding: "8px 16px", fontSize: 11, color: "#4a7a5a", borderBottom: "1px solid #1a3a28" }}>
{matchedCodes.length}件の候補
</div>
{matchedCodes.map(ec => (
<button key={ec.code} onClick={() => { setSelectedResult(ec); setMode("result"); }}
style={{
display: "flex", alignItems: "center", gap: 12,
width: "100%", padding: "10px 16px",
background: "transparent", border: "none", borderBottom: "1px solid #0f1f18",
color: "#e0e0e0", cursor: "pointer", textAlign: "left",
fontFamily: "inherit", fontSize: 12,
transition: "background 0.15s",
}}
onMouseOver={e => e.currentTarget.style.background = "#0f2a1a"}
onMouseOut={e => e.currentTarget.style.background = "transparent"}
>
<span style={{
color: SEVERITY_MAP[ec.severity].color,
fontWeight: 700, fontSize: 14, minWidth: 40,
}}>{ec.code}</span>
<span style={{ flex: 1, color: "#8ab09a" }}>{ec.summary}</span>
<span style={{
fontSize: 10, padding: "2px 6px", borderRadius: 3,
background: SEVERITY_MAP[ec.severity].bg,
color: SEVERITY_MAP[ec.severity].color,
}}>{SEVERITY_MAP[ec.severity].label}</span>
</button>
))}
</div>
)}
{/* Full code reference */}
{searchCode.length === 0 && (
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, overflow: "hidden",
}}>
<div style={{ padding: "10px 16px", fontSize: 12, color: "#00ff88", fontWeight: 700, borderBottom: "1px solid #1a3a28" }}>
▸ 全エラーコード一覧
</div>
<div style={{ maxHeight: 400, overflowY: "auto" }}>
{ERROR_CODES.map(ec => (
<button key={ec.code} onClick={() => { setSelectedResult(ec); setMode("result"); }}
style={{
display: "flex", alignItems: "center", gap: 12,
width: "100%", padding: "8px 16px",
background: "transparent", border: "none", borderBottom: "1px solid #0f1f18",
color: "#e0e0e0", cursor: "pointer", textAlign: "left",
fontFamily: "inherit", fontSize: 11, transition: "background 0.15s",
}}
onMouseOver={e => e.currentTarget.style.background = "#0f2a1a"}
onMouseOut={e => e.currentTarget.style.background = "transparent"}
>
<span style={{
color: SEVERITY_MAP[ec.severity].color,
fontWeight: 700, fontSize: 13, minWidth: 36,
}}>{ec.code}</span>
<span style={{ color: "#5a8a6a", fontSize: 10, minWidth: 50 }}>{ec.location}</span>
<span style={{ flex: 1, color: "#8ab09a" }}>{ec.summary}</span>
<span style={{
fontSize: 9, padding: "2px 5px", borderRadius: 3,
background: SEVERITY_MAP[ec.severity].bg,
color: SEVERITY_MAP[ec.severity].color,
}}>{SEVERITY_MAP[ec.severity].label}</span>
</button>
))}
</div>
</div>
)}
</div>
)}
{/* ═══ RESULT ═══ */}
{mode === "result" && selectedResult && (
<div>
<button onClick={() => setMode("code")} style={{
background: "transparent", border: "none", color: "#4a7a5a",
cursor: "pointer", fontSize: 12, fontFamily: "inherit", marginBottom: 12,
padding: 0,
}}>
← コード検索に戻る
</button>
{/* Main result card */}
<div style={{
background: "linear-gradient(135deg, #0a1f14 0%, #0f1a14 100%)",
border: `1px solid ${SEVERITY_MAP[selectedResult.severity].color}40`,
borderRadius: 8, overflow: "hidden", marginBottom: 16,
}}>
{/* Header bar */}
<div style={{
background: `${SEVERITY_MAP[selectedResult.severity].color}15`,
padding: "16px 20px",
borderBottom: `1px solid ${SEVERITY_MAP[selectedResult.severity].color}30`,
display: "flex", alignItems: "center", justifyContent: "space-between",
}}>
<div>
<div style={{
fontSize: 28, fontWeight: 900,
color: SEVERITY_MAP[selectedResult.severity].color,
letterSpacing: 3,
}}>
{selectedResult.code}
</div>
<div style={{ fontSize: 14, color: "#b0d0c0", marginTop: 4 }}>
{selectedResult.summary}
</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{
padding: "4px 12px", borderRadius: 4,
background: SEVERITY_MAP[selectedResult.severity].bg,
color: SEVERITY_MAP[selectedResult.severity].color,
fontSize: 12, fontWeight: 700, marginBottom: 4,
border: `1px solid ${SEVERITY_MAP[selectedResult.severity].color}40`,
}}>
重要度: {SEVERITY_MAP[selectedResult.severity].label}
</div>
<div style={{ fontSize: 10, color: "#5a8a6a" }}>
{selectedResult.location} | {CATEGORY_LABELS[selectedResult.category]}
</div>
</div>
</div>
<div style={{ padding: 20 }}>
{/* Cannot reset warning */}
{!selectedResult.canReset && selectedResult.severity >= 3 && (
<div style={{
background: "#2e050520", border: "1px solid #ef444440",
borderRadius: 6, padding: 12, marginBottom: 16,
color: "#ef4444", fontSize: 12,
}}>
⚠️ <strong>再運転禁止</strong> — このエラーは電源リセットでは解消しません。使用を中止し、専門業者に修理を依頼してください。
</div>
)}
{/* Cause */}
<Section title="原因">
<div style={{ fontSize: 12, color: "#b0d0c0", lineHeight: 1.8 }}>
{selectedResult.cause}
</div>
</Section>
{/* Check steps */}
<Section title="点検・対処手順">
<div style={{ fontSize: 12, color: "#b0d0c0", lineHeight: 1.8, whiteSpace: "pre-wrap" }}>
{selectedResult.check}
</div>
</Section>
{/* Parts */}
{selectedResult.parts.length > 0 && (
<Section title="関連部品">
<div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
{selectedResult.parts.map((p, i) => (
<span key={i} style={{
padding: "4px 10px", borderRadius: 4,
background: "#0f2a1a", border: "1px solid #2a4a38",
fontSize: 11, color: "#8ab09a",
}}>{p}</span>
))}
</div>
</Section>
)}
{/* Reset procedure */}
{selectedResult.canReset && (
<Section title="リセット手順">
<div style={{ fontSize: 12, color: "#8ab09a", lineHeight: 2 }}>
{[
"1. エアコンのブレーカーをOFFにする(またはコンセントを抜く)",
"2. 約10秒以上待機する",
"3. ブレーカーをONにし、エアコンを運転する",
"4. 再発する場合は専門業者に修理依頼",
].map((s, i) => (
<div key={i} style={{ paddingLeft: 8, borderLeft: "2px solid #2a4a38", marginBottom: 2 }}>
{s}
</div>
))}
</div>
</Section>
)}
{/* Repair cost */}
{selectedResult.repairCost && (
<Section title="修理費用目安">
<div style={{ fontSize: 14, color: "#eab308", fontWeight: 700 }}>
{selectedResult.repairCost}
</div>
<div style={{ fontSize: 10, color: "#5a8a6a", marginTop: 4 }}>
※部品代+工賃+出張費の合計。地域・業者により異なります
</div>
</Section>
)}
{/* Log button */}
<div style={{ marginTop: 16, display: "flex", gap: 8, alignItems: "center" }}>
<input
type="text" placeholder="メモ(任意)" value={logNote}
onChange={e => setLogNote(e.target.value)}
style={{
flex: 1, background: "#060d09", border: "1px solid #2a4a38",
color: "#8ab09a", padding: "8px 12px", borderRadius: 4,
fontSize: 11, fontFamily: "inherit", outline: "none",
}}
/>
<button onClick={() => addToLog(selectedResult)} style={{
background: "#1a3a28", border: "1px solid #2a5a38",
color: "#00ff88", padding: "8px 16px", borderRadius: 4,
cursor: "pointer", fontSize: 11, fontFamily: "inherit",
whiteSpace: "nowrap",
}}>
📋 ログに記録
</button>
</div>
</div>
</div>
</div>
)}
{/* ═══ SYMPTOM ═══ */}
{mode === "symptom" && !selectedSymptom && (
<div>
<div style={{ fontSize: 13, color: "#00ff88", marginBottom: 16, fontWeight: 700 }}>
▸ 症状を選択してください
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
{SYMPTOMS.map(s => (
<button key={s.id} onClick={() => setSelectedSymptom(s)} style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 16, cursor: "pointer",
display: "flex", flexDirection: "column", alignItems: "center", gap: 8,
transition: "all 0.2s", fontFamily: "inherit",
color: "#e0e0e0",
}}
onMouseOver={e => { e.currentTarget.style.borderColor = "#00ff88"; e.currentTarget.style.background = "#0f2a1a"; }}
onMouseOut={e => { e.currentTarget.style.borderColor = "#1a3a28"; e.currentTarget.style.background = "#0a1510"; }}
>
<span style={{ fontSize: 32 }}>{s.icon}</span>
<span style={{ fontSize: 13, fontWeight: 700 }}>{s.label}</span>
</button>
))}
</div>
</div>
)}
{mode === "symptom" && selectedSymptom && (
<div>
<button onClick={() => setSelectedSymptom(null)} style={{
background: "transparent", border: "none", color: "#4a7a5a",
cursor: "pointer", fontSize: 12, fontFamily: "inherit", marginBottom: 12,
padding: 0,
}}>
← 症状選択に戻る
</button>
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 20, marginBottom: 16,
}}>
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16 }}>
<span style={{ fontSize: 32 }}>{selectedSymptom.icon}</span>
<div>
<div style={{ fontSize: 18, color: "#00ff88", fontWeight: 700 }}>
{selectedSymptom.label}
</div>
<div style={{ fontSize: 11, color: "#5a8a6a" }}>症状別診断フロー</div>
</div>
</div>
{/* Check items */}
<Section title="まず確認すべきこと">
<div style={{ fontSize: 12, color: "#b0d0c0", lineHeight: 2 }}>
{selectedSymptom.checks.map((c, i) => (
<div key={i} style={{
paddingLeft: 8, borderLeft: "2px solid #2a4a38", marginBottom: 4,
display: "flex", alignItems: "center", gap: 8,
}}>
<span style={{ color: "#eab308" }}>□</span> {c}
</div>
))}
</div>
</Section>
{/* Related codes */}
{selectedSymptom.possibleCodes.length > 0 && (
<Section title="関連するエラーコード">
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
{selectedSymptom.possibleCodes.map(code => {
const ec = ERROR_CODES.find(e => e.code === code);
if (!ec) return null;
return (
<button key={code} onClick={() => { setSelectedResult(ec); setMode("result"); }}
style={{
display: "flex", alignItems: "center", gap: 10,
background: "#060d09", border: "1px solid #1a3a28",
borderRadius: 4, padding: "8px 12px",
cursor: "pointer", textAlign: "left",
fontFamily: "inherit", color: "#e0e0e0", fontSize: 11,
transition: "border-color 0.15s",
}}
onMouseOver={e => e.currentTarget.style.borderColor = "#00ff88"}
onMouseOut={e => e.currentTarget.style.borderColor = "#1a3a28"}
>
<span style={{
color: SEVERITY_MAP[ec.severity].color,
fontWeight: 700, fontSize: 13, minWidth: 36,
}}>{ec.code}</span>
<span style={{ flex: 1, color: "#8ab09a" }}>{ec.summary}</span>
<span style={{
fontSize: 9, padding: "2px 5px", borderRadius: 3,
background: SEVERITY_MAP[ec.severity].bg,
color: SEVERITY_MAP[ec.severity].color,
}}>{SEVERITY_MAP[ec.severity].label}</span>
<span style={{ color: "#4a7a5a" }}>→</span>
</button>
);
})}
</div>
</Section>
)}
{selectedSymptom.possibleCodes.length === 0 && (
<div style={{
background: "#0f1a14", border: "1px solid #2a4a38",
borderRadius: 6, padding: 12, marginTop: 12,
fontSize: 12, color: "#8ab09a",
}}>
この症状に直接対応するエラーコードはありません。上記のチェック項目を確認し、改善しない場合はフィルター清掃・熱交換器洗浄を検討してください。
</div>
)}
</div>
</div>
)}
{/* ═══ LOG ═══ */}
{mode === "log" && (
<div>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }}>
<div style={{ fontSize: 13, color: "#00ff88", fontWeight: 700 }}>
▸ 診断ログ ({diagnosticLog.length}件)
</div>
{diagnosticLog.length > 0 && (
<button onClick={() => setDiagnosticLog([])} style={{
background: "transparent", border: "1px solid #4a2020",
color: "#ef4444", padding: "4px 10px", borderRadius: 4,
cursor: "pointer", fontSize: 10, fontFamily: "inherit",
}}>
全消去
</button>
)}
</div>
{diagnosticLog.length === 0 ? (
<div style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 40, textAlign: "center",
color: "#4a7a5a", fontSize: 12,
}}>
診断ログはまだありません。<br />
エラーコード検索結果から「ログに記録」で追加できます。
</div>
) : (
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{diagnosticLog.map(log => (
<div key={log.id} style={{
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 6, padding: 12,
}}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span style={{
color: SEVERITY_MAP[log.severity].color,
fontWeight: 700, fontSize: 14,
}}>{log.code}</span>
<span style={{ color: "#8ab09a", fontSize: 12 }}>{log.summary}</span>
</div>
<span style={{ fontSize: 10, color: "#4a7a5a" }}>{log.timestamp}</span>
</div>
{log.note && (
<div style={{ marginTop: 6, fontSize: 11, color: "#6a9a7a", fontStyle: "italic" }}>
📝 {log.note}
</div>
)}
</div>
))}
</div>
)}
</div>
)}
</main>
{/* Footer */}
<footer style={{
padding: "12px 20px", textAlign: "center",
borderTop: "1px solid #1a3a28", marginTop: 20,
fontSize: 10, color: "#3a5a48",
}}>
⚠️ 本ツールは参考情報です。高電圧部品・冷媒回路の作業は第二種電気工事士以上の資格を持つ技術者が行ってください<br />
Panasonic CS-223CF-W Diagnostic System v1.0 | エラーコード {ERROR_CODES.length}件収録
</footer>
<style>{`
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
* { box-sizing: border-box; }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: #0a0a0a; }
::-webkit-scrollbar-thumb { background: #1a3a28; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: #2a4a38; }
`}</style>
</div>
);
}
// ── Sub-components ──
const Section = ({ title, children }) => (
<div style={{ marginBottom: 16 }}>
<div style={{
fontSize: 11, color: "#5a8a6a", marginBottom: 8,
textTransform: "uppercase", letterSpacing: 2, fontWeight: 700,
}}>
▸ {title}
</div>
{children}
</div>
);
const quickBtnStyle = {
background: "#0a1510", border: "1px solid #1a3a28",
borderRadius: 8, padding: 20, cursor: "pointer",
display: "flex", flexDirection: "column", alignItems: "center", gap: 8,
fontFamily: "'JetBrains Mono', monospace", color: "#e0e0e0",
transition: "all 0.2s",
};
</PRE>
コメント
コメントを投稿