import { ChangeDetectionStrategy, Component, computed, input, } from '@angular/core'; import { PiActionButtonComponent, PiAvatarComponent, PiChipComponent, PiChipStatusComponent, PiTextColorDirective, PiTextLargeDirective, PiTextWeightBoldDirective, } from '@pi/ui'; import type { Locale } from '../core/api'; import type { PublicProject } from '../core/api.types'; import type { Copy } from '../core/copy'; import { ApiImage } from './api-image'; import { ContactCta } from './contact-cta'; import { PROJECT_STATUS_TONES } from './project-card'; /** * A project on its own address: what it is, why it was built the way it was, * what went wrong, who made it, and where the code is. * * `description`, `decisions` and `postmortem` are HTML from the panel's rich * editor and go through Angular's sanitiser, as a page's body does. */ @Component({ selector: 'app-project-detail', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ ApiImage, ContactCta, PiActionButtonComponent, PiAvatarComponent, PiChipComponent, PiChipStatusComponent, PiTextColorDirective, PiTextLargeDirective, PiTextWeightBoldDirective, ], styleUrl: './project-detail.scss', template: `

{{ copy().project.details }}

{{ project().title }}

@if (project().tagline; as tagline) {

{{ tagline }}

} @if (project().demo_url || repositoryUrl()) {
@if (project().demo_url; as demo) { } @if (repositoryUrl(); as repository) { }
}
@if (project().cover_image; as cover) {
} @if (project().description; as description) {
} @if (project().decisions; as decisions) {

{{ copy().project.decisions }}

} @if (project().postmortem; as postmortem) {

{{ copy().project.postmortem }}

} @if ((project().gallery ?? []).length > 0) {

{{ copy().project.gallery }}

}
`, }) export class ProjectDetail { readonly project = input.required(); readonly copy = input.required(); readonly locale = input.required(); protected readonly statusTone = computed( () => PROJECT_STATUS_TONES[this.project().status], ); protected readonly repositoryUrl = computed( () => (this.project().repositories ?? []).find((repository) => repository.url) ?.url ?? null, ); /** "marzo de 2025 – junio de 2026", or just the start while it runs. */ protected readonly period = computed(() => { const { started_at, ended_at } = this.project(); const format = (date?: string) => date ? new Intl.DateTimeFormat(this.locale(), { month: 'long', year: 'numeric', }).format(new Date(date)) : null; const [start, end] = [format(started_at), format(ended_at)]; return start && end ? `${start} – ${end}` : (start ?? end); }); }