functionality tests mostly pass

This commit is contained in:
2026-01-03 14:52:25 -07:00
parent 55b0a698d0
commit e3d7e7de3a
11 changed files with 779 additions and 269 deletions

View File

@@ -1,78 +1,142 @@
import React, { useState, useEffect } from 'react';
const PromptDisplay = () => {
const [prompts, setPrompts] = useState([]);
const [prompts, setPrompts] = useState([]); // Changed to array to handle multiple prompts
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?"
];
const [selectedIndex, setSelectedIndex] = useState(null);
const [viewMode, setViewMode] = useState('history'); // 'history' or 'drawn'
useEffect(() => {
// Simulate API call
setTimeout(() => {
setPrompts(mockPrompts);
setLoading(false);
}, 1000);
fetchMostRecentPrompt();
}, []);
const handleSelectPrompt = (index) => {
setSelectedPrompt(index);
const fetchMostRecentPrompt = async () => {
setLoading(true);
setError(null);
try {
// Try to fetch from actual API first
const response = await fetch('/api/v1/prompts/history');
if (response.ok) {
const data = await response.json();
// API returns array directly, not object with 'prompts' key
if (Array.isArray(data) && data.length > 0) {
// Get the most recent prompt (first in array, position 0)
// Show only one prompt from history
setPrompts([{ text: data[0].text, position: data[0].position }]);
setViewMode('history');
} else {
// No history yet, show placeholder
setPrompts([{ text: "No recent prompts in history. Draw some prompts to get started!", position: 0 }]);
}
} else {
// API not available, use mock data
setPrompts([{ text: "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?", position: 0 }]);
}
} catch (err) {
console.error('Error fetching prompt:', err);
// Fallback to mock data
setPrompts([{ text: "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?", position: 0 }]);
} finally {
setLoading(false);
}
};
const handleDrawPrompts = async () => {
setLoading(true);
setError(null);
setSelectedIndex(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);
// Draw 3 prompts from pool (Task 4)
const response = await fetch('/api/v1/prompts/draw?count=3');
if (response.ok) {
const data = await response.json();
// Draw API returns object with 'prompts' array
if (data.prompts && data.prompts.length > 0) {
// Show all drawn prompts
const drawnPrompts = data.prompts.map((text, index) => ({
text,
position: index
}));
setPrompts(drawnPrompts);
setViewMode('drawn');
} else {
setError('No prompts available in pool. Please fill the pool first.');
}
} else {
setError('Failed to draw prompts. Please try again.');
}
} catch (err) {
setError('Failed to draw prompts. Please try again.');
} finally {
setLoading(false);
}
};
const handleAddToHistory = async () => {
if (selectedPrompt === null) {
setError('Please select a prompt first');
const handleAddToHistory = async (index) => {
if (index < 0 || index >= prompts.length) {
setError('Invalid prompt index');
return;
}
try {
// TODO: Replace with actual API call
// await fetch(`/api/v1/prompts/select/${selectedPrompt}`, { method: 'POST' });
const promptText = prompts[index].text;
// For now, just show success message
alert(`Prompt ${selectedPrompt + 1} added to history!`);
setSelectedPrompt(null);
// Send the prompt to the API to add to history
const response = await fetch('/api/v1/prompts/select', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt_text: promptText }),
});
if (response.ok) {
const data = await response.json();
// Mark as selected and show success
setSelectedIndex(index);
// Instead of showing an alert, refresh the page to show the updated history
// The default view shows the most recent prompt from history
alert(`Prompt added to history as ${data.position_in_history}! Refreshing to show updated history...`);
// Refresh the page to show the updated history
// The default view shows the most recent prompt from history (position 0)
fetchMostRecentPrompt();
} else {
const errorData = await response.json();
setError(`Failed to add prompt to history: ${errorData.detail || 'Unknown error'}`);
}
} catch (err) {
setError('Failed to add prompt to history');
}
};
const handleFillPool = async () => {
setLoading(true);
try {
const response = await fetch('/api/v1/prompts/fill-pool', { method: 'POST' });
if (response.ok) {
alert('Prompt pool filled successfully!');
// Refresh the prompt
fetchMostRecentPrompt();
} else {
setError('Failed to fill prompt pool');
}
} catch (err) {
setError('Failed to fill prompt pool');
} finally {
setLoading(false);
}
};
if (loading) {
return (
<div className="text-center p-8">
<div className="spinner mx-auto"></div>
<p className="mt-4">Loading prompts...</p>
<p className="mt-4">Loading prompt...</p>
</div>
);
}
@@ -88,83 +152,98 @@ const PromptDisplay = () => {
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>
) : (
{prompts.length > 0 ? (
<>
<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 className="mb-6">
<div className="grid grid-cols-1 gap-4">
{prompts.map((promptObj, index) => (
<div
key={index}
className={`prompt-card cursor-pointer ${selectedIndex === index ? 'selected' : ''}`}
onClick={() => setSelectedIndex(index)}
>
<div className="flex items-start gap-3">
<div className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center ${selectedIndex === index ? 'bg-green-100 text-green-600' : 'bg-blue-100 text-blue-600'}`}>
{selectedIndex === index ? (
<i className="fas fa-check"></i>
) : (
<span>{index + 1}</span>
)}
</div>
<div className="flex-grow">
<p className="prompt-text">{promptObj.text}</p>
<div className="prompt-meta">
<span>
<i className="fas fa-ruler-combined mr-1"></i>
{promptObj.text.length} characters
</span>
<span>
{selectedIndex === 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>
</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 flex-col gap-4">
<div className="flex gap-2">
<button className="btn btn-secondary" onClick={handleDrawPrompts}>
<i className="fas fa-redo"></i> Draw New Set
<button
className="btn btn-success flex-1"
onClick={() => handleAddToHistory(selectedIndex !== null ? selectedIndex : 0)}
disabled={selectedIndex === null}
>
<i className="fas fa-history"></i>
{selectedIndex !== null ? 'Use Selected Prompt' : 'Select a Prompt First'}
</button>
<button className="btn btn-primary" onClick={handleDrawPrompts}>
<i className="fas fa-dice"></i> Draw 6 More
<button className="btn btn-primary flex-1" onClick={handleDrawPrompts}>
<i className="fas fa-dice"></i>
{viewMode === 'history' ? 'Draw 3 New Prompts' : 'Draw 3 More Prompts'}
</button>
</div>
<button className="btn btn-secondary" onClick={handleFillPool}>
<i className="fas fa-sync"></i> Fill Prompt Pool
</button>
</div>
<div className="mt-4 text-sm text-gray-600">
<div className="mt-6 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.
<strong>
{viewMode === 'history' ? 'Most Recent Prompt from History' : `${prompts.length} Drawn Prompts`}:
</strong>
{viewMode === 'history'
? ' This is the latest prompt from your history. Using it helps the AI learn your preferences.'
: ' Select a prompt to use for journaling. The AI will learn from your selection.'}
</p>
<p className="mt-2">
<i className="fas fa-lightbulb mr-1"></i>
<strong>Tip:</strong> The prompt pool needs regular refilling. Check the stats panel
to see how full it is.
</p>
</div>
</>
) : (
<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">There are no prompts in history or pool. Get started by filling the pool.</p>
<button className="btn btn-primary" onClick={handleFillPool}>
<i className="fas fa-plus"></i> Fill Prompt Pool
</button>
</div>
)}
</div>
);

View File

@@ -18,59 +18,61 @@ const StatsDashboard = () => {
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 fetchStats = async () => {
try {
// Fetch pool stats
const poolResponse = await fetch('/api/v1/prompts/stats');
const poolData = poolResponse.ok ? await poolResponse.json() : {
total_prompts: 0,
target_pool_size: 20,
available_sessions: 0,
needs_refill: true
};
// Fetch history stats
const historyResponse = await fetch('/api/v1/prompts/history/stats');
const historyData = historyResponse.ok ? await historyResponse.json() : {
total_prompts: 0,
history_capacity: 60,
available_slots: 60,
is_full: false
};
setStats({
pool: {
total: poolData.total_prompts || 0,
target: poolData.target_pool_size || 20,
sessions: poolData.available_sessions || 0,
needsRefill: poolData.needs_refill || true
},
history: {
total: historyData.total_prompts || 0,
capacity: historyData.history_capacity || 60,
available: historyData.available_slots || 60,
isFull: historyData.is_full || false
}
});
} catch (error) {
console.error('Error fetching stats:', error);
// Use default values on error
} finally {
setLoading(false);
}
};
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!');
const response = await fetch('/api/v1/prompts/fill-pool', { method: 'POST' });
if (response.ok) {
alert('Prompt pool filled successfully!');
// Refresh stats
fetchStats();
} else {
alert('Failed to fill prompt pool');
}
} catch (error) {
alert('Failed to fill prompt pool');
}
@@ -120,7 +122,7 @@ const StatsDashboard = () => {
<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}%` }}
style={{ width: `${Math.min((stats.pool.total / stats.pool.target) * 100, 100)}%` }}
></div>
</div>
<div className="text-xs text-gray-600 mt-1">
@@ -146,7 +148,7 @@ const StatsDashboard = () => {
<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}%` }}
style={{ width: `${Math.min((stats.history.total / stats.history.capacity) * 100, 100)}%` }}
></div>
</div>
<div className="text-xs text-gray-600 mt-1">

View File

@@ -15,15 +15,7 @@ import StatsDashboard from '../components/StatsDashboard.jsx';
<div class="lg:col-span-2">
<div class="card">
<div class="card-header">
<h2><i class="fas fa-scroll"></i> Today's Prompts</h2>
<div class="flex gap-2">
<button class="btn btn-primary">
<i class="fas fa-redo"></i> Draw New Prompts
</button>
<button class="btn btn-secondary">
<i class="fas fa-plus"></i> Fill Pool
</button>
</div>
<h2><i class="fas fa-scroll"></i> Today's Writing Prompt</h2>
</div>
<PromptDisplay client:load />
@@ -45,17 +37,14 @@ import StatsDashboard from '../components/StatsDashboard.jsx';
</div>
<div class="flex flex-col gap-2">
<button class="btn btn-primary">
<i class="fas fa-dice"></i> Draw 6 Prompts
<button class="btn btn-primary" onclick="document.querySelector('button[onclick*=\"handleDrawPrompts\"]')?.click()">
<i class="fas fa-dice"></i> Draw 3 Prompts
</button>
<button class="btn btn-secondary">
<i class="fas fa-sync"></i> Refill Pool
<button class="btn btn-secondary" onclick="document.querySelector('button[onclick*=\"handleFillPool\"]')?.click()">
<i class="fas fa-sync"></i> Fill Pool
</button>
<button class="btn btn-success">
<i class="fas fa-palette"></i> Generate Themes
</button>
<button class="btn btn-warning">
<i class="fas fa-history"></i> View History
<button class="btn btn-warning" onclick="window.location.href='/api/v1/prompts/history'">
<i class="fas fa-history"></i> View History (API)
</button>
</div>
</div>

View File

@@ -86,8 +86,8 @@ a:hover {
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}
.btn-secondary {
@@ -133,7 +133,6 @@ a:hover {
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}