<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Google Fonts: Tajawal -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;700;900&display=swap" rel="stylesheet">
<!-- Lucide Icons & React -->
<script src="https://unpkg.com/lucide-react@latest/dist/lucide-react.js"></script>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Recharts for Charts -->
<script src="https://unpkg.com/recharts/umd/Recharts.min.js"></script>
<!-- Firebase SDKs -->
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js";
window.FirebaseApp = { initializeApp };
import { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js";
window.FirebaseAuth = { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged };
import { getFirestore, collection, doc, addDoc, deleteDoc, onSnapshot, query, serverTimestamp } from "https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js";
window.FirebaseFirestore = { getFirestore, collection, doc, addDoc, deleteDoc, onSnapshot, query, serverTimestamp };
</script>
<style>
:root {
--font-family: 'Tajawal', sans-serif;
--gradient: linear-gradient(135deg, #4f46e5, #a855f7, #ec4899);
}
html[data-theme="dark"] {
--bg-color: #0a192f;
--text-color: #f5f8ff;
--slate-color: #a0aec0;
--card-bg: rgba(25, 42, 86, 0.5);
--border-color: rgba(255, 255, 255, 0.1);
--primary-color: #58a6ff;
--input-bg: #020617;
}
html[data-theme="light"] {
--bg-color: #f0f2f5;
--text-color: #1c1e21;
--slate-color: #4a5568;
--card-bg: rgba(255, 255, 255, 1);
--border-color: rgba(0, 0, 0, 0.1);
--primary-color: #1a73e8;
--input-bg: #e2e8f0;
}
body { font-family: var(--font-family); background-color: var(--bg-color); color: var(--text-color); transition: background-color 0.3s, color 0.3s; }
.gradient-text { background: var(--gradient); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.recharts-text, .recharts-legend-item-text { fill: var(--slate-color) !important; font-family: 'Tajawal', sans-serif; }
.loading-dots span { animation: blink 1.4s infinite both; }
.loading-dots span:nth-child(2) { animation-delay: .2s; }
.loading-dots span:nth-child(3) { animation-delay: .4s; }
@keyframes blink { 0% { opacity: .2; } 20% { opacity: 1; } 100% { opacity: .2; } }
</style>
<script type="text/babel">
const { useState, useEffect, useCallback, useRef } = React;
const { initializeApp } = window.FirebaseApp;
const { getAuth, signInAnonymously, signInWithCustomToken, onAuthStateChanged } = window.FirebaseAuth;
const { getFirestore, collection, doc, addDoc, deleteDoc, onSnapshot, query, serverTimestamp } = window.FirebaseFirestore;
const { BarChart, Bar, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer } = window.Recharts;
// --- GLOBAL CONFIG & HELPERS ---
const API_KEY = ""; // Provided by the environment
const Icon = ({ name, ...props }) => {
const LucideIcon = lucide[name];
return LucideIcon ? <LucideIcon {...props} /> : null;
};
// --- COMPONENTS ---
const ProjectModal = ({ project, isOpen, onClose, onChat, onGenerateTasks, isChatting, isGeneratingTasks }) => {
if (!isOpen) return null;
const [activeTab, setActiveTab] = useState('details');
const chatEndRef = useRef(null);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [project.chatHistory]);
const TabButton = ({ id, label, icon }) => (
<button
onClick={() => setActiveTab(id)}
className={`flex items-center gap-2 px-4 py-2 text-sm font-bold rounded-t-lg border-b-2 ${activeTab === id ? 'border-[var(--primary-color)] text-[var(--primary-color)]' : 'border-transparent text-[var(--slate-color)] hover:text-[var(--text-color)]'}`}
>
<Icon name={icon} size={16}/> {label}
</button>
);
return (
<div className="fixed inset-0 bg-black/70 backdrop-blur-sm z-40 flex items-center justify-center p-4" onClick={onClose}>
<div className="bg-[var(--bg-color)] border border-[var(--border-color)] rounded-2xl max-w-4xl w-full h-[90vh] flex flex-col" onClick={e => e.stopPropagation()}>
<div className="flex justify-between items-center p-4 md:p-6 border-b border-[var(--border-color)]">
<h2 className="text-xl md:text-2xl font-bold gradient-text">{project.projectName}</h2>
<button onClick={onClose} className="p-2 rounded-full hover:bg-white/10"><Icon name="X"/></button>
</div>
<div className="border-b border-[var(--border-color)] px-4">
<div className="flex items-center -mb-px">
<TabButton id="details" label="التفاصيل" icon="Info"/>
<TabButton id="tasks" label="خطة العمل" icon="ListChecks"/>
<TabButton id="chat" label="مستشار AI" icon="Bot"/>
</div>
</div>
<div className="flex-grow overflow-y-auto p-4 md:p-6">
{activeTab === 'details' && (
<div className="space-y-6">
<p className="text-[var(--slate-color)]">{project.description}</p>
<div>
<h3 className="font-bold mb-3">الميزات الرئيسية</h3>
<ul className="list-disc pr-5 space-y-2 text-sm text-[var(--slate-color)]">
{project.keyFeatures.map((f, i) => <li key={i}>{f}</li>)}
</ul>
</div>
<div>
<h3 className="font-bold mb-3">التقنيات المقترحة</h3>
<div className="flex flex-wrap gap-2">
{project.techStack.map((t, i) => <span key={i} className="bg-blue-500/10 text-blue-400 text-xs font-bold px-3 py-1.5 rounded-full">{t}</span>)}
</div>
</div>
<div>
<h3 className="font-bold mb-3">طرق تحقيق الدخل</h3>
<ul className="list-disc pr-5 space-y-2 text-sm text-[var(--slate-color)]">
{project.monetization.map((m, i) => <li key={i}>{m}</li>)}
</ul>
</div>
</div>
)}
{activeTab === 'tasks' && (
<div>
{project.tasks && project.tasks.phases ? (
<div className="space-y-6">
<div className="w-full h-64 mb-6">
<ResponsiveContainer width="100%" height="100%">
<BarChart data={project.tasks.phases.map(p => ({name: p.phaseName, tasks: p.tasks.length}))}>
<XAxis dataKey="name" stroke="var(--slate-color)" fontSize={12} />
<YAxis stroke="var(--slate-color)" fontSize={12} allowDecimals={false} />
<Tooltip contentStyle={{backgroundColor: "var(--card-bg)", border: "1px solid var(--border-color)", borderRadius: "10px"}}/>
<Bar dataKey="tasks" fill="var(--primary-color)" name="عدد المهام" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
{project.tasks.phases.map((phase, i) => (
<div key={i}>
<h4 className="font-bold text-lg mb-3 text-[var(--primary-color)]">{phase.phaseName}</h4>
<ul className="list-decimal pr-6 space-y-2 text-sm">
{phase.tasks.map((task, j) => <li key={j} className="text-[var(--slate-color)]">{task}</li>)}
</ul>
</div>
))}
</div>
) : (
<div className="text-center py-10">
<Icon name="ListTodo" size={48} className="mx-auto text-[var(--slate-color)] mb-4"/>
<h3 className="font-bold text-lg mb-2">تخطيط المهام</h3>
<p className="text-[var(--slate-color)] mb-4">قم بتقسيم مشروعك إلى مهام قابلة للتنفيذ.</p>
<button onClick={() => onGenerateTasks(project)} disabled={isGeneratingTasks} className="inline-flex items-center gap-2 bg-[var(--primary-color)] text-white font-bold py-2 px-5 rounded-full transition-transform hover:scale-105 disabled:opacity-50">
{isGeneratingTasks ? 'جاري التخطيط...' : '✨ توليد خطة العمل'}
</button>
</div>
)}
</div>
)}
{activeTab === 'chat' && (
<div className="h-full flex flex-col">
<div className="flex-grow space-y-4 mb-4 overflow-y-auto pr-2">
{project.chatHistory && project.chatHistory.map((msg, i) => (
<div key={i} className={`flex items-end gap-2 ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}>
{msg.role === 'model' && <div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-500 flex items-center justify-center flex-shrink-0"><Icon name="Bot" size={18}/></div>}
<div className={`max-w-md p-3 rounded-2xl ${msg.role === 'user' ? 'bg-[var(--primary-color)] text-white rounded-br-none' : 'bg-white/10 rounded-bl-none'}`}>
<p className="text-sm">{msg.text}</p>
</div>
</div>
))}
{isChatting && (
<div className="flex items-end gap-2 justify-start">
<div className="w-8 h-8 rounded-full bg-gradient-to-br from-indigo-500 to-purple-500 flex items-center justify-center flex-shrink-0"><Icon name="Bot" size={18}/></div>
<div className="max-w-md p-3 rounded-2xl bg-white/10 rounded-bl-none">
<div className="loading-dots"><span className="w-2 h-2 bg-[var(--slate-color)] rounded-full inline-block"></span><span className="w-2 h-2 bg-[var(--slate-color)] rounded-full inline-block"></span><span className="w-2 h-2 bg-[var(--slate-color)] rounded-full inline-block"></span></div>
</div>
</div>
)}
<div ref={chatEndRef}></div>
</div>
<form onSubmit={e => { e.preventDefault(); onChat(project, e.target.message.value); e.target.message.value = ''; }} className="flex items-center gap-2 border-t border-[var(--border-color)] pt-4">
<input name="message" required disabled={isChatting} placeholder="اسأل أي شيء عن مشروعك..." className="w-full bg-[var(--input-bg)] text-[var(--text-color)] rounded-full py-2 px-4 outline-none focus:ring-2 focus:ring-[var(--primary-color)]"/>
<button type="submit" disabled={isChatting} className="bg-[var(--primary-color)] w-10 h-10 flex items-center justify-center rounded-full text-white flex-shrink-0 disabled:opacity-50"><Icon name="Send" size={18}/></button>
</form>
</div>
)}
</div>
</div>
</div>
);
};
function App() {
const [theme, setTheme] = useState('dark');
const [firebase, setFirebase] = useState({ db: null, auth: null });
const [userId, setUserId] = useState(null);
const [appId, setAppId] = useState('default-app-id');
const [isAuthReady, setIsAuthReady] = useState(false);
// State
const [terminalHistory, setTerminalHistory] = useState([{ type: 'system', text: 'مرحباً بك في واجهة Tota الذكية. ماذا يمكنني أن أبني لك اليوم؟' }]);
const [isGenerating, setIsGenerating] = useState(false);
const [isGeneratingTasks, setIsGeneratingTasks] = useState(false);
const [isChatting, setIsChatting] = useState(false);
const [projects, setProjects] = useState([]);
const [isLoadingProjects, setIsLoadingProjects] = useState(true);
const [selectedProject, setSelectedProject] = useState(null);
const [modalToDelete, setModalToDelete] = useState(null);
const terminalEndRef = useRef(null);
useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
useEffect(() => {
const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : null;
if (!firebaseConfig) return;
const app = initializeApp(firebaseConfig);
setFirebase({ db: getFirestore(app), auth: getAuth(app) });
setAppId(typeof __app_id !== 'undefined' ? __app_id : 'default-app-id');
onAuthStateChanged(getAuth(app), async (user) => {
if (user) setUserId(user.uid);
else {
try {
const token = typeof __initial_auth_token !== 'undefined' ? __initial_auth_token : null;
if (token) await signInWithCustomToken(getAuth(app), token);
else await signInAnonymously(getAuth(app));
} catch (error) { console.error("Auth Error:", error); }
}
setIsAuthReady(true);
});
}, []);
useEffect(() => {
if (!isAuthReady || !firebase.db || !userId) return;
setIsLoadingProjects(true);
const q = query(collection(firebase.db, 'artifacts', appId, 'users', userId, 'projects'));
const unsubscribe = onSnapshot(q, snapshot => {
const projectsData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
setProjects(projectsData.sort((a,b) => b.createdAt?.seconds - a.createdAt?.seconds));
setIsLoadingProjects(false);
});
return () => unsubscribe();
}, [isAuthReady, firebase.db, userId, appId]);
useEffect(() => terminalEndRef.current?.scrollIntoView({ behavior: 'smooth' }), [terminalHistory]);
const handleTerminalCommand = async (command) => {
setTerminalHistory(prev => [...prev, { type: 'user', text: command }]);
setIsGenerating(true);
const prompt = `بصفتك خبير في تخطيط المشاريع، قم بإنشاء خطة مشروع مفصلة للفكرة التالية: "${command}". يجب أن تكون الاستجابة على هيئة JSON فقط بدون أي نص إضافي. يجب أن يتضمن الـ JSON الحقول التالية: 'projectName' (اسم المشروع)، 'description' (وصف موجز من جملة واحدة)، 'keyFeatures' (مصفوفة من 3-5 ميزات رئيسية)، 'techStack' (مصفوفة من 3 تقنيات مقترحة)، 'monetization' (مصفوفة من طريقتين لتحقيق الدخل).`;
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }], generationConfig: { responseMimeType: "application/json" } };
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
const result = await response.json();
const projectData = JSON.parse(result.candidates[0].content.parts[0].text);
setTerminalHistory(prev => [...prev, { type: 'ai', data: projectData }]);
await addDoc(collection(firebase.db, 'artifacts', appId, 'users', userId, 'projects'), { ...projectData, createdAt: serverTimestamp() });
} catch (error) {
setTerminalHistory(prev => [...prev, { type: 'error', text: 'عذراً، حدث خطأ أثناء تحليل طلبك.' }]);
} finally { setIsGenerating(false); }
};
const handleGenerateTasks = async (project) => {
setIsGeneratingTasks(true);
const prompt = `بصفتك مدير مشاريع محترف، قم بتقسيم المشروع التالي إلى مراحل عمل قابلة للتنفيذ. لكل مرحلة، قدم قائمة بالمهام المحددة. المشروع: ${project.projectName}. الوصف: ${project.description}. الميزات الرئيسية: ${project.keyFeatures.join(', ')}. يجب أن تكون الاستجابة على هيئة JSON فقط، بالشكل التالي: { "phases": [ { "phaseName": "اسم المرحلة", "tasks": ["مهمة 1", "مهمة 2"] } ] }`;
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }], generationConfig: { responseMimeType: "application/json" } };
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) throw new Error('API Error');
const result = await response.json();
const tasksData = JSON.parse(result.candidates[0].content.parts[0].text);
const updatedProjects = projects.map(p => p.id === project.id ? { ...p, tasks: tasksData } : p);
setProjects(updatedProjects);
setSelectedProject(prev => ({...prev, tasks: tasksData}));
} catch (error) { console.error("Task Gen Error:", error); }
finally { setIsGeneratingTasks(false); }
};
const handleChat = async (project, message) => {
setIsChatting(true);
const currentHistory = project.chatHistory || [];
const updatedHistory = [...currentHistory, { role: 'user', text: message }];
setSelectedProject(prev => ({...prev, chatHistory: updatedHistory}));
const prompt = `أنت مستشار خبير للمشاريع. المستخدم يسأل عن مشروعه "${project.projectName}". هذا هو سجل المحادثة حتى الآن: ${JSON.stringify(currentHistory)}. سؤال المستخدم الجديد هو: "${message}". قدم إجابة مفيدة وموجزة.`;
const payload = { contents: [{ role: "user", parts: [{ text: prompt }] }] };
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
if (!response.ok) throw new Error('API Error');
const result = await response.json();
const aiResponse = result.candidates[0].content.parts[0].text;
const finalHistory = [...updatedHistory, { role: 'model', text: aiResponse }];
setSelectedProject(prev => ({...prev, chatHistory: finalHistory}));
} catch (error) {
console.error("Chat Error:", error);
const finalHistory = [...updatedHistory, { role: 'model', text: "عذراً، أواجه صعوبة في الاتصال الآن." }];
setSelectedProject(prev => ({...prev, chatHistory: finalHistory}));
}
finally { setIsChatting(false); }
};
const handleProjectDelete = async () => {
if (!modalToDelete) return;
await deleteDoc(doc(firebase.db, 'artifacts', appId, 'users', userId, 'projects', modalToDelete.id));
setModalToDelete(null);
};
// ... JSX rendering using components like ProjectModal ...
return (
<>
<header className="fixed w-full top-0 z-30 bg-[var(--card-bg)] backdrop-blur-lg border-b border-[var(--border-color)]">
<div className="container mx-auto px-4">
<nav className="flex justify-between items-center h-20">
<a href="#" className="text-2xl font-bold">Tota AI</a>
<div className="flex items-center gap-4">
<button onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')} aria-label="تبديل المظهر" className="text-xl w-10 h-10 flex items-center justify-center rounded-full hover:bg-white/10 transition-colors">
<Icon name={theme === 'dark' ? 'Sun' : 'Moon'}/>
</button>
</div>
</nav>
</div>
</header>
<main>
<section className="min-h-screen flex items-center text-center">
<div className="container mx-auto px-4">
<h1 className="text-4xl md:text-5xl font-extrabold leading-tight">
Tota AI: <span className="gradient-text">بوابتك الذكية لبناء المشاريع</span>
</h1>
<p className="text-lg md:text-xl max-w-3xl mx-auto my-6 text-[var(--slate-color)]">
حوّل أفكارك إلى خطط عمل متكاملة. أدخل فكرة مشروعك في الواجهة التفاعلية وشاهد السحر يحدث.
</p>
<a href="#playground" className="inline-block bg-[var(--primary-color)] text-white font-bold py-3 px-8 rounded-full transition-transform hover:scale-105">
جرب الآن <Icon name="ArrowDown" className="inline-block mr-2"/>
</a>
</div>
</section>
<section id="playground" className="py-24 bg-[var(--card-bg)]/30">
<div className="container mx-auto px-4">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold mb-4"><span className="gradient-text">الواجهة التفاعلية</span></h2>
<p className="max-w-2xl mx-auto text-[var(--slate-color)]">أعطِ أمرًا للتطبيق وشاهد كيف يحول أفكارك إلى خطط منظمة. جرب "موقع لبيع الزهور" أو "تطبيق للياقة البدنية".</p>
</div>
<div className="max-w-4xl mx-auto bg-black/80 rounded-xl border border-slate-700 shadow-2xl shadow-black/40">
<div className="h-9 bg-slate-800 flex items-center gap-2 px-4 rounded-t-xl"><div className="w-3 h-3 rounded-full bg-red-500"></div><div className="w-3 h-3 rounded-full bg-yellow-500"></div><div className="w-3 h-3 rounded-full bg-green-500"></div></div>
<div className="p-4 h-96 overflow-y-auto font-mono text-sm" >
{terminalHistory.map((item, index) => (
<div key={index} className="mb-2">
{item.type === 'user' && <div><span className="text-green-400">></span> <span className="text-white">{item.text}</span></div>}
{item.type === 'system' && <div className="text-blue-400">{item.text}</div>}
{item.type === 'error' && <div className="text-red-400">{item.text}</div>}
{item.type === 'ai' && <div className="text-blue-400">تم إنشاء خطة مشروع بنجاح: <span className="font-bold text-white underline">{item.data.projectName}</span>. يمكنك رؤية التفاصيل في قسم المشاريع بالأسفل.</div>}
</div>
))}
{isGenerating && <div className="text-slate-400">جاري التحليل والتخطيط...</div>}
<div ref={terminalEndRef}></div>
</div>
<div className="p-2 border-t border-slate-700">
<form onSubmit={(e) => { e.preventDefault(); handleTerminalCommand(e.target.command.value); e.target.command.value = ''; }}>
<input type="text" name="command" disabled={isGenerating || !isAuthReady} placeholder={isAuthReady ? "اكتب فكرة مشروعك هنا واضغط Enter..." : "جاري تهيئة الاتصال..."} className="w-full bg-transparent text-white outline-none font-mono text-sm p-2 disabled:opacity-50"/>
</form>
</div>
</div>
</div>
</section>
<section id="projects" className="py-24">
<div className="container mx-auto px-4">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold mb-4"><span className="gradient-text">مشاريعك المحفوظة</span></h2>
<p className="max-w-2xl mx-auto text-[var(--slate-color)]">هذه هي المشاريع التي تم إنشاؤها وحفظها في قاعدة بياناتك. اضغط على أي مشروع لعرض التفاصيل.</p>
</div>
{isLoadingProjects ? <p className="text-center">جاري تحميل المشاريع...</p> : (
projects.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{projects.map(p => (
<div key={p.id} className="bg-[var(--card-bg)] border border-[var(--border-color)] rounded-2xl p-6 flex flex-col justify-between group transition-all hover:border-[var(--primary-color)] hover:-translate-y-2">
<div>
<h3 className="text-xl font-bold mb-2">{p.projectName}</h3>
<p className="text-sm text-[var(--slate-color)] mb-4 line-clamp-2">{p.description}</p>
</div>
<div className="flex justify-between items-center">
<button onClick={() => setSelectedProject(p)} className="text-sm font-bold text-[var(--primary-color)]">عرض التفاصيل</button>
<button onClick={() => setModalToDelete(p)} className="text-red-500 opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded-md hover:bg-red-500/20"><Icon name="Trash2" size={16}/></button>
</div>
</div>
))}
</div>
) : (
<p className="text-center text-[var(--slate-color)]">لم تقم بإنشاء أي مشاريع بعد. جرب الواجهة التفاعلية!</p>
)
)}
</div>
</section>
</main>
<ProjectModal
isOpen={!!selectedProject}
onClose={() => setSelectedProject(null)}
project={selectedProject}
onGenerateTasks={handleGenerateTasks}
onChat={handleChat}
isGeneratingTasks={isGeneratingTasks}
isChatting={isChatting}
/>
<ConfirmationModal
isOpen={!!modalToDelete}
onClose={() => setModalToDelete(null)}
onConfirm={handleProjectDelete}
title="حذف المشروع">
هل أنت متأكد من رغبتك في حذف مشروع "{modalToDelete?.projectName}" بشكل نهائي؟
</ConfirmationModal>
</>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>