import { ChangeDetectionStrategy, Component, computed, input, } from '@angular/core'; import { RouterLink } from '@angular/router'; import { AgendaItem } from '@task-stream/web/tracker/domain'; const DAY = 86_400_000; const KIND: Record = { ISSUE: 'vence', SPRINT_END: 'fin de sprint', RELEASE: 'release', }; /** My coming week: what is due, which sprints end, which releases land. */ @Component({ selector: 'ts-week-agenda', changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink], host: { class: 'ts-panel', role: 'region', 'aria-labelledby': 'agenda-title', }, styleUrl: './home-panel.scss', template: `

Tu semana

{{ count() }} cosas en los próximos 7 días
    @for (day of days(); track day.date) {
  1. {{ day.weekday }} {{ day.number }}

    @for (item of day.items; track $index) { {{ kindOf(item) }} {{ item.issueKey ?? item.projectKey }} {{ item.title }} } @empty { — }
  2. }
`, styles: ` .ts-agenda { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 0.5rem; margin: 0; padding: 0; list-style: none; li { display: grid; align-content: start; gap: 0.25rem; min-height: 6rem; padding: 0.5rem; border-radius: 0.625rem; background: var(--ts-sunken); } li.is-today { outline: 2px solid var(--ts-brand); outline-offset: -2px; } } .ts-agenda__day { margin: 0 0 0.25rem; color: var(--ts-muted); font-size: 0.75rem; text-transform: capitalize; strong { color: var(--ts-ink); font-size: 1rem; } } .ts-agenda__item { display: grid; gap: 0.0625rem; padding: 0.25rem 0.375rem; border-left: 3px solid var(--ts-brand); border-radius: 0.375rem; background: var(--ts-surface); color: inherit; font-family: var(--ts-font-mono); font-size: 0.6875rem; text-decoration: none; &[data-kind='SPRINT_END'] { border-left-color: var(--ts-c-amber, #c98a12); } &[data-kind='RELEASE'] { border-left-color: var(--ts-c-green, #2e9e6a); } &:hover { background: var(--pi-color-primary-25); } } .ts-agenda__kind { color: var(--ts-muted); font-family: inherit; } .ts-agenda__title { overflow: hidden; font-family: var(--ts-font-body, inherit); font-size: 0.75rem; text-overflow: ellipsis; white-space: nowrap; } .ts-agenda__none { color: var(--ts-muted); font-size: 0.75rem; } @media (max-width: 720px) { .ts-agenda { grid-template-columns: minmax(0, 1fr); li { min-height: 0; } /* On a phone, only the days with something (and today). */ li.is-empty:not(.is-today) { display: none; } } } `, }) export class WeekAgendaComponent { readonly items = input.required(); readonly now = input(new Date()); protected readonly count = computed(() => this.items().length); protected readonly days = computed(() => { const now = this.now(); const start = Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()); return Array.from({ length: 7 }, (_, n) => { const time = start + n * DAY; const date = new Date(time).toISOString().slice(0, 10); return { date, today: n === 0, weekday: new Date(time).toLocaleDateString('es', { weekday: 'short', timeZone: 'UTC', }), number: new Date(time).getUTCDate(), items: this.items().filter((i) => i.day === date), }; }); }); protected kindOf(item: AgendaItem): string { return KIND[item.kind]; } protected link(item: AgendaItem): unknown[] { return item.issueKey ? ['/p', item.projectKey, 'issues', item.issueKey] : [ '/p', item.projectKey, item.kind === 'RELEASE' ? 'releases' : 'sprints', ]; } }