UNPKG

bookr-cli

Version:

A terminal-based CLI tool to book time in Jira using the Tempo plugin by parsing your current Git branch name

99 lines 5.04 kB
import { createClient } from '../api/jira-client.js'; import { secondsToJiraFormat } from '../utils/time-parser.js'; import { createTempoClient } from '../api/tempo-client.js'; import { parallelMapLimit } from '../utils/concurrency.js'; import { getRequiredHoursForDate } from '../utils/workload.js'; export async function showTodayWorklogs() { try { const client = createClient(); const tempoClient = createTempoClient(); const user = await client.getCurrentUser(); const todayISO = new Date().toISOString().slice(0, 10); // Get user workload scheme let workloadScheme; try { const userWorkload = await tempoClient.getUserWorkload(user.accountId); workloadScheme = userWorkload.workloadScheme; } catch { console.log('⚠️ Could not fetch user workload scheme, using default 8 hours per day.'); // Fallback to default 8 hours per day for weekdays workloadScheme = { days: [ { day: 'MONDAY', requiredSeconds: 8 * 3600 }, { day: 'TUESDAY', requiredSeconds: 8 * 3600 }, { day: 'WEDNESDAY', requiredSeconds: 8 * 3600 }, { day: 'THURSDAY', requiredSeconds: 8 * 3600 }, { day: 'FRIDAY', requiredSeconds: 8 * 3600 }, ], defaultScheme: true, description: 'Default 8-hour workday', id: 0, memberCount: 1, name: 'Default Workload', self: '', }; } const tempoWorklogsResponse = await tempoClient.getWorklogsForUser(user.accountId, todayISO, todayISO); const tempoWorklogs = (tempoWorklogsResponse && typeof tempoWorklogsResponse === 'object' && Array.isArray(tempoWorklogsResponse.results)) ? tempoWorklogsResponse.results : Array.isArray(tempoWorklogsResponse) ? tempoWorklogsResponse : []; if (!tempoWorklogs.length) { console.log('📭 No worklogs found for today.'); return; } // Collect unique issue IDs const uniqueIssueIds = Array.from(new Set(tempoWorklogs.map((w) => w.issue?.id).filter(Boolean))); // Fetch all issues in parallel (limit concurrency to 5) const issueMap = new Map(); await parallelMapLimit(uniqueIssueIds, 5, async (id) => { try { const issue = await client.getIssue(String(id)); issueMap.set(id, { key: issue.key, summary: issue.fields.summary }); } catch { // If not found, skip } }); // Table header console.log('─'.repeat(140)); console.log(`${'Tempo ID'.padEnd(12)} | ${'Jira ID'.padEnd(12)} | ${'Issue'.padEnd(16)} | ${'Time'.padEnd(6)} | Summary`); console.log('─'.repeat(140)); let totalSeconds = 0; const uniqueIssues = new Set(); for (const worklog of tempoWorklogs) { const timeSpentSeconds = typeof worklog.timeSpentSeconds === 'number' ? worklog.timeSpentSeconds : 0; totalSeconds += timeSpentSeconds; const timeDisplay = secondsToJiraFormat(timeSpentSeconds); const issueId = worklog.issue?.id || ''; const issueInfo = issueMap.get(issueId) || {}; const issueKey = issueInfo.key || ''; uniqueIssues.add(String(issueKey)); const summary = issueInfo.summary || worklog.description || ''; const tempoId = worklog.tempoWorklogId ? String(worklog.tempoWorklogId) : ''; const jiraId = worklog.id ? String(worklog.id) : ''; console.log(`${tempoId.padEnd(12)} | ${jiraId.padEnd(12)} | ${issueKey.padEnd(16)} | ${timeDisplay.padEnd(6)} | ${summary}`); } console.log('─'.repeat(140)); const totalTime = secondsToJiraFormat(totalSeconds); const totalHours = (totalSeconds / 3600).toFixed(2); // Calculate today's required hours const today = new Date(); const requiredHours = getRequiredHoursForDate(workloadScheme, today); const progressPercentage = requiredHours > 0 ? ((totalSeconds / 3600) / requiredHours) * 100 : 0; console.log(`📊 Total time today: ${totalTime} (${totalHours} hours)`); console.log(`📝 Worklog entries: ${tempoWorklogs.length}`); console.log(`🎯 Issues worked on: ${uniqueIssues.size}`); console.log(`📈 Progress: ${progressPercentage.toFixed(1)}% of ${requiredHours.toFixed(1)}h required (${workloadScheme.name})`); } catch (error) { console.error('❌ Error:', error instanceof Error ? error.message : error); } } // Default export for CLI usage export default showTodayWorklogs; // For backward compatibility when running directly if (import.meta.url === `file://${process.argv[1]}`) { showTodayWorklogs().catch(console.error); } //# sourceMappingURL=today.js.map