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:
@@ -3,6 +3,7 @@ import {
|
||||
Component,
|
||||
computed,
|
||||
input,
|
||||
output,
|
||||
} from '@angular/core';
|
||||
|
||||
const WEEKDAYS = [
|
||||
@@ -55,12 +56,23 @@ const MONTHS = [
|
||||
}}</span>
|
||||
@for (week of weeks(); track week.start) {
|
||||
@if (week.days[row]; as day) {
|
||||
<span
|
||||
class="ts-heat__day"
|
||||
[attr.data-level]="day.level"
|
||||
[class.is-future]="day.future"
|
||||
[attr.title]="day.title"
|
||||
></span>
|
||||
@if (selectable() && day.count && !day.future) {
|
||||
<button
|
||||
type="button"
|
||||
class="ts-heat__day is-selectable"
|
||||
[attr.data-level]="day.level"
|
||||
[attr.title]="day.title + ' · ver qué pasó'"
|
||||
[attr.aria-label]="day.title + '. Ver qué pasó ese día'"
|
||||
(click)="daySelected.emit(day.date)"
|
||||
></button>
|
||||
} @else {
|
||||
<span
|
||||
class="ts-heat__day"
|
||||
[attr.data-level]="day.level"
|
||||
[class.is-future]="day.future"
|
||||
[attr.title]="day.title"
|
||||
></span>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,6 +128,21 @@ const MONTHS = [
|
||||
outline: 1px dashed var(--ts-line);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
/* A plain square, past @pi/ui's global button look. */
|
||||
&.is-selectable {
|
||||
display: block;
|
||||
width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
height: auto !important;
|
||||
padding: 0 !important;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
outline: 2px solid var(--ts-ink);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ts-heat__note {
|
||||
margin-top: 0.875rem;
|
||||
@@ -127,6 +154,10 @@ export class ActivityHeatmapComponent {
|
||||
readonly days = input.required<{ day: string; count: number }[]>();
|
||||
/** Today (`YYYY-MM-DD`), to grey out the rest of this week. */
|
||||
readonly today = input<string>(new Date().toISOString().slice(0, 10));
|
||||
/** Days with activity can be picked (not on the page for clients). */
|
||||
readonly selectable = input(false);
|
||||
/** A day picked, as `YYYY-MM-DD`. */
|
||||
readonly daySelected = output<string>();
|
||||
|
||||
protected readonly total = computed(() =>
|
||||
this.days().reduce((sum, d) => sum + d.count, 0),
|
||||
@@ -139,7 +170,13 @@ export class ActivityHeatmapComponent {
|
||||
const weeks: {
|
||||
start: string;
|
||||
month: string;
|
||||
days: { level: number; future: boolean; title: string }[];
|
||||
days: {
|
||||
date: string;
|
||||
count: number;
|
||||
level: number;
|
||||
future: boolean;
|
||||
title: string;
|
||||
}[];
|
||||
}[] = [];
|
||||
for (let w = 0; w * 7 < days.length; w++) {
|
||||
const week = days.slice(w * 7, w * 7 + 7);
|
||||
@@ -162,6 +199,8 @@ export class ActivityHeatmapComponent {
|
||||
days: week.map((d) => {
|
||||
const future = d.day > this.today();
|
||||
return {
|
||||
date: d.day,
|
||||
count: d.count,
|
||||
future,
|
||||
level: d.count ? Math.min(4, Math.ceil((d.count / max) * 4)) : 0,
|
||||
title: future
|
||||
|
||||
Reference in New Issue
Block a user