-
Notifications
You must be signed in to change notification settings - Fork 59
gemini #31
Description
`import React, { useState, useEffect } from 'react';
import {
Lightbulb, BookOpen, Users, FileText, Target, Database, Loader2,
ArrowRight, Layers, FileCheck, Sparkles, ArrowRightCircle,
ShieldAlert, Landmark, BookMarked, ClipboardList, BookText,
History, Trash2
} from 'lucide-react';
import { initializeApp } from 'firebase/app';
import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth';
import { getFirestore, collection, addDoc, onSnapshot, deleteDoc, doc } from 'firebase/firestore';
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id';
export default function App() {
const [keyword, setKeyword] = useState('');
const [category, setCategory] = useState('การเมือง');
const [researchType, setResearchType] = useState('เชิงปริมาณ');
const [searchResults, setSearchResults] = useState([]);
const [isSearching, setIsSearching] = useState(false);
const [hasSearched, setHasSearched] = useState(false);
const [searchError, setSearchError] = useState('');
const [aiLoading, setAiLoading] = useState(false);
const [aiResult, setAiResult] = useState(null);
const [aiError, setAiError] = useState('');
const [draftLoading, setDraftLoading] = useState(false);
const [fullDraftResult, setFullDraftResult] = useState(null);
const [draftError, setDraftError] = useState('');
const [user, setUser] = useState(null);
const [history, setHistory] = useState([]);
// ตั้งค่าการยืนยันตัวตนแบบไม่ระบุตัวตน (เพื่อแยกข้อมูลของแต่ละเครื่อง)
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 (error) {
console.error("Auth error:", error);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
// ดึงข้อมูลประวัติการค้นหาจากฐานข้อมูลคลาวด์ส่วนตัวของผู้ใช้
useEffect(() => {
if (!user) return;
const historyRef = collection(db, 'artifacts', appId, 'users', user.uid, 'searchHistory');
const unsubscribe = onSnapshot(historyRef, (snapshot) => {
const historyData = [];
snapshot.forEach((doc) => {
historyData.push({ id: doc.id, ...doc.data() });
});
// เรียงลำดับจากใหม่ไปเก่า และแสดงแค่ 5 รายการล่าสุด
historyData.sort((a, b) => b.createdAt - a.createdAt);
setHistory(historyData.slice(0, 5));
}, (error) => {
console.error("Firestore error:", error);
});
return () => unsubscribe();
}, [user]);
const saveToHistory = async (kw, cat, type) => {
if (!user) return;
try {
const historyRef = collection(db, 'artifacts', appId, 'users', user.uid, 'searchHistory');
// ลบคำค้นหาเดิมออกถ้ามีซ้ำ เพื่อให้ขยับขึ้นมาอยู่บนสุด
const existing = history.find(h => h.keyword.toLowerCase() === kw.toLowerCase());
if (existing) {
await deleteDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'searchHistory', existing.id));
}
// บันทึกคำค้นหาใหม่
await addDoc(historyRef, {
keyword: kw,
category: cat,
researchType: type,
createdAt: Date.now()
});
} catch (error) {
console.error("Error saving history:", error);
}
};
const applyHistoryItem = (item) => {
setKeyword(item.keyword);
setCategory(item.category);
setResearchType(item.researchType);
};
const handleClearHistory = async () => {
if (!user) return;
try {
for (const item of history) {
await deleteDoc(doc(db, 'artifacts', appId, 'users', user.uid, 'searchHistory', item.id));
}
} catch (error) {
console.error("Error clearing history:", error);
}
};
const apiKey = "";
const apiEndpoint = https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey};
const fetchWithRetry = async (payload, maxRetries = 5) => {
const delays = [1000, 2000, 4000, 8000, 16000];
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(apiEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (response.ok) return await response.json();
if (i < maxRetries - 1) await new Promise(resolve => setTimeout(resolve, delays[i]));
} catch (networkError) {
if (i === maxRetries - 1) throw networkError;
await new Promise(resolve => setTimeout(resolve, delays[i]));
}
}
throw new Error('ขออภัย เซิร์ฟเวอร์ AI หนาแน่นหรือไม่ตอบสนอง โปรดลองใหม่อีกครั้ง');
};
const handleGenerateIdeas = async (e) => {
if (e) e.preventDefault();
if (!keyword.trim()) return;
setIsSearching(true);
setHasSearched(true);
setSearchResults([]);
setSearchError('');
setAiResult(null);
setFullDraftResult(null);
const prompt = `ในฐานะนักวิชาการระดับสูง จงคิดค้นไอเดีย "หัวข้อวิจัยที่น่าสนใจ ทันสมัย และมีโอกาสนำไปใช้ประโยชน์ได้จริงเชิงวิชาการ" จำนวน 5 หัวข้อ ที่เจาะลึกเกี่ยวกับประเด็น "${keyword}" ในหมวดหมู่ "${category}" (รูปแบบการวิจัย: ${researchType}) พร้อมอธิบายจุดเด่นสั้นๆ ว่าทำไมหัวข้อนี้น่าทำในมุมมองวิชาการ`;
const payload = {
contents: [{ parts: [{ text: prompt }] }],
generationConfig: {
responseMimeType: "application/json",
responseSchema: {
type: "ARRAY",
items: {
type: "OBJECT",
properties: {
title: { type: "STRING", description: "ชื่อหัวข้อวิจัยที่สละสลวย น่าสนใจ ตามหลักวิชาการ" },
reason: { type: "STRING", description: "ความสำคัญของปัญหา/จุดเด่นเชิงวิชาการ (1-2 บรรทัด)" }
},
required: ["title", "reason"]
}
}
}
};
try {
const result = await fetchWithRetry(payload);
const textResponse = result.candidates?.[0]?.content?.parts?.[0]?.text;
if (textResponse) {
setSearchResults(JSON.parse(textResponse));
saveToHistory(keyword, category, researchType);
} else {
throw new Error('ไม่พบข้อมูลไอเดีย');
}
} catch (err) {
setSearchError(err.message || 'เกิดข้อผิดพลาดในการสร้างไอเดียหัวข้อ');
} finally {
setIsSearching(false);
}
};
const generateFullDraft = async (overrideKeyword = null) => {
const currentKeyword = (typeof overrideKeyword === 'string') ? overrideKeyword : keyword;
if (!currentKeyword.trim()) {
setAiError('กรุณาระบุคีย์เวิร์ดหรือเลือกหัวข้อที่สนใจก่อน');
return;
}
window.scrollTo({ top: 0, behavior: 'smooth' });
setAiLoading(true);
setAiError('');
setAiResult(null);
setFullDraftResult(null);
const prompt = `ในฐานะนักวิชาการและนักวิจัยมืออาชีพที่มีทักษะการเล่าเรื่อง (Storytelling) จงสังเคราะห์ "โครงร่างงานวิจัยเบื้องต้น" ที่เกี่ยวข้องกับหัวข้อ/ประเด็น: "${currentKeyword}" (หมวดหมู่: ${category}, รูปแบบการวิจัย: ${researchType})
ข้อกำหนดสำคัญ:
- Academic Tone: ใช้ภาษาที่เป็นทางการ สละสลวย
- Research Design Alignment: เนื้อหาต้องสอดคล้องกับรูปแบบการวิจัย (${researchType})
โดยต้องมีองค์ประกอบดังนี้:
1. ชื่อเรื่องหลัก
2. ตัวอย่างหัวข้อวิจัยทางเลือก (3-5 หัวข้อ)
3. ความเป็นมาและความสำคัญของปัญหา
4. วัตถุประสงค์การวิจัย (เป็นข้อๆ)
5. แนวคิดและทฤษฎีที่ใช้ (2-3 ทฤษฎี)
6. กรอบแนวคิดในการวิจัย
7. ระเบียบวิธีวิจัย (วิธีวิทยา, ประชากร/กลุ่มตัวอย่าง, เครื่องมือที่ใช้)
8. การประเมินความเป็นไปได้`;
const payload = {
contents: [{ parts: [{ text: prompt }] }],
generationConfig: {
responseMimeType: "application/json",
responseSchema: {
type: "OBJECT",
properties: {
topic: { type: "STRING" },
suggested_topics: { type: "ARRAY", items: { type: "STRING" } },
background: { type: "STRING" },
objectives: { type: "ARRAY", items: { type: "STRING" } },
theories: { type: "ARRAY", items: { type: "OBJECT", properties: { name: { type: "STRING" }, description: { type: "STRING" } } } },
conceptual_framework: { type: "OBJECT", properties: { independent_variables: { type: "ARRAY", items: { type: "STRING" } }, dependent_variables: { type: "ARRAY", items: { type: "STRING" } } } },
methodology: { type: "STRING" },
target_group: { type: "STRING" },
instruments: { type: "STRING" },
feasibility: { type: "STRING" }
},
required: ["topic", "suggested_topics", "background", "objectives", "theories", "conceptual_framework", "methodology", "target_group", "instruments", "feasibility"]
}
}
};
try {
const result = await fetchWithRetry(payload);
const textResponse = result.candidates?.[0]?.content?.parts?.[0]?.text;
if (textResponse) {
setAiResult(JSON.parse(textResponse));
} else {
throw new Error('ไม่ได้รับข้อมูลเนื้อหาจาก AI');
}
} catch (err) {
setAiError(err.message || 'เกิดข้อผิดพลาดในการเชื่อมต่อกับระบบ AI');
} finally {
setAiLoading(false);
}
};
const handleSelectTopicForDraft = (selectedTitle) => {
setKeyword(selectedTitle);
generateFullDraft(selectedTitle);
};
const generateFullProposal = async () => {
if (!aiResult) return;
setDraftLoading(true);
setDraftError('');
setFullDraftResult(null);
setTimeout(() => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
}, 100);
const prompt = `ในฐานะนักวิชาการระดับสูง จงนำโครงร่างวิจัยต่อไปนี้มาขยายความและเขียนเป็น "เค้าโครงข้อเสนอโครงการวิจัยฉบับสมบูรณ์ (Research Proposal)" (บทที่ 1-3 และภาคผนวก) ด้วยภาษาทางวิชาการ (Academic Tone) ที่สละสลวยและถูกต้องตามระเบียบวิธีวิจัยรูปแบบ: ${researchType}
ข้อมูลตั้งต้น:
- ชื่อเรื่อง: ${aiResult.topic}
- วัตถุประสงค์: ${aiResult.objectives.join(', ')}
- ทฤษฎีที่ใช้: ${aiResult.theories.map(t => t.name).join(', ')}
- รูปแบบและเครื่องมือ: ${aiResult.methodology} / ${aiResult.instruments}
จงเขียนเนื้อหาแบ่งเป็น 4 ส่วนดังนี้:
1. บทที่ 1 บทนำ: ประกอบด้วย ความเป็นมาและความสำคัญของปัญหา (เกริ่นให้น่าสนใจ), วัตถุประสงค์การวิจัย, ขอบเขตการวิจัย (ด้านเนื้อหา พื้นที่ เวลา), และประโยชน์ที่คาดว่าจะได้รับ
2. บทที่ 2 แนวคิด ทฤษฎี และงานวิจัยที่เกี่ยวข้อง: ขยายความทฤษฎีที่ระบุไว้ให้เห็นภาพ และอธิบายว่าทฤษฎีเหล่านี้เชื่อมโยงสู่กรอบแนวคิดการวิจัยอย่างไร
3. บทที่ 3 ระเบียบวิธีวิจัย: อธิบายให้ชัดเจนถึง รูปแบบการวิจัย (${researchType}), ประชากรและกลุ่มตัวอย่าง (หรือผู้ให้ข้อมูลหลัก), การสร้างเครื่องมือวิจัย, การเก็บรวบรวมข้อมูล, และการวิเคราะห์ข้อมูล
4. ภาคผนวก (เครื่องมือวิจัย): ร่างโครงสร้าง "แบบสอบถาม" (ถ้าเป็นเชิงปริมาณ) หรือ "ร่างแนวคำถามสัมภาษณ์แบบกึ่งโครงสร้าง" (ถ้าเป็นเชิงคุณภาพ) โดยให้มีข้อคำถามที่สอดคล้องกับวัตถุประสงค์ อย่างน้อย 5-8 ข้อ`;
const payload = {
contents: [{ parts: [{ text: prompt }] }],
generationConfig: {
responseMimeType: "application/json",
responseSchema: {
type: "OBJECT",
properties: {
chapter1: { type: "STRING", description: "เนื้อหาบทที่ 1 บทนำ แบบครบถ้วนสมบูรณ์" },
chapter2: { type: "STRING", description: "เนื้อหาบทที่ 2 แนวคิด ทฤษฎี และงานวิจัยที่เกี่ยวข้อง" },
chapter3: { type: "STRING", description: "เนื้อหาบทที่ 3 ระเบียบวิธีวิจัย แบบละเอียด" },
appendix: { type: "STRING", description: "เนื้อหาภาคผนวก ร่างแบบสอบถาม หรือ แบบสัมภาษณ์" }
},
required: ["chapter1", "chapter2", "chapter3", "appendix"]
}
}
};
try {
const result = await fetchWithRetry(payload, 3);
const textResponse = result.candidates?.[0]?.content?.parts?.[0]?.text;
if (textResponse) {
setFullDraftResult(JSON.parse(textResponse));
} else {
throw new Error('ไม่ได้รับข้อมูลเอกสารจาก AI');
}
} catch (err) {
setDraftError(err.message || 'เกิดข้อผิดพลาดในการสร้างเอกสารบทที่ 1-3');
} finally {
setDraftLoading(false);
}
};
const indTitle = researchType === 'เชิงคุณภาพ' ? 'ปัจจัย/บริบท (Context/Factors)' : researchType === 'แบบผสมผสาน' ? 'ตัวแปร/ปัจจัย (Variables/Factors)' : 'ตัวแปรอิสระ (Independent Variables)';
const depTitle = researchType === 'เชิงคุณภาพ' ? 'ปรากฏการณ์ที่ศึกษา (Phenomenon)' : researchType === 'แบบผสมผสาน' ? 'ผลลัพธ์/ปรากฏการณ์ (Outcomes/Phenomena)' : 'ตัวแปรตาม (Dependent Variables)';
return (
<header className="bg-slate-900 text-white p-6 md:p-8 rounded-lg shadow-md border-b-4 border-amber-600 flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-4">
<div className="bg-slate-800 p-3 rounded-md border border-slate-700">
<Landmark className="w-8 h-8 text-amber-500" />
</div>
<div>
<h1 className="text-2xl md:text-3xl font-serif font-bold tracking-wide">
Smart Research Portal <span className="text-sm font-sans bg-slate-800 text-amber-500 px-2 py-1 rounded ml-2 border border-slate-700">v3.0</span>
</h1>
<p className="text-slate-300 mt-1 text-sm font-medium">ระบบสังเคราะห์และคัดสรรโครงร่างงานวิจัยอัจฉริยะ (Academic Edition)</p>
</div>
</div>
<div className="text-left md:text-right border-t md:border-t-0 border-slate-700 pt-4 md:pt-0 w-full md:w-auto">
<p className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-0.5">Designed By</p>
<p className="text-sm font-bold text-amber-500">บัณฑิตศึกษา สาขาวิชารัฐศาสตร์</p>
<p className="text-xs text-slate-300">มหาวิทยาลัยราชภัฏนครสวรรค์</p>
</div>
</header>
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
<div className="lg:col-span-4 space-y-6 sticky top-6">
<div className="bg-white p-6 rounded-lg shadow-sm border border-slate-300">
<div className="flex items-center gap-2 mb-5 text-slate-800">
<span className="bg-slate-800 text-white font-bold px-3 py-1 rounded-sm text-xs tracking-wider uppercase">Step 1</span>
<h2 className="text-lg font-serif font-bold flex items-center gap-2">
<Sparkles className="w-5 h-5 text-amber-600" />
กำหนดประเด็นที่สนใจ
</h2>
</div>
<form onSubmit={handleGenerateIdeas} className="space-y-4">
<div>
<label className="block text-sm font-bold text-slate-700 mb-1">หมวดหมู่งานวิจัย</label>
<select
value={category}
onChange={(e) => setCategory(e.target.value)}
className="w-full border-slate-300 rounded-md shadow-sm p-2.5 bg-slate-50 focus:ring-slate-800 focus:border-slate-800 border outline-none transition-all text-sm"
>
<option value="การเมือง">การเมือง (Politics)</option>
<option value="การบริหารภาครัฐ">การบริหารภาครัฐ (Public Administration)</option>
<option value="การปกครองท้องถิ่น">การปกครองท้องถิ่น (Local Government)</option>
<option value="การพัฒนาเมือง">การพัฒนาเมือง (Urban Development)</option>
<option value="รัฐธรรมนูญและสถาบันทางการเมือง">รัฐธรรมนูญและสถาบันทางการเมือง (Constitution & Political Institutions)</option>
<option value="การพัฒนาเศรษฐกิจและสังคม">การพัฒนาเศรษฐกิจและสังคม (Economic & Social Development)</option>
<option value="ความเหลื่อมล้ำทางสังคม">ความเหลื่อมล้ำทางสังคม (Social Inequality)</option>
<option value="การพัฒนาเชิงพื้นที่">การพัฒนาเชิงพื้นที่ (Spatial Development)</option>
</select>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-1">รูปแบบการวิจัย (Research Design)</label>
<select
value={researchType}
onChange={(e) => setResearchType(e.target.value)}
className="w-full border-slate-300 rounded-md shadow-sm p-2.5 bg-slate-50 focus:ring-slate-800 focus:border-slate-800 border outline-none transition-all text-sm"
>
<option value="เชิงปริมาณ">วิจัยเชิงปริมาณ (Quantitative Research)</option>
<option value="เชิงคุณภาพ">วิจัยเชิงคุณภาพ (Qualitative Research)</option>
<option value="แบบผสมผสาน">วิจัยแบบผสมผสาน (Mixed Methods)</option>
</select>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-1">ประเด็นกว้างๆ / คำค้นหา</label>
<input
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="เช่น นโยบายสาธารณะ, สมาร์ทซิตี้, ท้องถิ่น..."
className="w-full border-slate-300 rounded-md shadow-sm p-2.5 bg-slate-50 focus:ring-slate-800 focus:border-slate-800 border outline-none transition-all text-sm"
/>
</div>
<button
type="submit"
disabled={isSearching || !keyword.trim()}
className="w-full bg-slate-800 hover:bg-slate-700 text-white font-medium py-2.5 rounded-md transition-colors flex items-center justify-center gap-2 disabled:opacity-50 mt-2"
>
{isSearching ? <Loader2 className="w-5 h-5 animate-spin" /> : <Lightbulb className="w-5 h-5" />}
{isSearching ? 'กำลังประมวลผลวิชาการ...' : 'วิเคราะห์ 5 หัวข้อวิจัย'}
</button>
</form>
{/* ส่วนแสดงประวัติการค้นหา */}
{history.length > 0 && (
<div className="mt-8 pt-5 border-t border-slate-200 animate-in fade-in">
<div className="flex items-center justify-between mb-3">
<h3 className="text-xs font-bold text-slate-500 uppercase tracking-wider flex items-center gap-1.5">
<History className="w-4 h-4" /> ประวัติการค้นหาล่าสุด
</h3>
<button onClick={handleClearHistory} className="text-slate-400 hover:text-rose-500 transition-colors p-1" title="ลบประวัติ">
<Trash2 className="w-4 h-4" />
</button>
</div>
<div className="flex flex-col gap-2">
{history.map((item) => (
<button
key={item.id}
onClick={() => applyHistoryItem(item)}
className="text-left p-3 bg-slate-50 hover:bg-amber-50 hover:border-amber-200 border border-slate-200 rounded-md transition-all group flex flex-col gap-1"
>
<span className="text-sm font-bold text-slate-700 group-hover:text-amber-800 line-clamp-1">{item.keyword}</span>
<span className="text-[10px] text-slate-500 font-medium">{item.category} • {item.researchType}</span>
</button>
))}
</div>
</div>
)}
</div>
{hasSearched && (
<div className="bg-white p-6 rounded-lg shadow-sm border border-slate-300 animate-in fade-in slide-in-from-top-2">
<h3 className="text-md font-serif font-bold mb-4 text-slate-800 flex justify-between items-center border-b border-slate-200 pb-2">
<span>ข้อเสนอแนะหัวข้อวิจัย</span>
</h3>
{isSearching ? (
<div className="flex flex-col items-center justify-center py-10 text-slate-500">
<Loader2 className="w-8 h-8 animate-spin text-slate-800 mb-4" />
<p className="text-sm font-medium">กำลังทบทวนวรรณกรรม...</p>
</div>
) : searchError ? (
<div className="bg-red-50 text-red-700 p-4 rounded-md text-sm border border-red-200">{searchError}</div>
) : searchResults.length > 0 ? (
<div className="space-y-4 max-h-[500px] overflow-y-auto pr-2" style={{ scrollbarWidth: 'thin' }}>
{searchResults.map((item, index) => (
<div key={index} className="p-4 border border-slate-200 rounded-md hover:border-slate-400 hover:shadow-sm transition-all bg-slate-50 flex flex-col h-full group">
<h4 className="font-bold font-serif text-slate-900 text-sm leading-snug mb-2 group-hover:text-amber-700 transition-colors">{item.title}</h4>
<p className="text-xs text-slate-600 leading-relaxed mb-4 flex-grow"><span className="font-bold text-slate-800">นัยสำคัญ: </span>{item.reason}</p>
<button
onClick={() => handleSelectTopicForDraft(item.title)}
className="mt-auto w-full bg-white border border-slate-300 hover:bg-slate-800 hover:text-white text-slate-700 text-xs font-bold py-2 rounded-md transition-all flex items-center justify-center gap-1.5"
>
<ArrowRightCircle className="w-4 h-4" /> เลือกหัวข้อนี้ (Step 2)
</button>
</div>
))}
</div>
) : null}
</div>
)}
</div>
<div className="lg:col-span-8 space-y-6">
<div className="bg-slate-800 p-6 md:p-8 rounded-lg shadow-md text-white border-t-4 border-amber-600 relative overflow-hidden">
<div className="absolute top-0 right-0 p-8 opacity-5 pointer-events-none">
<BookOpen className="w-48 h-48" />
</div>
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-6">
<div className="flex-1">
<div className="flex items-center gap-2 mb-3 text-amber-500">
<span className="bg-amber-500/10 text-amber-500 font-bold px-3 py-1 rounded-sm text-xs border border-amber-500/20 uppercase tracking-wider">Step 2</span>
</div>
<h2 className="text-2xl font-serif font-bold flex items-center gap-3 mb-2">
สังเคราะห์โครงร่างงานวิจัยเบื้องต้น
</h2>
<p className="text-slate-300 text-sm md:text-base max-w-xl leading-relaxed">
ระบบจะทำการวางโครงสร้างงานวิจัย (Background, Theories, Conceptual Framework) โดยอัตโนมัติ
</p>
</div>
<button
onClick={() => generateFullDraft()}
disabled={aiLoading || !keyword.trim()}
className="bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-6 rounded-md transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 w-full md:w-auto shrink-0 h-fit"
>
{aiLoading ? <><Loader2 className="w-5 h-5 animate-spin" /> กำลังสร้างโครงร่าง...</> : <><FileCheck className="w-5 h-5" /> ร่างกรอบแนวคิด</>}
</button>
</div>
{aiError && <p className="text-rose-200 text-sm mt-4 bg-rose-900/50 inline-block px-4 py-2 rounded-md border border-rose-800">{aiError}</p>}
</div>
{aiResult && (
<div className="bg-white rounded-lg shadow-sm border border-slate-300 overflow-hidden animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="bg-slate-50 px-6 md:px-8 py-8 border-b border-slate-300">
<div className="flex items-center gap-2 text-sm font-bold text-slate-500 uppercase tracking-widest mb-4">
<Layers className="w-4 h-4" />
โครงร่างข้อเสนอโครงการวิจัย (Framework)
</div>
<h1 className="text-2xl md:text-3xl font-serif font-bold text-slate-900 leading-tight">{aiResult.topic}</h1>
</div>
<div className="p-6 md:p-8 space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-3">
<h3 className="text-lg font-serif font-bold text-slate-900 border-b-2 border-slate-800 pb-2">ความเป็นมาของปัญหา</h3>
<p className="text-slate-700 text-sm leading-relaxed text-justify whitespace-pre-line">{aiResult.background}</p>
</div>
<div className="space-y-3">
<h3 className="text-lg font-serif font-bold text-slate-900 border-b-2 border-slate-800 pb-2">วัตถุประสงค์การวิจัย</h3>
<ul className="space-y-2">
{aiResult.objectives?.map((obj, i) => (
<li key={i} className="flex items-start gap-2 text-sm text-slate-700">
<span className="w-4 h-4 rounded-full bg-slate-200 text-slate-800 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">{i+1}</span>
<span className="leading-relaxed">{obj}</span>
</li>
))}
</ul>
</div>
</div>
<div className="space-y-4">
<h3 className="text-lg font-serif font-bold text-slate-900 flex items-center gap-2 border-b-2 border-slate-800 pb-2">
<Layers className="w-5 h-5 text-slate-700" />
กรอบแนวคิดในการวิจัย (Conceptual Framework)
</h3>
<div className="bg-white p-6 rounded-lg border border-slate-200 flex flex-col md:flex-row items-center justify-center gap-6 md:gap-12 bg-slate-50/50">
<div className="bg-white rounded-md border border-slate-300 shadow-sm w-full md:w-64 overflow-hidden">
<h4 className="text-center font-bold text-slate-800 text-sm bg-slate-100 py-2 border-b border-slate-300">{indTitle}</h4>
<ul className="p-4 space-y-2">
{(aiResult.conceptual_framework?.independent_variables || []).map((v, i) => (
<li key={i} className="text-sm text-slate-700 flex items-start gap-2"><div className="w-1.5 h-1.5 rounded-sm bg-slate-800 mt-1.5 shrink-0"></div><span className="leading-snug">{v}</span></li>
))}
</ul>
</div>
<ArrowRight className="w-8 h-8 text-slate-400 hidden md:block" />
<div className="bg-white rounded-md border border-slate-300 shadow-sm w-full md:w-64 overflow-hidden">
<h4 className="text-center font-bold text-slate-800 text-sm bg-slate-100 py-2 border-b border-slate-300">{depTitle}</h4>
<ul className="p-4 space-y-2">
{(aiResult.conceptual_framework?.dependent_variables || []).map((v, i) => (
<li key={i} className="text-sm text-slate-700 flex items-start gap-2"><div className="w-1.5 h-1.5 rounded-sm bg-slate-800 mt-1.5 shrink-0"></div><span className="leading-snug">{v}</span></li>
))}
</ul>
</div>
</div>
</div>
</div>
</div>
)}
{aiResult && (
<div className="bg-amber-50 p-6 md:p-8 rounded-lg shadow-sm border border-amber-200 relative overflow-hidden mt-6">
<div className="relative z-10 flex flex-col md:flex-row md:items-center justify-between gap-6">
<div className="flex-1">
<div className="flex items-center gap-2 mb-3 text-amber-700">
<span className="bg-amber-200 text-amber-900 font-bold px-3 py-1 rounded-sm text-xs uppercase tracking-wider">Step 3</span>
</div>
<h2 className="text-2xl font-serif font-bold text-amber-900 flex items-center gap-3 mb-2">
เขียนเค้าโครงวิจัยฉบับจริง (บทที่ 1-3)
</h2>
<p className="text-amber-800 text-sm md:text-base max-w-xl leading-relaxed">
นำกรอบแนวคิดด้านบน มาแตกรายละเอียดเป็น <b>บทที่ 1 บทนำ, บทที่ 2 ทฤษฎี, บทที่ 3 ระเบียบวิธีวิจัย</b> พร้อมร่างแบบสอบถาม/คำถามสัมภาษณ์ใน <b>ภาคผนวก</b>
</p>
</div>
<button
onClick={() => generateFullProposal()}
disabled={draftLoading}
className="bg-amber-600 hover:bg-amber-700 text-white font-bold py-3 px-6 rounded-md transition-colors shadow-sm disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 w-full md:w-auto shrink-0 h-fit"
>
{draftLoading ? <><Loader2 className="w-5 h-5 animate-spin" /> กำลังเขียนบทที่ 1-3...</> : <><BookText className="w-5 h-5" /> สร้างเค้าโครงฉบับจริง (Step 3)</>}
</button>
</div>
{draftError && <p className="text-rose-600 text-sm mt-4 bg-rose-100 inline-block px-4 py-2 rounded-md border border-rose-200">{draftError}</p>}
</div>
)}
{fullDraftResult && (
<div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="bg-white rounded-lg shadow-sm border border-slate-300 overflow-hidden">
<div className="bg-slate-800 text-white px-6 py-4 flex items-center gap-3">
<BookMarked className="w-5 h-5 text-amber-500" />
<h3 className="font-serif font-bold text-lg">บทที่ 1: บทนำ</h3>
</div>
<div className="p-6 md:p-8">
<p className="text-slate-700 leading-loose text-justify whitespace-pre-line font-serif text-[15px]">
{fullDraftResult.chapter1}
</p>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm border border-slate-300 overflow-hidden">
<div className="bg-slate-800 text-white px-6 py-4 flex items-center gap-3">
<BookMarked className="w-5 h-5 text-amber-500" />
<h3 className="font-serif font-bold text-lg">บทที่ 2: แนวคิด ทฤษฎี และงานวิจัยที่เกี่ยวข้อง</h3>
</div>
<div className="p-6 md:p-8">
<p className="text-slate-700 leading-loose text-justify whitespace-pre-line font-serif text-[15px]">
{fullDraftResult.chapter2}
</p>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm border border-slate-300 overflow-hidden">
<div className="bg-slate-800 text-white px-6 py-4 flex items-center gap-3">
<BookMarked className="w-5 h-5 text-amber-500" />
<h3 className="font-serif font-bold text-lg">บทที่ 3: ระเบียบวิธีวิจัย</h3>
</div>
<div className="p-6 md:p-8">
<p className="text-slate-700 leading-loose text-justify whitespace-pre-line font-serif text-[15px]">
{fullDraftResult.chapter3}
</p>
</div>
</div>
<div className="bg-white rounded-lg shadow-sm border border-amber-300 overflow-hidden">
<div className="bg-amber-600 text-white px-6 py-4 flex items-center gap-3">
<ClipboardList className="w-5 h-5 text-amber-100" />
<h3 className="font-serif font-bold text-lg">ภาคผนวก: เครื่องมือการวิจัย (ร่างแบบสอบถาม/สัมภาษณ์)</h3>
</div>
<div className="p-6 md:p-8 bg-amber-50/30">
<p className="text-slate-800 leading-loose text-justify whitespace-pre-line font-serif text-[15px]">
{fullDraftResult.appendix}
</p>
</div>
</div>
</div>
)}
</div>
</div>
</div>
</div>
);
}`