import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core'; import { PiCardBottomContentDirective, PiCardCenterContentDirective, PiCardComponent, PiCardTopContentDirective, PiChipComponent, PiChipStatusComponent, PiTextColorDirective, PiTextSmallDirective, PiTextViewerLinkComponent, PiTextWeightBoldDirective, } from '@pi/ui'; import type { Copy } from '../core/copy'; import type { PublicProject } from '../core/api.types'; import { ApiImage } from './api-image'; @Component({ selector: 'app-project-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ ApiImage, PiCardComponent, PiCardTopContentDirective, PiCardCenterContentDirective, PiCardBottomContentDirective, PiChipComponent, PiChipStatusComponent, PiTextViewerLinkComponent, PiTextSmallDirective, PiTextColorDirective, PiTextWeightBoldDirective, ], template: ` @if (project().cover_image; as cover) { } @if (project().needs_help) { } @if (stars() > 0) { } {{ project().title }} @if (project().tagline; as tagline) { {{ tagline }} } @if (technologies().length > 0) { @for (technology of technologies(); track technology.id) { } } @if (project().demo_url; as demo) { } @if (repositoryUrl(); as repository) { } `, }) export class ProjectCard { readonly project = input.required(); readonly copy = input.required(); /** Enough to read the stack at a glance; the rest is noise on a card. */ protected readonly technologies = computed(() => (this.project().technologies ?? []).slice(0, 5) ); protected readonly stars = computed(() => (this.project().repositories ?? []).reduce( (total, repository) => total + (repository.stars ?? 0), 0 ) ); protected readonly repositoryUrl = computed( () => (this.project().repositories ?? []).find((repo) => repo.url)?.url ?? null ); /** The API's five project states onto the chip's five tones. */ protected readonly statusTone = computed(() => { const tones: Record< PublicProject['status'], 'success' | 'error' | 'pending' | 'info' | 'disabled' > = { idea: 'info', active: 'success', paused: 'pending', completed: 'info', archived: 'disabled', }; return tones[this.project().status]; }); }
{{ tagline }}