jobnimbus-mcp-client
Version:
JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server
129 lines • 5.36 kB
JavaScript
/**
* Get Timeline Data
* Fetches activities and creates a timeline visualization
*/
import { BaseTool } from '../baseTool.js';
export class GetTimelineData extends BaseTool {
get definition() {
return {
name: 'get_timeline_data',
description: 'Get timeline data for project scheduling (built from activities)',
inputSchema: {
type: 'object',
properties: {
from: {
type: 'number',
description: 'Starting index for pagination (default: 0)',
},
size: {
type: 'number',
description: 'Number of records to retrieve (default: 100, max: 100)',
},
date_from: {
type: 'string',
description: 'Start date filter (YYYY-MM-DD format)',
},
date_to: {
type: 'string',
description: 'End date filter (YYYY-MM-DD format)',
},
group_by: {
type: 'string',
enum: ['day', 'week', 'month'],
description: 'Group timeline by period (default: day)',
},
},
},
};
}
async execute(input, context) {
// Fetch activities from JobNimbus API
const params = {
from: input.from || 0,
size: input.size || 100,
date_from: input.date_from,
date_to: input.date_to,
};
const response = await this.client.get(context.apiKey, 'activities', params);
const activities = response.data?.activity || [];
const groupBy = input.group_by || 'day';
// Group activities by date
const timelineMap = new Map();
const byType = {};
const byUser = {};
activities.forEach((activity) => {
const timestamp = activity.date_created || activity.date_start;
if (!timestamp)
return;
const date = new Date(timestamp * 1000);
const dateKey = this.getDateKey(date, groupBy);
if (!timelineMap.has(dateKey)) {
timelineMap.set(dateKey, []);
}
const event = {
id: activity.jnid || activity.recid?.toString() || '',
title: activity.name || activity.subject || 'Untitled',
type: activity.record_type_name || 'activity',
timestamp: date.toISOString(),
};
timelineMap.get(dateKey).push(event);
// Count by type
const type = activity.record_type_name || 'unknown';
byType[type] = (byType[type] || 0) + 1;
// Count by user
const user = activity.created_by_name || 'Unknown';
byUser[user] = (byUser[user] || 0) + 1;
});
// Convert map to sorted timeline array
const timeline = Array.from(timelineMap.entries())
.map(([date, events]) => ({
date,
count: events.length,
events: events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()),
}))
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());
// Find most active day
const mostActiveDay = timeline.length > 0
? timeline.reduce((max, curr) => (curr.count > max.count ? curr : max)).date
: '';
// Calculate date range
const dates = timeline.map(t => new Date(t.date).getTime());
const startDate = dates.length > 0 ? new Date(Math.min(...dates)).toISOString() : '';
const endDate = dates.length > 0 ? new Date(Math.max(...dates)).toISOString() : '';
// Calculate average
const averageEventsPerPeriod = timeline.length > 0 ? activities.length / timeline.length : 0;
return {
success: true,
total_events: activities.length,
timeline,
summary: {
date_range: {
start: startDate,
end: endDate,
},
most_active_day: mostActiveDay,
average_events_per_period: Math.round(averageEventsPerPeriod * 100) / 100,
by_type: byType,
by_user: byUser,
},
};
}
getDateKey(date, groupBy) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
switch (groupBy) {
case 'day':
return `${year}-${month}-${day}`;
case 'week':
const weekStart = new Date(date);
weekStart.setDate(date.getDate() - date.getDay());
return `${weekStart.getFullYear()}-${String(weekStart.getMonth() + 1).padStart(2, '0')}-${String(weekStart.getDate()).padStart(2, '0')}`;
case 'month':
return `${year}-${month}`;
default:
return `${year}-${month}-${day}`;
}
}
}
//# sourceMappingURL=getTimelineData.js.map