Files
landing-app/landing/src/app/ui/routed-page.ts
pigui 399b8d5450 Add a page for every way to help
/colabora (/en/contribute, /ca/col-labora) — the calls' own section, so
it lists what /colabora/{slug} are — brings the open calls, the good
first issues across the collective's repositories, and a five-step
guide from picking a task to having it reviewed. With nothing open and
nothing labelled, the guide and the other ways to help are still there.
The landing's "contribute" section links to it.

Also fixes the landing's issue list, which @pi/ui's `li + li` margin
spaced twice over.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:14:16 +02:00

189 lines
5.2 KiB
TypeScript

import {
ChangeDetectionStrategy,
Component,
computed,
input,
} from '@angular/core';
import { copyFor, type Copy } from '../core/copy';
import { listingAlternates, listingPath } from '../core/routes';
import type { Locale } from '../core/api';
import { RadarPage } from './radar-page';
import { ServiceDetail } from './service-detail';
import { CallDetail } from './call-detail';
import type { RoutedPageData, RoutedRecord } from '../core/routed-page';
import { ContentPage } from './content-page';
import { ContributePage } from './contribute-page';
import { MemberDetail } from './member-detail';
import { PageShell, type PageSeo } from './page-shell';
import { ProjectDetail } from './project-detail';
/**
* A page reached by its address, whatever it holds: the shell around it, and
* the body for its type inside.
*/
@Component({
selector: 'app-routed-page',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CallDetail,
ContentPage,
ContributePage,
MemberDetail,
PageShell,
ProjectDetail,
RadarPage,
ServiceDetail,
],
template: `
<app-page-shell [data]="data()" [seo]="seo()">
@if (data().found; as found) {
@switch (found.type) {
@case ('page') {
<app-content-page
[page]="found.record"
[pages]="data().pages"
[copy]="copy()"
[locale]="data().locale"
/>
}
@case ('project') {
<app-project-detail
[project]="found.record"
[copy]="copy()"
[locale]="data().locale"
/>
}
@case ('call') {
<app-call-detail
[call]="found.record"
[copy]="copy()"
[locale]="data().locale"
/>
}
@case ('service') {
<app-service-detail
[service]="found.record"
[copy]="copy()"
[locale]="data().locale"
/>
}
@case ('contribute') {
<app-contribute-page
[calls]="found.record.calls"
[issues]="found.record.issues"
[copy]="copy()"
[locale]="data().locale"
/>
}
@case ('radar') {
<app-radar-page [technologies]="found.record" [copy]="copy()" />
}
@case ('member') {
<app-member-detail
[member]="found.record"
[copy]="copy()"
[locale]="data().locale"
/>
}
}
}
</app-page-shell>
`,
})
export class RoutedPage {
readonly data = input.required<RoutedPageData>();
protected readonly copy = computed(() => copyFor(this.data().locale));
protected readonly seo = computed(() =>
seoFor(this.data().found, this.copy(), this.data().locale),
);
}
/** What each type puts in the document head. Null when nothing was found. */
export function seoFor(
found: RoutedRecord | null,
copy: Copy,
locale: Locale,
): PageSeo | null {
if (!found) {
return null;
}
switch (found.type) {
case 'page': {
const page = found.record;
return {
title: page.meta_title || `Pireon — ${page.title}`,
description: page.meta_description || page.excerpt || '',
path: page.path,
alternates: page.alternates ?? {},
type: 'article',
};
}
case 'project': {
const project = found.record;
return {
title: project.meta_title || `Pireon — ${project.title}`,
description: project.meta_description || project.tagline || '',
path: project.path,
alternates: project.alternates ?? {},
type: 'article',
};
}
case 'member': {
const member = found.record;
return {
title: `Pireon — ${member.name}`,
description: member.headline || member.bio?.slice(0, 160) || '',
path: member.path,
alternates: member.alternates ?? {},
type: 'profile',
};
}
case 'service': {
const service = found.record;
return {
title: service.meta_title || `Pireon — ${service.title}`,
description: service.meta_description || service.tagline || '',
path: service.path,
alternates: service.alternates ?? {},
type: 'article',
};
}
case 'contribute':
return {
title: `Pireon — ${copy.contribute.title}`,
description: copy.contribute.lead,
path: listingPath('contribute', locale),
alternates: listingAlternates('contribute'),
type: 'website',
};
case 'radar':
return {
title: `Pireon — ${copy.sections.radar.title}`,
description: copy.radar.lead,
path: listingPath('radar', locale),
alternates: listingAlternates('radar'),
type: 'website',
};
case 'call': {
const call = found.record;
const project = call.project?.title;
return {
title: `Pireon — ${call.title}${project ? ` · ${project}` : ''}`,
description: call.description?.slice(0, 160) || '',
path: call.path,
alternates: call.alternates ?? {},
type: 'article',
};
}
}
}