import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { RouterLink } from '@angular/router';
import { PiEmptyStateComponent } from '@pi/ui';
import { ProjectFacade } from '@task-stream/web/tracker/application';
import { clock, initials, timeAgo } from '../ui/copy';
/** Live feed of everything that happens in the project, as a log. */
@Component({
selector: 'ts-activity-page',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [RouterLink, PiEmptyStateComponent],
template: `
En directo
{{ facade.activity().length }} eventos
@if (facade.activity().length) {
@for (entry of facade.activity(); track entry.id) {
{{
clock(entry.occurredAt)
}}
{{
initialsOf(entry.actor.name)
}}
{{ entry.summary }}
@if (entry.issueKey) {
{{
entry.issueKey
}}
} @else {
}
{{ ago(entry.occurredAt) }}
}
} @else {
}
`,
styles: `
.ts-log-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.75rem;
.ts-eyebrow {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.ts-muted {
font-size: 0.75rem;
}
}
.ts-live {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
background: var(--ts-s-done);
animation: ts-pulse 2s ease-out infinite;
}
@keyframes ts-pulse {
0% {
box-shadow: 0 0 0 0
color-mix(in srgb, var(--ts-s-done) 60%, transparent);
}
70%,
100% {
box-shadow: 0 0 0 0.5rem transparent;
}
}
.ts-log {
margin: 0;
padding: 0.25rem 0;
list-style: none;
li {
display: grid;
grid-template-columns: 3.5rem 1.75rem minmax(0, 1fr) auto 8.5rem;
align-items: center;
gap: 0.875rem;
margin: 0;
padding: 0.625rem 1.125rem;
border-bottom: 1px dashed var(--ts-line);
animation: ts-in 0.35s ease-out;
&:last-child {
border-bottom: 0;
}
&:first-child {
animation-name: ts-in, ts-flash;
animation-duration: 0.35s, 1.6s;
}
}
}
.ts-log__time,
.ts-log__ago {
font-family: var(--ts-font-mono);
font-size: 0.75rem;
color: var(--ts-muted);
}
.ts-log__ago {
text-align: right;
white-space: nowrap;
}
.ts-log__who {
display: inline-grid;
place-items: center;
width: 1.75rem;
height: 1.75rem;
border-radius: 50%;
background: color-mix(in srgb, var(--ts-brand) 16%, var(--ts-surface));
color: color-mix(in srgb, var(--ts-brand) 70%, var(--ts-ink));
font-family: var(--ts-font-mono);
font-size: 0.625rem;
font-weight: 600;
}
.ts-log__what {
font-size: 0.875rem;
color: var(--ts-ink-soft);
}
a.ts-key {
text-decoration: none;
&:hover {
color: var(--ts-brand);
border-color: var(--ts-brand);
}
}
@keyframes ts-in {
from {
opacity: 0;
transform: translateY(-4px);
}
}
@keyframes ts-flash {
from {
background: color-mix(in srgb, var(--ts-brand) 10%, transparent);
}
}
@media (max-width: 640px) {
.ts-log li {
grid-template-columns: 1.75rem minmax(0, 1fr) auto;
gap: 0.625rem;
padding-inline: 0.875rem;
}
.ts-log__time,
.ts-log__ago {
display: none;
}
}
`,
})
export class ActivityPage {
protected readonly facade = inject(ProjectFacade);
protected readonly ago = timeAgo;
protected readonly initialsOf = initials;
protected readonly clock = clock;
}