non-building checkpoint 1
This commit is contained in:
174
frontend/src/components/PromptDisplay.jsx
Normal file
174
frontend/src/components/PromptDisplay.jsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
const PromptDisplay = () => {
|
||||
const [prompts, setPrompts] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [selectedPrompt, setSelectedPrompt] = useState(null);
|
||||
|
||||
// Mock data for demonstration
|
||||
const mockPrompts = [
|
||||
"Write about a time when you felt completely at peace with yourself and the world around you. What were the circumstances that led to this feeling, and how did it change your perspective on life?",
|
||||
"Imagine you could have a conversation with your future self 10 years from now. What questions would you ask, and what advice do you think your future self would give you?",
|
||||
"Describe a place from your childhood that holds special meaning to you. What made this place so significant, and how does remembering it make you feel now?",
|
||||
"Write about a skill or hobby you've always wanted to learn but never had the chance to pursue. What has held you back, and what would be the first step to starting?",
|
||||
"Reflect on a mistake you made that ultimately led to personal growth. What did you learn from the experience, and how has it shaped who you are today?",
|
||||
"Imagine you wake up tomorrow with the ability to understand and speak every language in the world. How would this change your life, and what would you do with this newfound ability?"
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
setPrompts(mockPrompts);
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
}, []);
|
||||
|
||||
const handleSelectPrompt = (index) => {
|
||||
setSelectedPrompt(index);
|
||||
};
|
||||
|
||||
const handleDrawPrompts = async () => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
// TODO: Replace with actual API call
|
||||
// const response = await fetch('/api/v1/prompts/draw');
|
||||
// const data = await response.json();
|
||||
// setPrompts(data.prompts);
|
||||
|
||||
// For now, use mock data
|
||||
setTimeout(() => {
|
||||
setPrompts(mockPrompts);
|
||||
setSelectedPrompt(null);
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
} catch (err) {
|
||||
setError('Failed to draw prompts. Please try again.');
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddToHistory = async () => {
|
||||
if (selectedPrompt === null) {
|
||||
setError('Please select a prompt first');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO: Replace with actual API call
|
||||
// await fetch(`/api/v1/prompts/select/${selectedPrompt}`, { method: 'POST' });
|
||||
|
||||
// For now, just show success message
|
||||
alert(`Prompt ${selectedPrompt + 1} added to history!`);
|
||||
setSelectedPrompt(null);
|
||||
} catch (err) {
|
||||
setError('Failed to add prompt to history');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="text-center p-8">
|
||||
<div className="spinner mx-auto"></div>
|
||||
<p className="mt-4">Loading prompts...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="alert alert-error">
|
||||
<i className="fas fa-exclamation-circle mr-2"></i>
|
||||
{error}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{prompts.length === 0 ? (
|
||||
<div className="text-center p-8">
|
||||
<i className="fas fa-inbox fa-3x mb-4" style={{ color: 'var(--gray-color)' }}></i>
|
||||
<h3>No Prompts Available</h3>
|
||||
<p className="mb-4">The prompt pool is empty. Please fill the pool to get started.</p>
|
||||
<button className="btn btn-primary" onClick={handleDrawPrompts}>
|
||||
<i className="fas fa-plus"></i> Fill Pool First
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{prompts.map((prompt, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`prompt-card cursor-pointer ${selectedPrompt === index ? 'selected' : ''}`}
|
||||
onClick={() => handleSelectPrompt(index)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center ${selectedPrompt === index ? 'bg-green-100 text-green-600' : 'bg-blue-100 text-blue-600'}`}>
|
||||
{selectedPrompt === index ? (
|
||||
<i className="fas fa-check"></i>
|
||||
) : (
|
||||
<span>{index + 1}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<p className="prompt-text">{prompt}</p>
|
||||
<div className="prompt-meta">
|
||||
<span>
|
||||
<i className="fas fa-ruler-combined mr-1"></i>
|
||||
{prompt.length} characters
|
||||
</span>
|
||||
<span>
|
||||
{selectedPrompt === index ? (
|
||||
<span className="text-green-600">
|
||||
<i className="fas fa-check-circle mr-1"></i>
|
||||
Selected
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-500">
|
||||
Click to select
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center mt-6">
|
||||
<div>
|
||||
{selectedPrompt !== null && (
|
||||
<button className="btn btn-success" onClick={handleAddToHistory}>
|
||||
<i className="fas fa-history"></i> Add Prompt #{selectedPrompt + 1} to History
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button className="btn btn-secondary" onClick={handleDrawPrompts}>
|
||||
<i className="fas fa-redo"></i> Draw New Set
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={handleDrawPrompts}>
|
||||
<i className="fas fa-dice"></i> Draw 6 More
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 text-sm text-gray-600">
|
||||
<p>
|
||||
<i className="fas fa-info-circle mr-1"></i>
|
||||
Select a prompt by clicking on it, then add it to your history. The AI will use your history to generate more relevant prompts in the future.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PromptDisplay;
|
||||
|
||||
211
frontend/src/components/StatsDashboard.jsx
Normal file
211
frontend/src/components/StatsDashboard.jsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
const StatsDashboard = () => {
|
||||
const [stats, setStats] = useState({
|
||||
pool: {
|
||||
total: 0,
|
||||
target: 20,
|
||||
sessions: 0,
|
||||
needsRefill: true
|
||||
},
|
||||
history: {
|
||||
total: 0,
|
||||
capacity: 60,
|
||||
available: 60,
|
||||
isFull: false
|
||||
}
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// Simulate API calls
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
// TODO: Replace with actual API calls
|
||||
// const poolResponse = await fetch('/api/v1/prompts/stats');
|
||||
// const historyResponse = await fetch('/api/v1/prompts/history/stats');
|
||||
// const poolData = await poolResponse.json();
|
||||
// const historyData = await historyResponse.json();
|
||||
|
||||
// Mock data for demonstration
|
||||
setTimeout(() => {
|
||||
setStats({
|
||||
pool: {
|
||||
total: 15,
|
||||
target: 20,
|
||||
sessions: Math.floor(15 / 6),
|
||||
needsRefill: 15 < 20
|
||||
},
|
||||
history: {
|
||||
total: 8,
|
||||
capacity: 60,
|
||||
available: 52,
|
||||
isFull: false
|
||||
}
|
||||
});
|
||||
setLoading(false);
|
||||
}, 800);
|
||||
} catch (error) {
|
||||
console.error('Error fetching stats:', error);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchStats();
|
||||
}, []);
|
||||
|
||||
const handleFillPool = async () => {
|
||||
try {
|
||||
// TODO: Replace with actual API call
|
||||
// await fetch('/api/v1/prompts/fill-pool', { method: 'POST' });
|
||||
|
||||
// For now, update local state
|
||||
setStats(prev => ({
|
||||
...prev,
|
||||
pool: {
|
||||
...prev.pool,
|
||||
total: prev.pool.target,
|
||||
sessions: Math.floor(prev.pool.target / 6),
|
||||
needsRefill: false
|
||||
}
|
||||
}));
|
||||
|
||||
alert('Prompt pool filled successfully!');
|
||||
} catch (error) {
|
||||
alert('Failed to fill prompt pool');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="text-center p-4">
|
||||
<div className="spinner mx-auto"></div>
|
||||
<p className="mt-2 text-sm">Loading stats...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||
<div className="stats-card">
|
||||
<div className="p-3">
|
||||
<i className="fas fa-database fa-2x mb-2" style={{ color: 'var(--primary-color)' }}></i>
|
||||
<div className="stats-value">{stats.pool.total}</div>
|
||||
<div className="stats-label">Prompts in Pool</div>
|
||||
<div className="mt-2 text-sm">
|
||||
Target: {stats.pool.target}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stats-card">
|
||||
<div className="p-3">
|
||||
<i className="fas fa-history fa-2x mb-2" style={{ color: 'var(--secondary-color)' }}></i>
|
||||
<div className="stats-value">{stats.history.total}</div>
|
||||
<div className="stats-label">History Items</div>
|
||||
<div className="mt-2 text-sm">
|
||||
Capacity: {stats.history.capacity}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<span className="text-sm font-medium">Prompt Pool</span>
|
||||
<span className="text-sm">{stats.pool.total}/{stats.pool.target}</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${(stats.pool.total / stats.pool.target) * 100}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 mt-1">
|
||||
{stats.pool.needsRefill ? (
|
||||
<span className="text-orange-600">
|
||||
<i className="fas fa-exclamation-triangle mr-1"></i>
|
||||
Needs refill ({stats.pool.target - stats.pool.total} prompts needed)
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-green-600">
|
||||
<i className="fas fa-check-circle mr-1"></i>
|
||||
Pool is full
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between items-center mb-1">
|
||||
<span className="text-sm font-medium">Prompt History</span>
|
||||
<span className="text-sm">{stats.history.total}/{stats.history.capacity}</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 rounded-full h-2">
|
||||
<div
|
||||
className="bg-purple-600 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: `${(stats.history.total / stats.history.capacity) * 100}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-600 mt-1">
|
||||
{stats.history.available} slots available
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-6">
|
||||
<h4 className="font-medium mb-3">Quick Insights</h4>
|
||||
<ul className="space-y-2 text-sm">
|
||||
<li className="flex items-start">
|
||||
<i className="fas fa-calendar-day text-blue-600 mt-1 mr-2"></i>
|
||||
<span>
|
||||
<strong>{stats.pool.sessions} sessions</strong> available in pool
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<i className="fas fa-bolt text-yellow-600 mt-1 mr-2"></i>
|
||||
<span>
|
||||
{stats.pool.needsRefill ? (
|
||||
<span className="text-orange-600">Pool needs refilling</span>
|
||||
) : (
|
||||
<span className="text-green-600">Pool is ready for use</span>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<i className="fas fa-brain text-purple-600 mt-1 mr-2"></i>
|
||||
<span>
|
||||
AI has learned from <strong>{stats.history.total} prompts</strong> in history
|
||||
</span>
|
||||
</li>
|
||||
<li className="flex items-start">
|
||||
<i className="fas fa-chart-line text-green-600 mt-1 mr-2"></i>
|
||||
<span>
|
||||
History is <strong>{Math.round((stats.history.total / stats.history.capacity) * 100)}% full</strong>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{stats.pool.needsRefill && (
|
||||
<div className="mt-6">
|
||||
<button
|
||||
className="btn btn-primary w-full"
|
||||
onClick={handleFillPool}
|
||||
>
|
||||
<i className="fas fa-sync mr-2"></i>
|
||||
Fill Prompt Pool ({stats.pool.target - stats.pool.total} prompts)
|
||||
</button>
|
||||
<p className="text-xs text-gray-600 mt-2 text-center">
|
||||
This will use AI to generate new prompts and fill the pool to target capacity
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatsDashboard;
|
||||
|
||||
Reference in New Issue
Block a user