@dirsigler/astro-techradar 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/config.ts +12 -7
- package/package.json +1 -1
- package/schemas.ts +13 -1
- package/src/layouts/Base.astro +3 -3
- package/src/pages/index.astro +7 -5
- package/src/pages/technology/[...slug].astro +78 -0
package/README.md
CHANGED
|
@@ -109,12 +109,29 @@ Each technology file:
|
|
|
109
109
|
title: Kubernetes
|
|
110
110
|
ring: adopt
|
|
111
111
|
moved: 0
|
|
112
|
+
history:
|
|
113
|
+
- date: "2025-03"
|
|
114
|
+
ring: adopt
|
|
115
|
+
description: "Promoted after 2 years of successful production use."
|
|
116
|
+
- date: "2023-01"
|
|
117
|
+
ring: trial
|
|
118
|
+
description: "Started evaluating for container orchestration."
|
|
119
|
+
- date: "2022-06"
|
|
120
|
+
ring: assess
|
|
112
121
|
---
|
|
113
122
|
|
|
114
123
|
Your description in Markdown. Explain why this technology is in this ring
|
|
115
124
|
and what your experience has been.
|
|
116
125
|
```
|
|
117
126
|
|
|
127
|
+
The `history` field is optional. When present, a timeline is rendered on the technology detail page showing how its ring classification changed over time. Each entry has:
|
|
128
|
+
|
|
129
|
+
| Field | Required | Description |
|
|
130
|
+
| ----- | -------- | ----------- |
|
|
131
|
+
| `date` | yes | Free-text date string (e.g. `"2025-03"`, `"Q1 2025"`) |
|
|
132
|
+
| `ring` | yes | The ring at that point in time (`adopt`, `trial`, `assess`, `hold`) |
|
|
133
|
+
| `description` | no | Short rationale for the change |
|
|
134
|
+
|
|
118
135
|
### 5. Run
|
|
119
136
|
|
|
120
137
|
```bash
|
package/config.ts
CHANGED
|
@@ -18,11 +18,14 @@ export interface ResolvedColorMode {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface TechRadarUserConfig {
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/** Brand name shown in the navbar. */
|
|
22
|
+
name: string;
|
|
23
|
+
|
|
24
|
+
/** Main page title shown as the h1 heading. Falls back to name if not set. */
|
|
25
|
+
title?: string;
|
|
23
26
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
27
|
+
/** Subtitle shown below the title on the main page. */
|
|
28
|
+
subtitle?: string;
|
|
26
29
|
|
|
27
30
|
/**
|
|
28
31
|
* URL path prefix where the tech radar is mounted (e.g. "/techradar").
|
|
@@ -54,8 +57,9 @@ export interface TechRadarUserConfig {
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
export interface ResolvedConfig {
|
|
60
|
+
name: string;
|
|
57
61
|
title: string;
|
|
58
|
-
|
|
62
|
+
subtitle?: string;
|
|
59
63
|
/** Normalized base path with leading slash, no trailing slash. Empty string when mounted at root. */
|
|
60
64
|
basePath: string;
|
|
61
65
|
logo?: string;
|
|
@@ -76,8 +80,9 @@ function normalizeBasePath(raw?: string): string {
|
|
|
76
80
|
|
|
77
81
|
export function resolveConfig(user: TechRadarUserConfig): ResolvedConfig {
|
|
78
82
|
return {
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
name: user.name,
|
|
84
|
+
title: user.title ?? user.name,
|
|
85
|
+
subtitle: user.subtitle,
|
|
81
86
|
basePath: normalizeBasePath(user.basePath),
|
|
82
87
|
logo: user.logo,
|
|
83
88
|
footerText: user.footerText ?? '',
|
package/package.json
CHANGED
package/schemas.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { z } from 'astro/zod';
|
|
2
|
+
import { RINGS } from './src/lib/radar';
|
|
3
|
+
|
|
4
|
+
const ringEnum = z.enum(RINGS);
|
|
2
5
|
|
|
3
6
|
export const segmentSchema = z.object({
|
|
4
7
|
title: z.string(),
|
|
@@ -8,6 +11,15 @@ export const segmentSchema = z.object({
|
|
|
8
11
|
|
|
9
12
|
export const technologySchema = z.object({
|
|
10
13
|
title: z.string(),
|
|
11
|
-
ring:
|
|
14
|
+
ring: ringEnum,
|
|
12
15
|
moved: z.number().int().min(-1).max(1).default(0),
|
|
16
|
+
history: z
|
|
17
|
+
.array(
|
|
18
|
+
z.object({
|
|
19
|
+
date: z.string(),
|
|
20
|
+
ring: ringEnum,
|
|
21
|
+
description: z.string().optional(),
|
|
22
|
+
}),
|
|
23
|
+
)
|
|
24
|
+
.optional(),
|
|
13
25
|
});
|
package/src/layouts/Base.astro
CHANGED
|
@@ -10,7 +10,7 @@ interface Props {
|
|
|
10
10
|
description?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const { title = config.
|
|
13
|
+
const { title = config.name, description = 'Technology Radar — tracking technology adoption across our organization' } = Astro.props;
|
|
14
14
|
const siteBase = import.meta.env.BASE_URL.replace(/\/+$/, '');
|
|
15
15
|
const base = `${siteBase}${config.basePath}/`;
|
|
16
16
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
|
@@ -61,11 +61,11 @@ const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
|
|
61
61
|
{config.logo && (
|
|
62
62
|
<img
|
|
63
63
|
src={config.logo.startsWith('https://') ? config.logo : `${base}${config.logo.replace(/^\//, '')}`}
|
|
64
|
-
alt={config.
|
|
64
|
+
alt={config.name}
|
|
65
65
|
class="h-8 w-auto"
|
|
66
66
|
/>
|
|
67
67
|
)}
|
|
68
|
-
{config.
|
|
68
|
+
{config.name}
|
|
69
69
|
</a>
|
|
70
70
|
{config.color.toggle && <ThemeToggle />}
|
|
71
71
|
</nav>
|
package/src/pages/index.astro
CHANGED
|
@@ -53,12 +53,14 @@ const legendSegments = segments.map((s) => ({
|
|
|
53
53
|
const ringEntries = Object.entries(RING_LABELS) as [string, string][];
|
|
54
54
|
---
|
|
55
55
|
|
|
56
|
-
<Base title={config.
|
|
56
|
+
<Base title={config.name}>
|
|
57
57
|
<div class="text-center mb-10">
|
|
58
|
-
<h1 class="text-4xl font-bold" style="color: var(--radar-text);">{config.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
<h1 class="text-4xl font-bold" style="color: var(--radar-text);">{config.title}</h1>
|
|
59
|
+
{config.subtitle && (
|
|
60
|
+
<p class="mt-3 text-lg" style="color: var(--radar-text-muted);">
|
|
61
|
+
{config.subtitle}
|
|
62
|
+
</p>
|
|
63
|
+
)}
|
|
62
64
|
</div>
|
|
63
65
|
|
|
64
66
|
<!-- Filter bar -->
|
|
@@ -70,6 +70,31 @@ const editUrl = config.editBaseUrl
|
|
|
70
70
|
<Content />
|
|
71
71
|
</div>
|
|
72
72
|
|
|
73
|
+
{entry.data.history && entry.data.history.length > 0 && (
|
|
74
|
+
<section class="mt-10">
|
|
75
|
+
<h2 class="text-xl font-semibold mb-4" style="color: var(--radar-text);">History</h2>
|
|
76
|
+
<ol class="history-timeline">
|
|
77
|
+
{[...entry.data.history].sort((a, b) => b.date.localeCompare(a.date)).map((h) => (
|
|
78
|
+
<li class="history-entry">
|
|
79
|
+
<div class="history-marker">
|
|
80
|
+
<span class={`history-dot ring-${h.ring}`} />
|
|
81
|
+
<span class="history-line" />
|
|
82
|
+
</div>
|
|
83
|
+
<div class="history-content">
|
|
84
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
85
|
+
<RingBadge ring={h.ring} />
|
|
86
|
+
<time class="text-sm" style="color: var(--radar-text-faint);">{h.date}</time>
|
|
87
|
+
</div>
|
|
88
|
+
{h.description && (
|
|
89
|
+
<p class="mt-1 text-sm" style="color: var(--radar-text-muted);">{h.description}</p>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
</li>
|
|
93
|
+
))}
|
|
94
|
+
</ol>
|
|
95
|
+
</section>
|
|
96
|
+
)}
|
|
97
|
+
|
|
73
98
|
{editUrl && (
|
|
74
99
|
<div class="mt-8 pt-6" style="border-top: 1px solid var(--radar-border);">
|
|
75
100
|
<a
|
|
@@ -110,4 +135,57 @@ const editUrl = config.editBaseUrl
|
|
|
110
135
|
.edit-link:hover {
|
|
111
136
|
color: var(--radar-link);
|
|
112
137
|
}
|
|
138
|
+
|
|
139
|
+
.history-timeline {
|
|
140
|
+
list-style: none;
|
|
141
|
+
padding: 0;
|
|
142
|
+
margin: 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.history-entry {
|
|
146
|
+
display: flex;
|
|
147
|
+
gap: 0.75rem;
|
|
148
|
+
position: relative;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.history-marker {
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
align-items: center;
|
|
155
|
+
flex-shrink: 0;
|
|
156
|
+
width: 1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.history-dot {
|
|
160
|
+
width: 0.75rem;
|
|
161
|
+
height: 0.75rem;
|
|
162
|
+
border-radius: 9999px;
|
|
163
|
+
flex-shrink: 0;
|
|
164
|
+
margin-top: 0.375rem;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.history-dot.ring-adopt { background-color: var(--radar-ring-adopt); }
|
|
168
|
+
.history-dot.ring-trial { background-color: var(--radar-ring-trial); }
|
|
169
|
+
.history-dot.ring-assess { background-color: var(--radar-ring-assess); }
|
|
170
|
+
.history-dot.ring-hold { background-color: var(--radar-ring-hold); }
|
|
171
|
+
|
|
172
|
+
.history-line {
|
|
173
|
+
flex: 1;
|
|
174
|
+
width: 2px;
|
|
175
|
+
background-color: var(--radar-border, #e2e8f0);
|
|
176
|
+
margin-top: 0.25rem;
|
|
177
|
+
margin-bottom: 0.25rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.history-entry:last-child .history-line {
|
|
181
|
+
display: none;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.history-content {
|
|
185
|
+
padding-bottom: 1.25rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.history-entry:last-child .history-content {
|
|
189
|
+
padding-bottom: 0;
|
|
190
|
+
}
|
|
113
191
|
</style>
|