alnilam-cli
Version:
Git-native AI career coach that converts multi-year ambitions into weekly execution
73 lines (72 loc) • 7.19 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoalProgressGrid = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const ink_1 = require("ink");
const Theme_js_1 = require("../shared/Theme.js");
const GoalProgressGrid = ({ goals, compact = false, maxItems = 8 }) => {
// Group goals by horizon and status
const groupedGoals = goals.reduce((acc, goal) => {
if (!acc[goal.horizon]) {
acc[goal.horizon] = { active: 0, completed: 0, paused: 0, cancelled: 0, total: 0 };
}
const statusKey = goal.status;
if (typeof acc[goal.horizon][statusKey] === 'number') {
acc[goal.horizon][statusKey]++;
}
acc[goal.horizon].total++;
return acc;
}, {});
// Horizon order for display
const horizonOrder = ['weekly', 'quarterly', 'annual', 'multi-year'];
// Get horizon emoji
const getHorizonEmoji = (horizon) => {
switch (horizon) {
case 'weekly': return '📅';
case 'quarterly': return '📊';
case 'annual': return '🗓️';
case 'multi-year': return '🔭';
default: return '🎯';
}
};
// Calculate completion percentage for horizon
const getCompletionPercentage = (horizon) => {
if (!horizon || horizon.total === 0)
return 0;
return Math.round((horizon.completed / horizon.total) * 100);
};
// Create progress bar
const createProgressBar = (percentage, width = 20) => {
const filled = Math.round((percentage / 100) * width);
return '█'.repeat(filled) + '░'.repeat(width - filled);
};
// Get recent goals for detailed view
const recentGoals = goals
.filter(g => g.status === 'active')
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
.slice(0, maxItems);
if (compact) {
// Compact view - just show horizon summary
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: horizonOrder.map(horizon => {
const data = groupedGoals[horizon];
if (!data || data.total === 0)
return null;
const percentage = getCompletionPercentage(data);
const statusColor = percentage > 70 ? 'green' : percentage > 40 ? 'yellow' : 'red';
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", justifyContent: "space-between", marginBottom: 1, children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", gap: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: getHorizonEmoji(horizon) }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: horizon })] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", gap: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: statusColor, children: data.active }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "/" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: data.total })] })] }, horizon));
}) }));
}
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "blue", children: "By Horizon" }) }), horizonOrder.map(horizon => {
const data = groupedGoals[horizon];
if (!data || data.total === 0)
return null;
const percentage = getCompletionPercentage(data);
const statusColor = percentage > 70 ? 'green' : percentage > 40 ? 'yellow' : 'red';
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", justifyContent: "space-between", children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", gap: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { children: getHorizonEmoji(horizon) }), (0, jsx_runtime_1.jsx)(ink_1.Text, { children: horizon.charAt(0).toUpperCase() + horizon.slice(1) })] }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: [data.active, " active \u2022 ", data.completed, " done"] })] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", alignItems: "center", gap: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "[" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: statusColor, children: createProgressBar(percentage, 15) }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "]" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: statusColor, children: [percentage, "%"] })] })] }, horizon));
})] }), recentGoals.length > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "blue", children: "Recent Active Goals" }) }), recentGoals.map(goal => {
const daysAgo = Math.floor((new Date().getTime() - new Date(goal.created_at).getTime()) / (1000 * 60 * 60 * 24));
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 1, children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", justifyContent: "space-between", children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", gap: 1, flexGrow: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: (0, Theme_js_1.getStatusColor)(goal.status), children: "\u25CF" }), (0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [goal.title.substring(0, 40), goal.title.length > 40 ? '...' : ''] })] }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", dimColor: true, children: daysAgo === 0 ? 'today' : `${daysAgo}d ago` })] }), (0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "row", gap: 2, marginLeft: 2, children: [(0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", dimColor: true, children: [getHorizonEmoji(goal.horizon), " ", goal.horizon] }), goal.target_date && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "yellow", dimColor: true, children: ["\uD83D\uDCC5 ", new Date(goal.target_date).toLocaleDateString()] }))] })] }, goal.id));
}), goals.filter(g => g.status === 'active').length > maxItems && ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", dimColor: true, children: ["... and ", goals.filter(g => g.status === 'active').length - maxItems, " more active goals"] }))] })), goals.length === 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 5, children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: "\uD83D\uDCED No goals yet" }), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", dimColor: true, children: "Create your first goal with: alnl goal add" })] }))] }));
};
exports.GoalProgressGrid = GoalProgressGrid;
exports.default = exports.GoalProgressGrid;