diff --git a/src/renderer/components/Dashboard/Dashboard.tsx b/src/renderer/components/Dashboard/Dashboard.tsx index 8c6d0b7..4860752 100644 --- a/src/renderer/components/Dashboard/Dashboard.tsx +++ b/src/renderer/components/Dashboard/Dashboard.tsx @@ -6,6 +6,11 @@ function daysSince(iso: string): number { return Math.floor((Date.now() - new Date(iso).getTime()) / 86_400_000) } +type StoryRow = + | { kind: 'out'; storyId: string; storyTitle: string; storyPath: string; marketName: string; days: number; overdue: boolean } + | { kind: 'ready'; storyId: string; storyTitle: string; storyPath: string; wordCount: number } + | { kind: 'accepted' | 'rejected'; storyId: string; storyTitle: string; storyPath: string; marketName: string } + export function Dashboard(): JSX.Element { const { stories, submissions, markets, setActiveStory, markSaved, isDirty, activeStoryPath, activeStoryContent } = useBorgesStore() @@ -19,26 +24,45 @@ export function Dashboard(): JSX.Element { setActiveStory(path, id, content) } - // Stories ready to submit: no active pending, never submitted or last status rejected - const readyToSubmit = stories.filter((story) => { + const rows: StoryRow[] = [] + + // Out: stories with active pending submissions + const pendingSubs = submissions + .filter((s) => s.status === 'pending' || s.status === 'pending-revision') + .sort((a, b) => a.submittedAt.localeCompare(b.submittedAt)) + for (const sub of pendingSubs) { + const story = stories.find((s) => s.id === sub.storyId) + const market = markets.find((m) => m.id === sub.marketId) + if (!story) continue + const days = daysSince(sub.submittedAt) + const overdue = !!(market?.responseTimeWeeks && days > market.responseTimeWeeks * 7) + rows.push({ kind: 'out', storyId: story.id, storyTitle: story.meta.title || story.id, storyPath: story.path, marketName: market?.name ?? sub.marketId, days, overdue }) + } + + // Ready: no active pending + const outIds = new Set(pendingSubs.map((s) => s.storyId)) + const readyStories = stories.filter((story) => { + if (outIds.has(story.id)) return false const storySubs = submissions.filter((s) => s.storyId === story.id) - const hasActive = storySubs.some((s) => s.status === 'pending' || s.status === 'pending-revision') - if (hasActive) return false if (storySubs.length === 0) return true const last = storySubs.sort((a, b) => b.submittedAt.localeCompare(a.submittedAt))[0] return last.status === 'rejected' || last.status === 'withdrawn' }) + for (const story of readyStories) { + rows.push({ kind: 'ready', storyId: story.id, storyTitle: story.meta.title || story.id, storyPath: story.path, wordCount: story.wordCount }) + } - // Pending submissions - const pending = submissions - .filter((s) => s.status === 'pending' || s.status === 'pending-revision') - .sort((a, b) => a.submittedAt.localeCompare(b.submittedAt)) - - // Recent activity (last 10 non-pending changes) - const recent = submissions + // Recent activity + const recentSubs = submissions .filter((s) => s.status === 'accepted' || s.status === 'rejected') .sort((a, b) => b.statusUpdatedAt.localeCompare(a.statusUpdatedAt)) - .slice(0, 10) + .slice(0, 8) + for (const sub of recentSubs) { + const story = stories.find((s) => s.id === sub.storyId) + const market = markets.find((m) => m.id === sub.marketId) + if (!story) continue + rows.push({ kind: sub.status as 'accepted' | 'rejected', storyId: story.id, storyTitle: story.meta.title || story.id, storyPath: story.path, marketName: market?.name ?? sub.marketId }) + } const totalWords = stories.reduce((s, story) => s + story.wordCount, 0) @@ -52,73 +76,44 @@ export function Dashboard(): JSX.Element {