Reuben_OS / frontend-fetch-example.js
Reubencf's picture
TESTING SESSIONS AND QUIZES
e9d7b34
raw
history blame
11.3 kB
// frontend-fetch-example.js
// Frontend code snippet for Reuben OS to fetch and display session files
// React Component Example for Session Management
import React, { useState, useEffect, useCallback } from 'react';
// Configuration
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000';
const POLL_INTERVAL = 5000; // Poll every 5 seconds
// ===== SESSION MANAGER COMPONENT =====
export function SessionManager() {
const [sessionId, setSessionId] = useState('');
const [files, setFiles] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [quizDetected, setQuizDetected] = useState(false);
// Generate a new session ID on component mount
useEffect(() => {
const generateSessionId = () => {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 9);
return `session_${timestamp}_${random}`;
};
// Check if session ID exists in localStorage, otherwise generate new one
const storedSessionId = localStorage.getItem('reubenOSSessionId');
if (storedSessionId) {
setSessionId(storedSessionId);
} else {
const newSessionId = generateSessionId();
setSessionId(newSessionId);
localStorage.setItem('reubenOSSessionId', newSessionId);
}
}, []);
// Fetch files for the current session
const fetchFiles = useCallback(async () => {
if (!sessionId) return;
setLoading(true);
setError(null);
try {
const response = await fetch(`${API_URL}/api/mcp-handler?sessionId=${sessionId}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.success) {
setFiles(data.files || []);
// Check if quiz.json exists
const hasQuiz = data.files.some(file => file.name === 'quiz.json');
setQuizDetected(hasQuiz);
} else {
setError(data.error || 'Failed to fetch files');
}
} catch (err) {
console.error('Error fetching files:', err);
setError(err.message);
} finally {
setLoading(false);
}
}, [sessionId]);
// Set up polling
useEffect(() => {
if (!sessionId) return;
// Initial fetch
fetchFiles();
// Set up polling interval
const interval = setInterval(fetchFiles, POLL_INTERVAL);
return () => clearInterval(interval);
}, [sessionId, fetchFiles]);
// Handle manual refresh
const handleRefresh = () => {
fetchFiles();
};
// Copy session ID to clipboard
const copySessionId = () => {
navigator.clipboard.writeText(sessionId);
alert('Session ID copied to clipboard!');
};
return (
<div className="session-manager">
<div className="session-header">
<h2>Session Manager</h2>
<div className="session-info">
<span>Session ID: {sessionId}</span>
<button onClick={copySessionId}>Copy ID</button>
</div>
<button onClick={handleRefresh} disabled={loading}>
{loading ? 'Loading...' : 'Refresh'}
</button>
</div>
{error && (
<div className="error-message">
Error: {error}
</div>
)}
{quizDetected && (
<div className="quiz-alert">
🎯 Quiz Detected! Click to launch Quiz App
<button onClick={() => launchQuizApp(sessionId)}>
Launch Quiz
</button>
</div>
)}
<div className="files-list">
<h3>Files ({files.length})</h3>
{files.length === 0 ? (
<p>No files yet. Use Claude to save files to this session.</p>
) : (
<ul>
{files.map((file, index) => (
<FileItem key={index} file={file} sessionId={sessionId} />
))}
</ul>
)}
</div>
</div>
);
}
// ===== FILE ITEM COMPONENT =====
function FileItem({ file, sessionId }) {
const [showContent, setShowContent] = useState(false);
const handleDownload = () => {
// Create a download link
const blob = new Blob([file.content || ''], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = file.name;
a.click();
URL.revokeObjectURL(url);
};
const getFileIcon = (fileName) => {
if (fileName.endsWith('.dart')) return '🎯';
if (fileName.endsWith('.tex')) return '📜';
if (fileName.endsWith('.json')) return '📋';
if (fileName.endsWith('.js') || fileName.endsWith('.ts')) return '📝';
if (fileName.endsWith('.py')) return '🐍';
return '📄';
};
return (
<li className="file-item">
<div className="file-header">
<span className="file-icon">{getFileIcon(file.name)}</span>
<span className="file-name">{file.name}</span>
<span className="file-size">({(file.size / 1024).toFixed(2)} KB)</span>
<button onClick={() => setShowContent(!showContent)}>
{showContent ? 'Hide' : 'Show'}
</button>
<button onClick={handleDownload}>Download</button>
</div>
{showContent && file.content && (
<div className="file-content">
<pre>{file.content.substring(0, 500)}</pre>
{file.content.length > 500 && <p>... (truncated)</p>}
</div>
)}
</li>
);
}
// ===== QUIZ APP LAUNCHER =====
function launchQuizApp(sessionId) {
// This function would navigate to your Quiz app with the session ID
window.location.href = `/apps/quiz?sessionId=${sessionId}`;
}
// ===== UTILITY FUNCTIONS =====
// Function to save a file from the frontend (for testing)
export async function saveFile(sessionId, fileName, content) {
try {
const response = await fetch(`${API_URL}/api/mcp-handler`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sessionId,
action: 'save_file',
fileName,
content,
}),
});
const data = await response.json();
return data;
} catch (error) {
console.error('Error saving file:', error);
throw error;
}
}
// Function to clear all files for a session
export async function clearSession(sessionId) {
try {
const response = await fetch(`${API_URL}/api/mcp-handler`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
sessionId,
action: 'clear_session',
fileName: '',
content: '',
}),
});
const data = await response.json();
return data;
} catch (error) {
console.error('Error clearing session:', error);
throw error;
}
}
// ===== FLUTTER APP COMPONENT =====
export function FlutterAppViewer({ sessionId }) {
const [dartFiles, setDartFiles] = useState([]);
useEffect(() => {
const fetchDartFiles = async () => {
try {
const response = await fetch(`${API_URL}/api/mcp-handler?sessionId=${sessionId}`);
const data = await response.json();
if (data.success) {
// Filter only .dart files
const dartOnly = data.files.filter(f => f.name.endsWith('.dart'));
setDartFiles(dartOnly);
}
} catch (error) {
console.error('Error fetching Dart files:', error);
}
};
if (sessionId) {
fetchDartFiles();
const interval = setInterval(fetchDartFiles, POLL_INTERVAL);
return () => clearInterval(interval);
}
}, [sessionId]);
const openInZapp = (file) => {
// Open Dart code in Zapp runner
const zappUrl = 'https://zapp.run/';
const code = encodeURIComponent(file.content);
window.open(`${zappUrl}?code=${code}`, '_blank');
};
return (
<div className="flutter-viewer">
<h3>Flutter/Dart Files</h3>
{dartFiles.map((file, index) => (
<div key={index} className="dart-file">
<span>{file.name}</span>
<button onClick={() => openInZapp(file)}>
Open in Zapp
</button>
</div>
))}
</div>
);
}
// ===== LATEX VIEWER COMPONENT =====
export function LaTeXViewer({ sessionId }) {
const [texFiles, setTexFiles] = useState([]);
useEffect(() => {
const fetchTexFiles = async () => {
try {
const response = await fetch(`${API_URL}/api/mcp-handler?sessionId=${sessionId}`);
const data = await response.json();
if (data.success) {
// Filter only .tex files
const texOnly = data.files.filter(f => f.name.endsWith('.tex'));
setTexFiles(texOnly);
}
} catch (error) {
console.error('Error fetching LaTeX files:', error);
}
};
if (sessionId) {
fetchTexFiles();
const interval = setInterval(fetchTexFiles, POLL_INTERVAL);
return () => clearInterval(interval);
}
}, [sessionId]);
return (
<div className="latex-viewer">
<h3>LaTeX Documents</h3>
{texFiles.map((file, index) => (
<div key={index} className="tex-file">
<span>{file.name}</span>
<button onClick={() => openInLatexEditor(file)}>
Open in Editor
</button>
</div>
))}
</div>
);
}
function openInLatexEditor(file) {
// Your LaTeX editor integration
window.location.href = `/apps/latex-studio?file=${file.name}`;
}
// ===== EXAMPLE USAGE IN NEXT.JS PAGE =====
/*
// pages/index.js or pages/dashboard.js
import { SessionManager } from '../components/SessionManager';
export default function Dashboard() {
return (
<div className="dashboard">
<h1>Reuben OS - File Manager</h1>
<SessionManager />
</div>
);
}
*/
// ===== CSS STYLES (add to your global styles or styled-components) =====
const styles = `
.session-manager {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.session-header {
background: #f5f5f5;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.session-info {
display: flex;
align-items: center;
gap: 10px;
margin: 10px 0;
font-family: monospace;
background: white;
padding: 10px;
border-radius: 4px;
}
.error-message {
background: #fee;
color: #c00;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.quiz-alert {
background: #efe;
color: #060;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.files-list {
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.file-item {
list-style: none;
padding: 10px;
border-bottom: 1px solid #eee;
}
.file-header {
display: flex;
align-items: center;
gap: 10px;
}
.file-icon {
font-size: 20px;
}
.file-name {
flex: 1;
font-weight: 500;
}
.file-size {
color: #666;
font-size: 0.9em;
}
.file-content {
margin-top: 10px;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
overflow-x: auto;
}
.file-content pre {
margin: 0;
font-size: 0.9em;
line-height: 1.4;
}
button {
padding: 6px 12px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
`;