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

111 lines 6.7 kB
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; import { Box, Text, render } from 'ink'; import { createClient } from '../api/jira-client.js'; import { createTempoClient } from '../api/tempo-client.js'; import { ProgressTable } from '../components/ProgressTable.js'; import { getWorkingDaysInRange, calculateDailyProgress, getRequiredHoursUpToDate } from '../utils/workload.js'; function groupWorklogsByDate(worklogs) { const grouped = new Map(); for (const worklog of worklogs) { if (worklog.started) { const date = worklog.started.split('T')[0]; // Get YYYY-MM-DD part if (date) { const hours = (worklog.timeSpentSeconds || 0) / 3600; const currentHours = grouped.get(date) || 0; grouped.set(date, currentHours + hours); } } } return grouped; } export async function showSprintProgress() { try { const client = createClient(); const tempoClient = createTempoClient(); // Get current sprint const sprint = await client.getCurrentSprint(); if (!sprint.startDate || !sprint.endDate) { console.log('❌ Current sprint does not have valid start or end dates.'); return; } const startDate = new Date(sprint.startDate); const endDate = new Date(sprint.endDate); // Convert to local dates to avoid timezone issues const fromISO = startDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format const toISO = endDate.toLocaleDateString('en-CA'); // YYYY-MM-DD format // Get current user const user = await client.getCurrentUser(); // Get user workload scheme let workloadScheme; try { const userWorkload = await tempoClient.getUserWorkload(user.accountId); if (userWorkload?.workloadScheme?.days && Array.isArray(userWorkload.workloadScheme.days)) { workloadScheme = userWorkload.workloadScheme; } else { throw new Error('Invalid workload scheme response'); } } 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: '', }; } // Get worklogs for sprint period const tempoWorklogsResponse = await tempoClient.getWorklogsForUser(user.accountId, fromISO, toISO); const tempoWorklogs = (tempoWorklogsResponse && typeof tempoWorklogsResponse === 'object' && Array.isArray(tempoWorklogsResponse.results)) ? tempoWorklogsResponse.results : Array.isArray(tempoWorklogsResponse) ? tempoWorklogsResponse : []; // Group worklogs by date const worklogsByDate = groupWorklogsByDate(tempoWorklogs); // Get working days in sprint based on workload scheme const workingDays = getWorkingDaysInRange(startDate, endDate); // Calculate daily progress with workload awareness const dailyProgress = calculateDailyProgress(worklogsByDate, workingDays, workloadScheme); // Render the progress table render(_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\uD83C\uDFC3 Sprint: ", sprint.name] }), _jsxs(Text, { color: "gray", children: ["\uD83D\uDCC5 ", fromISO, " to ", toISO] }), _jsxs(Text, { color: "blue", children: ["\uD83D\uDCCA Workload: ", workloadScheme?.name] }), _jsx(ProgressTable, { rows: dailyProgress, title: "\uD83D\uDCCA Sprint Progress", showWorkload: true }), _jsx(Text, { color: "gray", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }), (() => { const today = new Date(); const effectiveEndDate = today <= endDate ? today : endDate; // Up to now const upToNowRequired = getRequiredHoursUpToDate(workloadScheme, startDate, effectiveEndDate); const upToNowLogged = dailyProgress .filter(row => new Date(row.date) <= effectiveEndDate) .reduce((sum, row) => sum + row.hoursLogged, 0); const upToNowPerc = upToNowRequired > 0 ? (upToNowLogged / upToNowRequired) * 100 : 0; let upToNowColor = 'red'; if (upToNowPerc >= 100) upToNowColor = 'green'; else if (upToNowPerc >= 75) upToNowColor = 'yellow'; // Whole sprint const totalRequired = getRequiredHoursUpToDate(workloadScheme, startDate, endDate); const totalLogged = dailyProgress.reduce((sum, row) => sum + row.hoursLogged, 0); const totalPerc = totalRequired > 0 ? (totalLogged / totalRequired) * 100 : 0; let totalColor = 'red'; if (totalPerc >= 100) totalColor = 'green'; else if (totalPerc >= 75) totalColor = 'yellow'; return (_jsxs(Text, { children: [_jsx(Text, { color: "lightGray", children: "Progress: " }), _jsxs(Text, { color: upToNowColor, children: [upToNowLogged, "h/", upToNowRequired, "h (", upToNowPerc.toFixed(1), "%)"] }), _jsx(Text, { color: "lightGray", children: " | Sprint: " }), _jsxs(Text, { color: totalColor, children: [totalLogged, "h/", totalRequired, "h (", totalPerc.toFixed(1), "%)"] })] })); })()] })); } catch (error) { console.error('❌ Error fetching sprint progress:', error instanceof Error ? error.message : 'Unknown error'); } } export default showSprintProgress; //# sourceMappingURL=progress.js.map