import React, { useState, useEffect } from ‘react’;
import { initializeApp } from ‘firebase/app’;
import { getAuth, signInWithCustomToken, signInAnonymously, onAuthStateChanged } from ‘firebase/auth’;
import { getFirestore, collection, addDoc, query, onSnapshot, doc, deleteDoc } from ‘firebase/firestore’;
import {
Search,
Book,
FileText,
Send,
Loader2,
ChevronRight,
Database,
Info,
ExternalLink,
ShieldCheck,
AlertCircle,
MessageSquare
} from ‘lucide-react’;
// — Firebase Configuration —
const firebaseConfig = JSON.parse(__firebase_config);
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== ‘undefined’ ? __app_id : ‘medical-revision-explorer’;
// 固定ソースの定義
const FIXED_SOURCES = [
{ id: ‘f1’, title: ‘令和8年度診療報酬改定の概要’, type: ‘PDF/概要’ },
{ id: ‘f2’, title: ‘個別改定項目について (001639439)’, type: ‘PDF/詳細’ },
{ id: ‘f3’, title: ‘令和8年度診療報酬改定内容説明 (YouTube転記)’, type: ‘Content’ },
{ id: ‘f4’, title: ‘公用文作成の考え方’, type: ‘Guidelines’ },
{ id: ‘f5’, title: ‘関連通知及び官報掲載事項の一部訂正について’, type: ‘Notice/Errata’ },
{ id: ‘f6’, title: ‘調剤報酬改定等に係る資料 (日薬事務連絡)’, type: ‘Pharmacy’ },
{ id: ‘f7’, title: ‘疑義解釈資料の送付について(その2)’, type: ‘Q&A’ },
];
const App = () => {
const [user, setUser] = useState(null);
const [userNotes, setUserNotes] = useState([]);
const [searchQuery, setSearchQuery] = useState(”);
const [answer, setAnswer] = useState(null);
const [isSearching, setIsSearching] = useState(false);
const [error, setError] = useState(null);
const [activeTab, setActiveTab] = useState(‘fixed’); // ‘fixed’ or ‘notes’
// — Auth Setup —
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== ‘undefined’ && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) {
setError(“認証エラーが発生しました。”);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
// — Data Fetching (User Notes) —
useEffect(() => {
if (!user) return;
const q = collection(db, ‘artifacts’, appId, ‘users’, user.uid, ‘notes’);
const unsubscribe = onSnapshot(q, (snapshot) => {
setUserNotes(snapshot.docs.map(doc => ({ id: doc.id, …doc.data() })));
});
return () => unsubscribe();
}, [user]);
// — Gemini API Call —
const callGemini = async (prompt) => {
const apiKey = “”;
const systemPrompt = `あなたは「令和8年度診療報酬改定」の専門家です。
以下の資料(固定ソース)を主要なナレッジベースとして持っています:
${FIXED_SOURCES.map(s => `- ${s.title} (${s.type})`).join(‘\n’)}
回答のガイドライン:
1. 令和8年度(2026年度)の改定内容(賃上げ対応、医療DX、調剤基本料の見直し、疑義解釈など)に基づき回答してください。
2. 根拠となる資料名(例:「疑義解釈資料(その2)」より)を必ず明記してください。
3. 数値や点数、算定要件については、資料に記載がある範囲で正確に伝えてください。
4. 公用文作成の考え方に基づき、丁寧かつ簡潔な表現を心がけてください。`;
let retries = 0;
while (retries <= 5) {
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
systemInstruction: { parts: [{ text: systemPrompt }] }
})
});
if (!response.ok) throw new Error('API Error');
const data = await response.json();
return data.candidates?.[0]?.content?.parts?.[0]?.text;
} catch (err) {
if (retries === 5) throw err;
await new Promise(r => setTimeout(r, Math.pow(2, retries) * 1000));
retries++;
}
}
};
const handleSearch = async (e) => {
e.preventDefault();
if (!searchQuery.trim()) return;
setIsSearching(true);
setAnswer(null);
setError(null);
const contextNotes = userNotes.map(n => `[ユーザーメモ: ${n.title}]\n${n.content}`).join(‘\n\n’);
const prompt = `以下の質問について、令和8年度診療報酬改定の資料に基づいて回答してください。\n\n質問:${searchQuery}\n\n${contextNotes ? `補足(ユーザーメモ):\n${contextNotes}` : ”}`;
try {
const result = await callGemini(prompt);
setAnswer(result);
} catch (err) {
setError(“AI検索中にエラーが発生しました。時間を置いて再度お試しください。”);
} finally {
setIsSearching(false);
}
};
return (
{/* Sidebar */}
R8 診療報酬改定Explorer
Knowledge Base v2.0
{activeTab === ‘fixed’ ? (
FIXED_SOURCES.map(s => (
))
) : (
ノート機能はオプションです
)}
{/* Main Content */}
{/* Search Bar */}
{/* Answer Area */}
{error && (
)}
{answer ? (
Revision Intelligence Analysis
AI生成回答につき、最終的な判断は各告示・通知を確認してください
) : isSearching ? (
資料をスキャンしています…
令和8年度改定のナレッジから回答を構成中
) : (
Knowledge Base Ready
アップロードされた7つの資料に基づいて、具体的な算定要件や疑義解釈について回答できます。
)}
);
};
export default App;
コメント