removed strict mode, rendering row titles
This commit is contained in:
parent
8bbdcf3673
commit
6e6176af7f
4 changed files with 88 additions and 41 deletions
|
|
@ -27,7 +27,6 @@ body {
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-family: "Russo One", sans-serif !important;
|
font-family: "Russo One", sans-serif !important;
|
||||||
font-size:1.2rem;
|
|
||||||
}
|
}
|
||||||
/* App theme containers */
|
/* App theme containers */
|
||||||
.app-dark {
|
.app-dark {
|
||||||
|
|
@ -59,7 +58,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
.widget-row.condensed {
|
.widget-row.condensed {
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
}
|
}
|
||||||
.news-widget img {
|
.news-widget img {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { Box, Container, CircularProgress, IconButton, Alert } from '@mui/material';
|
import { Box, Container, CircularProgress, IconButton, Alert } from '@mui/material';
|
||||||
import { Brightness4, Brightness7, Refresh } from '@mui/icons-material';
|
import { Brightness4, Brightness7, Refresh } from '@mui/icons-material';
|
||||||
import { fetchAllSources } from '../services/dataFetcher';
|
import { fetchAllSources } from '../services/dataFetcher';
|
||||||
|
|
@ -18,15 +18,27 @@ function Dashboard({ themeToggle, isDark }) {
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const [tokenUsage, setTokenUsage] = useState(null);
|
const [tokenUsage, setTokenUsage] = useState(null);
|
||||||
const [cacheInfo, setCacheInfo] = useState(null);
|
const [cacheInfo, setCacheInfo] = useState(null);
|
||||||
|
const isLoadingRef = useRef(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('=== Dashboard mounting, calling loadDashboard ===');
|
||||||
loadDashboard();
|
loadDashboard();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function loadDashboard(forceRefresh = false) {
|
async function loadDashboard(forceRefresh = false) {
|
||||||
|
// Prevent concurrent requests using ref for immediate check
|
||||||
|
if (isLoadingRef.current) {
|
||||||
|
console.log('=== loadDashboard SKIPPED - already in progress ===');
|
||||||
|
console.log('Attempted at:', new Date().toISOString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoadingRef.current = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('=== loadDashboard called ===');
|
console.log('=== loadDashboard called ===');
|
||||||
console.log('Force refresh:', forceRefresh);
|
console.log('Force refresh:', forceRefresh);
|
||||||
|
console.log('Request timestamp:', new Date().toISOString());
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
// Check if we already have today's briefing (unless forcing refresh)
|
// Check if we already have today's briefing (unless forcing refresh)
|
||||||
|
|
@ -126,11 +138,15 @@ function Dashboard({ themeToggle, isDark }) {
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
|
isLoadingRef.current = false;
|
||||||
|
console.log('=== loadDashboard completed at:', new Date().toISOString(), '===');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleRefresh = () => {
|
const handleRefresh = () => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
|
setLoading(true);
|
||||||
|
setLoadingStatus('Refreshing your dashboard...');
|
||||||
loadDashboard(true);
|
loadDashboard(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -221,17 +237,28 @@ function Dashboard({ themeToggle, isDark }) {
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Render rows based on the new data structure */}
|
{/* Render rows based on the data structure */}
|
||||||
{Object.entries(sourceData).map(([key, data]) => {
|
{Object.entries(sourceData).map(([key, data]) => {
|
||||||
// Skip special entries like weather and legacy flat entries
|
|
||||||
if (key === 'weather' || key.startsWith('reddit_') || key.startsWith('rss_') || key.startsWith('source_')) {
|
// Skip non-array entries (like weather)
|
||||||
|
if (!Array.isArray(data) || data.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only process row entries (row1, row2, etc.)
|
return (
|
||||||
if (key.startsWith('row') && Array.isArray(data)) {
|
<Box key={key} sx={{ mb: 2 }}>
|
||||||
return (
|
{/* Display row header if not using legacy row naming */}
|
||||||
<Box key={key} className="widget-row condensed">
|
{!key.startsWith('row') && (
|
||||||
|
<h1 style={{
|
||||||
|
marginBottom: '12px',
|
||||||
|
marginTop: '20px',
|
||||||
|
color: 'inherit',
|
||||||
|
fontWeight: 600
|
||||||
|
}}>
|
||||||
|
{key}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
|
<Box className="widget-row condensed">
|
||||||
{data.map((widgetData, index) => (
|
{data.map((widgetData, index) => (
|
||||||
<NewsWidget
|
<NewsWidget
|
||||||
key={`${key}-${index}`}
|
key={`${key}-${index}`}
|
||||||
|
|
@ -239,26 +266,9 @@ function Dashboard({ themeToggle, isDark }) {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
</Box>
|
||||||
}
|
);
|
||||||
|
|
||||||
return null;
|
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* Fallback for legacy format - if no rows detected, render flat structure */}
|
|
||||||
{!Object.keys(sourceData).some(k => k.startsWith('row')) && (
|
|
||||||
<Box className="widget-row condensed">
|
|
||||||
{Object.entries(sourceData).map(([key, data]) => {
|
|
||||||
if (key === 'weather' || !data) return null;
|
|
||||||
return (
|
|
||||||
<NewsWidget
|
|
||||||
key={key}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,5 @@ import ReactDOM from 'react-dom/client'
|
||||||
import App from './App.jsx'
|
import App from './App.jsx'
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||||
<React.StrictMode>
|
<App />
|
||||||
<App />
|
)
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,15 @@ CURRENT DATA:
|
||||||
- Includes relevant weather information if available
|
- Includes relevant weather information if available
|
||||||
- Ends with something engaging or thought-provoking
|
- Ends with something engaging or thought-provoking
|
||||||
|
|
||||||
Focus on the most newsworthy and interesting items. Don't try to mention everything.`;
|
Focus on the most newsworthy and interesting items. Don't try to mention everything.
|
||||||
|
|
||||||
|
IMPORTANT: Provide your response as valid JSON with exactly two fields:
|
||||||
|
{
|
||||||
|
"fullBriefing": "Your complete 300-400 word friendly briefing text here",
|
||||||
|
"condensedSummary": "A 50-75 word ultra-condensed summary of the key topics and main stories for historical context"
|
||||||
|
}
|
||||||
|
|
||||||
|
The JSON must be properly formatted with escaped quotes where necessary.`;
|
||||||
|
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
@ -171,13 +179,46 @@ export async function generateBriefing(currentData, previousBriefing) {
|
||||||
const prompt = buildBriefingPrompt(currentData, previousBriefing);
|
const prompt = buildBriefingPrompt(currentData, previousBriefing);
|
||||||
const result = await callClaude(prompt);
|
const result = await callClaude(prompt);
|
||||||
|
|
||||||
// Generate condensed summary for historical storage
|
// Parse JSON response that now includes both fullBriefing and condensedSummary
|
||||||
const condensedSummary = await generateCondensedSummary(result.text);
|
try {
|
||||||
|
// Strip markdown code blocks if present
|
||||||
return {
|
let jsonText = result.text;
|
||||||
...result,
|
if (jsonText.includes('```json')) {
|
||||||
condensedSummary: condensedSummary
|
jsonText = jsonText.replace(/```json\s*/g, '').replace(/```\s*/g, '');
|
||||||
};
|
} else if (jsonText.includes('```')) {
|
||||||
|
jsonText = jsonText.replace(/```\s*/g, '');
|
||||||
|
}
|
||||||
|
jsonText = jsonText.trim();
|
||||||
|
|
||||||
|
const parsed = JSON.parse(jsonText);
|
||||||
|
|
||||||
|
// Validate the JSON structure
|
||||||
|
if (!parsed.fullBriefing || !parsed.condensedSummary) {
|
||||||
|
throw new Error('Invalid response structure from Claude');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Successfully parsed briefing with condensed summary');
|
||||||
|
console.log('Condensed summary:', parsed.condensedSummary.substring(0, 100) + '...');
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: parsed.fullBriefing,
|
||||||
|
condensedSummary: parsed.condensedSummary,
|
||||||
|
usage: result.usage
|
||||||
|
};
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('Failed to parse JSON response, attempting fallback:', parseError);
|
||||||
|
|
||||||
|
// Fallback: if JSON parsing fails, use the raw text and generate summary separately
|
||||||
|
// This maintains backward compatibility but should rarely happen
|
||||||
|
console.warn('Using fallback: generating condensed summary separately');
|
||||||
|
const condensedSummary = await generateCondensedSummary(result.text);
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: result.text,
|
||||||
|
condensedSummary: condensedSummary,
|
||||||
|
usage: result.usage
|
||||||
|
};
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating briefing:', error);
|
console.error('Error generating briefing:', error);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue