import chalk from 'chalk'; import Table from 'cli-table3'; import type { Feed } from '../db/schema'; import { MAX_TITLE_DISPLAY_LENGTH } from './constants'; import { formatRelativeTime } from './date'; import { sanitizeTerminalText } from './terminal'; /** * Render the Feed list as a CLI table. */ export function createFeedsTable(feeds: Feed[]): string { const table = new Table({ head: [ chalk.cyan('ID'), chalk.cyan('Title'), chalk.cyan('Category'), chalk.cyan('Last Fetched'), chalk.cyan('Status'), chalk.cyan('Articles'), ], colWidths: [6, 40, 15, 20, 12, 10], wordWrap: true, }); for (const feed of feeds) { const status = feed.isActive ? feed.fetchError ? chalk.red('Error') : chalk.green('Active') : chalk.gray('Inactive'); const lastFetched = feed.lastFetchedAt ? formatRelativeTime(feed.lastFetchedAt) : chalk.gray('Never'); const articleCount = 'articleCount' in feed ? String((feed as Feed & { articleCount: number }).articleCount) : '-'; table.push([ feed.id.toString(), sanitizeTerminalText(feed.title).slice(0, MAX_TITLE_DISPLAY_LENGTH), feed.category ? sanitizeTerminalText(feed.category) : chalk.gray('-'), lastFetched, status, articleCount, ]); } return table.toString(); } /** * Render the summary banner shown after `fetch all`. */ export function formatFetchSummary( results: Array<{ feed: Feed; status: string; newArticles: number; error?: string; deactivated?: boolean; }>, ): string { const lines: string[] = []; const success = results.filter((r) => r.status === 'success'); const notModified = results.filter((r) => r.status === 'not_modified'); const errors = results.filter((r) => r.status === 'error'); const deactivated = results.filter((r) => r.deactivated); const totalNew = results.reduce((sum, r) => sum + r.newArticles, 0); lines.push(''); lines.push(chalk.bold('Fetch Summary:')); lines.push(chalk.green(` ✓ Fetched: ${success.length}`)); lines.push(chalk.blue(` ○ Not modified: ${notModified.length}`)); if (errors.length > 0) { lines.push(chalk.red(` ✗ Errors: ${errors.length}`)); for (const e of errors) { lines.push( chalk.red(` - ${sanitizeTerminalText(e.feed.title)}: ${sanitizeTerminalText(e.error || 'Unknown error')}`), ); } } if (deactivated.length > 0) { lines.push(chalk.yellow(` ⊘ Deactivated: ${deactivated.length} (too many consecutive errors)`)); } lines.push(chalk.cyan(` Total new articles: ${totalNew}`)); return lines.join('\n'); }