import { ChangeDetectionStrategy, Component, inject, TemplateRef, viewChild, } from '@angular/core'; import { Router } from '@angular/router'; import { PiActionButtonComponent, PiBadgeComponent, PiIconButtonComponent, PiIconComponent, PiCheckboxComponent, } from '@pi/ui'; import { DrawerService } from '@task-stream/web/shared/ui'; import { InboxFacade, PushFacade } from '@task-stream/web/tracker/application'; import { Notification, NotificationKind, } from '@task-stream/web/tracker/domain'; import { timeAgo } from './copy'; const ICON: Record = { [NotificationKind.ASSIGNED]: 'user-profile', [NotificationKind.STATUS_CHANGED]: 'arrow-forward', [NotificationKind.QA_FAILED]: 'exclamation', [NotificationKind.COMMENTED]: 'chat', [NotificationKind.MENTIONED]: 'chat', [NotificationKind.REPLIED]: 'chat', [NotificationKind.PROPOSED]: 'light-bulb', [NotificationKind.WATCHED]: 'focus', [NotificationKind.ADDED_TO_PROJECT]: 'briefcase', [NotificationKind.REMINDER]: 'calendar', }; /** * The bell in the header: how many unread, and the list in a side panel. * Opening one goes to its ticket (or project) and marks it read. */ @Component({ selector: 'ts-notifications-bell', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ PiActionButtonComponent, PiBadgeComponent, PiIconButtonComponent, PiIconComponent, PiCheckboxComponent, ], template: `
@if (inbox.settings()?.emailAvailable) { } @if (inbox.settings()?.pushPublicKey && push.supported) { }
@if (push.error(); as error) { } @if (inbox.items().length) {
    @for (item of inbox.items(); track item.id) {
  • }
} @else {

Nada nuevo. Te avisaremos cuando te asignen algo, comenten en tus tickets o los muevan.

}
`, styles: ` :host { display: inline-flex; align-items: center; } .ts-inbox { display: grid; gap: 1rem; } .ts-inbox__actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem; } .ts-inbox__error { margin: 0; color: var(--ts-c-red, var(--ts-ink)); font-size: 0.8125rem; } .ts-inbox__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; } /* As tall as its text: past @pi/ui's global button look (a fixed height that let a two-line notice run over the next one). */ .ts-inbox__item { width: 100%; height: auto !important; min-width: 0 !important; display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.625rem 0.5rem !important; line-height: 1.35; border: 0; border-radius: 0.5rem; background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer; } .ts-inbox__item:hover, .ts-inbox__item:focus-visible { background: rgba(127, 127, 127, 0.12); } .ts-inbox__item.is-unread .ts-inbox__summary { font-weight: 600; } .ts-inbox__icon { flex: none; width: 2rem; height: 2rem; border-radius: 50%; display: grid; place-items: center; background: rgba(127, 127, 127, 0.14); } .ts-inbox__text { display: grid; gap: 0.125rem; flex: 1; min-width: 0; } .ts-inbox__meta { font-size: 0.8125rem; opacity: 0.7; } .ts-inbox__dot { flex: none; width: 0.5rem; height: 0.5rem; margin-top: 0.5rem; border-radius: 50%; background: var(--ts-brand, #a347e1); } .ts-inbox__empty { opacity: 0.7; } `, }) export class NotificationsBell { protected readonly inbox = inject(InboxFacade); protected readonly push = inject(PushFacade); private readonly drawer = inject(DrawerService); private readonly router = inject(Router); private readonly panel = viewChild.required>('panel'); protected readonly ago = timeAgo; protected icon = (item: Notification) => ICON[item.kind] ?? 'info'; protected open(): void { void this.push.refresh(); this.drawer.open('Notificaciones', this.panel(), '28rem'); } protected togglePush(on: boolean): void { const key = this.inbox.settings()?.pushPublicKey; if (on && key) void this.push.turnOn(key); else if (!on) void this.push.turnOff(); } protected go(item: Notification): void { if (!item.readAt) this.inbox.markRead([item.id]); this.drawer.closeAll(); const project = encodeURIComponent(item.projectKey); void this.router.navigateByUrl( item.issueKey ? `/p/${project}/issues/${encodeURIComponent(item.issueKey)}` : `/p/${project}`, ); } }