feat: a day of the activity map opens what happened, ticket by ticket
On the project dashboard, a day with activity can be picked on the map: a side panel lists that day's moves grouped by ticket (each key opens it), with the time of each, and the project's own (sprints, releases, members) apart. The activity feed takes an `after` date (GraphQL, REST), so with `before` it reads a window such as a day; the MCP's get_project_activity takes a `day`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1425,6 +1425,26 @@ describe('task-stream (e2e)', () => {
|
||||
expect(insights.body['journey']).toMatchObject({ samples: 0, steps: [] });
|
||||
expect(insights.body['proposals']).toBeNull();
|
||||
|
||||
// A day of it: only what happened between after and before.
|
||||
const tomorrow = new Date(Date.parse(`${today}T00:00:00Z`) + 86_400_000);
|
||||
const ofToday = await rest(
|
||||
'token-ana',
|
||||
'GET',
|
||||
`/projects/${key}/activity?after=${today}T00:00:00.000Z&before=${tomorrow.toISOString()}&limit=200`,
|
||||
);
|
||||
expect(ofToday.body.length).toBeGreaterThan(0);
|
||||
expect(
|
||||
ofToday.body.every((a: { occurredAt: string }) =>
|
||||
a.occurredAt.startsWith(today),
|
||||
),
|
||||
).toBe(true);
|
||||
const ofYesterday = await rest(
|
||||
'token-ana',
|
||||
'GET',
|
||||
`/projects/${key}/activity?after=2020-01-01T00:00:00.000Z&before=2020-01-02T00:00:00.000Z`,
|
||||
);
|
||||
expect(ofYesterday.body).toEqual([]);
|
||||
|
||||
// What changed since a moment, and what the backlog lacks.
|
||||
const since = new Date(Date.now() - 60_000).toISOString();
|
||||
const later = await rest(
|
||||
|
||||
@@ -1775,6 +1775,16 @@
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Entries at or after this date (with before, a window such as a day)",
|
||||
"schema": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
|
||||
@@ -1464,6 +1464,11 @@ type Quality {
|
||||
type Query {
|
||||
"""Project activity feed (newest first)"""
|
||||
activity(
|
||||
"""
|
||||
ISO date: only activity at or after it (with before, a window such as a day)
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""ISO date cursor"""
|
||||
before: String
|
||||
issueId: ID
|
||||
|
||||
@@ -4750,6 +4750,8 @@ export interface operations {
|
||||
limit?: number;
|
||||
/** @description Cursor: entries older than this date */
|
||||
before?: string;
|
||||
/** @description Entries at or after this date (with before, a window such as a day) */
|
||||
after?: string;
|
||||
};
|
||||
header?: never;
|
||||
path: {
|
||||
|
||||
@@ -280,19 +280,36 @@ export class ProjectsResolver {
|
||||
|
||||
@Tool({
|
||||
name: 'get_project_activity',
|
||||
description: 'Latest activity of a project (newest first).',
|
||||
paramsSchema: z.object({ project: projectRef, limit: limit(200, 30) }),
|
||||
description:
|
||||
'Latest activity of a project (newest first), or that of one day (UTC) with day.',
|
||||
paramsSchema: z.object({
|
||||
project: projectRef,
|
||||
limit: limit(200, 30),
|
||||
day: z
|
||||
.string()
|
||||
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
||||
.optional()
|
||||
.describe('YYYY-MM-DD: only what happened that day (UTC)'),
|
||||
}),
|
||||
annotations: READ,
|
||||
})
|
||||
activity(
|
||||
{ project, limit }: { project: string; limit: number },
|
||||
{ project, limit, day }: { project: string; limit: number; day?: string },
|
||||
ctx: McpContext,
|
||||
) {
|
||||
const window = day
|
||||
? {
|
||||
after: `${day}T00:00:00.000Z`,
|
||||
before: new Date(
|
||||
Date.parse(`${day}T00:00:00Z`) + 86_400_000,
|
||||
).toISOString(),
|
||||
}
|
||||
: {};
|
||||
return toolResult(async () =>
|
||||
(
|
||||
await this.api.data(
|
||||
this.api.as(ctx).GET('/api/projects/{project}/activity', {
|
||||
params: { path: { project }, query: { limit } },
|
||||
params: { path: { project }, query: { limit, ...window } },
|
||||
}),
|
||||
)
|
||||
).map((a) => ({
|
||||
|
||||
Reference in New Issue
Block a user