taskmanager-ai
Version:
A CLI tool to manage your projects tasks with AI
349 lines (309 loc) • 10.1 kB
JavaScript
/**
* Utilitário de formatação para o TaskManager
*
* Este módulo oferece funções para formatar a saída de tarefas
* e outras informações no terminal de forma visualmente atraente.
*/
import chalk from 'chalk';
import boxen from 'boxen';
import Table from 'cli-table3';
import gradient from 'gradient-string';
/**
* Formata o status de uma tarefa com cores
* @param {String} status - Status da tarefa
* @returns {String} Status formatado com cores
*/
export function formatStatus(status) {
switch (status) {
case 'pending':
return chalk.yellow('⏳ Pendente');
case 'in-progress':
return chalk.blue('🔄 Em Andamento');
case 'done':
return chalk.green('✅ Concluída');
case 'deferred':
return chalk.gray('⏱️ Adiada');
case 'cancelled':
return chalk.red('❌ Cancelada');
default:
return chalk.white(`📋 ${status}`);
}
}
/**
* Formata a prioridade de uma tarefa com cores
* @param {String} priority - Prioridade da tarefa
* @returns {String} Prioridade formatada com cores
*/
export function formatPriority(priority) {
switch (priority) {
case 'high':
return chalk.red('🔴 Alta');
case 'medium':
return chalk.yellow('🟡 Média');
case 'low':
return chalk.green('🟢 Baixa');
default:
return chalk.white(`⚪ ${priority}`);
}
}
/**
* Formata uma data ISO em formato legível
* @param {String} isoDate - Data em formato ISO
* @returns {String} Data formatada
*/
export function formatDate(isoDate) {
if (!isoDate) return '';
const date = new Date(isoDate);
return date.toLocaleString('pt-BR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
}
/**
* Formata uma dependência com indicação de status
* @param {Number} depId - ID da dependência
* @param {Array} allTasks - Lista de todas as tarefas
* @returns {String} Dependência formatada
*/
export function formatDependency(depId, allTasks) {
const depTask = allTasks.find(t => t.id === depId);
if (!depTask) {
return chalk.red(`#${depId} (não encontrada)`);
}
const icon = depTask.status === 'done'
? chalk.green('✅')
: chalk.yellow('⏳');
return `${icon} #${depId} - ${depTask.title}`;
}
/**
* Formata uma tarefa completa como texto
* @param {Object} task - Tarefa a ser formatada
* @param {Array} allTasks - Lista de todas as tarefas (para dependências)
* @returns {String} Tarefa formatada
*/
export function formatTask(task, allTasks = []) {
const title = gradient.pastel.multiline(`#${task.id} - ${task.title}`);
// Formata a descrição principal
let output = boxen(title, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'cyan'
});
// Informações básicas
output += '\n';
output += `${chalk.bold('Status:')} ${formatStatus(task.status)}\n`;
output += `${chalk.bold('Prioridade:')} ${formatPriority(task.priority)}\n`;
output += `${chalk.bold('Criado em:')} ${formatDate(task.createdAt)}\n`;
output += `${chalk.bold('Atualizado em:')} ${formatDate(task.updatedAt)}\n`;
// Descrição
if (task.description) {
output += `\n${chalk.bold('Descrição:')}\n`;
output += `${task.description}\n`;
}
// Detalhes
if (task.details) {
output += `\n${chalk.bold('Detalhes:')}\n`;
output += `${task.details}\n`;
}
// Estratégia de teste
if (task.testStrategy) {
output += `\n${chalk.bold('Estratégia de Teste:')}\n`;
output += `${task.testStrategy}\n`;
}
// Dependências
if (task.dependencies && task.dependencies.length > 0) {
output += `\n${chalk.bold('Dependências:')}\n`;
for (const depId of task.dependencies) {
output += `${formatDependency(depId, allTasks)}\n`;
}
}
// Subtarefas
if (task.subtasks && task.subtasks.length > 0) {
output += `\n${chalk.bold('Subtarefas:')}\n`;
for (const subtask of task.subtasks) {
const statusIcon = subtask.status === 'done'
? chalk.green('✅')
: subtask.status === 'in-progress'
? chalk.blue('🔄')
: chalk.yellow('⏳');
output += `${statusIcon} ${subtask.id}. ${subtask.title}\n`;
if (subtask.description) {
output += ` ${chalk.gray(subtask.description)}\n`;
}
}
}
return output;
}
/**
* Cria uma tabela de tarefas
* @param {Array} tasks - Lista de tarefas
* @param {Boolean} withSubtasks - Incluir subtarefas na tabela
* @param {Boolean} highlightNextTask - Destacar a próxima tarefa
* @returns {String} Tabela formatada
*/
export function formatTasksTable(tasks, withSubtasks = false, highlightNextTask = false) {
// Cria uma tabela
const table = new Table({
head: [
chalk.cyan('ID'),
chalk.cyan('Título'),
chalk.cyan('Status'),
chalk.cyan('Prioridade'),
chalk.cyan('Descrição'),
chalk.cyan('Dependências')
],
colWidths: [8, 25, 15, 15, 50, 15],
wordWrap: true,
wrapOnWordBoundary: true,
style: {
head: [], // Remove o destaque padrão
border: [], // Remove o destaque da borda
'padding-left': 1,
'padding-right': 1
}
});
// Adiciona as tarefas à tabela
for (const task of tasks) {
const isNextTask = highlightNextTask && task.status === 'in-progress';
const taskRow = [
chalk.bold(`#${task.id}`),
task.title,
formatStatus(task.status),
formatPriority(task.priority),
task.description || '',
task.dependencies && task.dependencies.length > 0 ? `[${task.dependencies.join(', ')}]` : ''
];
table.push(taskRow);
// Adiciona subtarefas se solicitado
if (withSubtasks && task.subtasks && task.subtasks.length > 0) {
for (const subtask of task.subtasks) {
// No comando 'next' (highlightNextTask=true), destacamos a subtarefa pendente da tarefa em andamento
// No comando 'current' (highlightNextTask=false), destacamos a subtarefa em andamento
const shouldHighlight = highlightNextTask
? (isNextTask && subtask.status === 'pending')
: subtask.status === 'in-progress';
const subtaskTitle = shouldHighlight
? `${chalk.green('>')} ${chalk.gray(subtask.title)}`
: ` ${chalk.gray(subtask.title)}`;
const subtaskRow = [
chalk.gray(`${task.id}.${subtask.id}`),
subtaskTitle,
formatStatus(subtask.status),
'',
chalk.gray(subtask.description || ''),
''
];
table.push(subtaskRow);
}
}
}
return table.toString();
}
/**
* Formata estatísticas de tarefas
* @param {Object} metadata - Metadados das tarefas
* @returns {String} Estatísticas formatadas
*/
export function formatTaskStats(metadata) {
const total = metadata.taskCount || 0;
const completed = metadata.completedCount || 0;
const pending = metadata.pendingCount || 0;
const inProgress = metadata.inProgressCount || 0;
const deferred = metadata.deferredCount || 0;
const cancelled = metadata.cancelledCount || 0;
// Calcula a porcentagem de conclusão
const completionPercent = total > 0 ? Math.round((completed / total) * 100) : 0;
// Cria uma barra de progresso
const progressBarWidth = 30;
const completedChars = Math.floor((completionPercent / 100) * progressBarWidth);
const remainingChars = progressBarWidth - completedChars;
const progressBar =
chalk.green('█'.repeat(completedChars)) +
chalk.gray('░'.repeat(remainingChars)) +
` ${completionPercent}%`;
// Formata as estatísticas
let output = boxen(chalk.bold('Estatísticas do Projeto'), {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'yellow'
});
output += '\n';
output += `${chalk.bold('Progresso Total:')}\n${progressBar}\n\n`;
output += `${chalk.bold('Total de Tarefas:')} ${total}\n`;
output += `${chalk.green('✅ Concluídas:')} ${completed}\n`;
output += `${chalk.blue('🔄 Em Andamento:')} ${inProgress}\n`;
output += `${chalk.yellow('⏳ Pendentes:')} ${pending}\n`;
output += `${chalk.gray('⏱️ Adiadas:')} ${deferred}\n`;
output += `${chalk.red('❌ Canceladas:')} ${cancelled}\n`;
output += '\n';
output += `${chalk.bold('Por Prioridade:')}\n`;
output += `${chalk.red('🔴 Alta:')} ${metadata.highPriorityCount || 0}\n`;
output += `${chalk.yellow('🟡 Média:')} ${metadata.mediumPriorityCount || 0}\n`;
output += `${chalk.green('🟢 Baixa:')} ${metadata.lowPriorityCount || 0}\n`;
return output;
}
/**
* Formata um título com destaque visual
* @param {String} title - Título a ser formatado
* @returns {String} Título formatado
*/
export function formatTitle(title) {
return gradient.passion.multiline(`\n${title}\n${'='.repeat(title.length)}\n`);
}
/**
* Formata uma mensagem de sucesso
* @param {String} message - Mensagem a ser formatada
* @returns {String} Mensagem formatada
*/
export function formatSuccess(message) {
return boxen(chalk.green(`✓ ${message}`), {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'green'
});
}
/**
* Formata uma mensagem de erro
* @param {String} message - Mensagem a ser formatada
* @returns {String} Mensagem formatada
*/
export function formatError(message) {
return boxen(chalk.red(`✗ ${message}`), {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'red'
});
}
/**
* Formata uma mensagem de aviso
* @param {String} message - Mensagem a ser formatada
* @returns {String} Mensagem formatada
*/
export function formatWarning(message) {
return boxen(chalk.yellow(`⚠ ${message}`), {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'yellow'
});
}
/**
* Formata uma dica ou informação
* @param {String} message - Mensagem a ser formatada
* @returns {String} Mensagem formatada
*/
export function formatInfo(message) {
return boxen(chalk.blue(`ℹ ${message}`), {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'blue'
});
}