Give the tech radar its own page
/radar (/en/radar, /ca/radar) shows every technology the collective has placed, by ring: logo, category, what it is, why it sits there, its website and how many projects use it. The landing's radar summary links to it. It is the first listing — a page this site composes rather than one record's — so resolveRoute learns about those (LISTINGS), and builds their addresses and hreflang alternates from the same table. Logos sit on a light tile in both themes. A brand's mark is not ours to recolour, and Rust's, which is black, would vanish in dark mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,10 @@ import {
|
||||
input,
|
||||
} from '@angular/core';
|
||||
|
||||
import { copyFor } from '../core/copy';
|
||||
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 { CallDetail } from './call-detail';
|
||||
import type { RoutedPageData, RoutedRecord } from '../core/routed-page';
|
||||
import { ContentPage } from './content-page';
|
||||
@@ -20,7 +23,14 @@ import { ProjectDetail } from './project-detail';
|
||||
@Component({
|
||||
selector: 'app-routed-page',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CallDetail, ContentPage, MemberDetail, PageShell, ProjectDetail],
|
||||
imports: [
|
||||
CallDetail,
|
||||
ContentPage,
|
||||
MemberDetail,
|
||||
PageShell,
|
||||
ProjectDetail,
|
||||
RadarPage,
|
||||
],
|
||||
template: `
|
||||
<app-page-shell [data]="data()" [seo]="seo()">
|
||||
@if (data().found; as found) {
|
||||
@@ -47,6 +57,9 @@ import { ProjectDetail } from './project-detail';
|
||||
[locale]="data().locale"
|
||||
/>
|
||||
}
|
||||
@case ('radar') {
|
||||
<app-radar-page [technologies]="found.record" [copy]="copy()" />
|
||||
}
|
||||
@case ('member') {
|
||||
<app-member-detail
|
||||
[member]="found.record"
|
||||
@@ -64,11 +77,17 @@ export class RoutedPage {
|
||||
|
||||
protected readonly copy = computed(() => copyFor(this.data().locale));
|
||||
|
||||
protected readonly seo = computed(() => seoFor(this.data().found));
|
||||
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): PageSeo | null {
|
||||
export function seoFor(
|
||||
found: RoutedRecord | null,
|
||||
copy: Copy,
|
||||
locale: Locale,
|
||||
): PageSeo | null {
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
@@ -107,6 +126,14 @@ export function seoFor(found: RoutedRecord | null): PageSeo | null {
|
||||
type: 'profile',
|
||||
};
|
||||
}
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user