committed by
Bastian
parent
8d28e4c3a3
commit
70ea8d5bcd
16
README.md
16
README.md
@@ -110,6 +110,22 @@ To change the logo, create a public folder in your application and put your `log
|
||||
|
||||
For reference have a look at [public/logo.svg](./public/logo.svg).
|
||||
|
||||
### Change the rings and quadrants config
|
||||
To change the default rings and quadrants of the radar, you can place a custom `config.json` file within the `public` folder.
|
||||
The content should look as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"quadrants": {
|
||||
"languages-and-frameworks": "Languages & Frameworks",
|
||||
"methods-and-patterns": "Methods & Patterns",
|
||||
"platforms-and-aoe-services": "Platforms & Operations",
|
||||
"tools": "Tools"
|
||||
},
|
||||
"rings":["all", "adopt", "trial", "assess", "hold"]
|
||||
}
|
||||
```
|
||||
|
||||
### Change the index.html
|
||||
To change the index.html, create a public folder in your application and put your `index.html` in it.
|
||||
|
||||
|
||||
9
config_example.json
Normal file
9
config_example.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"quadrants": {
|
||||
"languages-and-frameworks": "Languages & Frameworks",
|
||||
"methods-and-patterns": "Methods & Patterns",
|
||||
"platforms-and-aoe-services": "Platforms & Operations",
|
||||
"tools": "Tools"
|
||||
},
|
||||
"rings":["all", "adopt", "trial", "assess", "hold"]
|
||||
}
|
||||
@@ -39,7 +39,6 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var fs_1 = require("fs");
|
||||
var radar_1 = require("./generateJson/radar");
|
||||
var config_1 = require("../src/config");
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = "production";
|
||||
process.env.NODE_ENV = "production";
|
||||
@@ -50,7 +49,7 @@ process.on("unhandledRejection", function (err) {
|
||||
throw err;
|
||||
});
|
||||
(function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var radar, e_1;
|
||||
var radar, rawConf, config, e_1;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
@@ -61,7 +60,9 @@ process.on("unhandledRejection", function (err) {
|
||||
radar = _a.sent();
|
||||
fs_1.copyFileSync("build/index.html", "build/overview.html");
|
||||
fs_1.copyFileSync("build/index.html", "build/help-and-about-tech-radar.html");
|
||||
config_1.quadrants.forEach(function (quadrant) {
|
||||
rawConf = fs_1.readFileSync("build/config.json", "utf-8");
|
||||
config = JSON.parse(rawConf);
|
||||
Object.keys(config.quadrants).forEach(function (quadrant) {
|
||||
var destFolder = "build/" + quadrant;
|
||||
fs_1.copyFileSync("build/index.html", destFolder + ".html");
|
||||
if (!fs_1.existsSync(destFolder)) {
|
||||
|
||||
@@ -76,13 +76,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createRadar = void 0;
|
||||
var fs_extra_1 = require("fs-extra");
|
||||
var fs_1 = require("fs");
|
||||
var path = __importStar(require("path"));
|
||||
var front_matter_1 = __importDefault(require("front-matter"));
|
||||
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
|
||||
var marked_1 = __importDefault(require("marked"));
|
||||
var highlight_js_1 = __importDefault(require("highlight.js"));
|
||||
var file_1 = require("./file");
|
||||
var config_1 = require("../../src/config");
|
||||
var paths_1 = require("../paths");
|
||||
marked_1.default.setOptions({
|
||||
highlight: function (code) { return highlight_js_1.default.highlightAuto(code).value; },
|
||||
});
|
||||
@@ -108,11 +109,14 @@ var createRadar = function () { return __awaiter(void 0, void 0, void 0, functio
|
||||
}); };
|
||||
exports.createRadar = createRadar;
|
||||
var checkAttributes = function (fileName, attributes) {
|
||||
if (attributes.ring && !config_1.rings.includes(attributes.ring)) {
|
||||
throw new Error("Error: " + fileName + " has an illegal value for 'ring' - must be one of " + config_1.rings);
|
||||
var rawConf = fs_1.readFileSync(path.resolve(paths_1.appBuild, 'config.json'), 'utf-8');
|
||||
var config = JSON.parse(rawConf);
|
||||
if (attributes.ring && !config.rings.includes(attributes.ring)) {
|
||||
throw new Error("Error: " + fileName + " has an illegal value for 'ring' - must be one of " + config.rings);
|
||||
}
|
||||
if (attributes.quadrant && !config_1.quadrants.includes(attributes.quadrant)) {
|
||||
throw new Error("Error: " + fileName + " has an illegal value for 'quadrant' - must be one of " + config_1.quadrants);
|
||||
var quadrants = Object.keys(config.quadrants);
|
||||
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
|
||||
throw new Error("Error: " + fileName + " has an illegal value for 'quadrant' - must be one of " + quadrants);
|
||||
}
|
||||
return attributes;
|
||||
};
|
||||
@@ -120,19 +124,21 @@ var createRevisionsFromFiles = function (fileNames) {
|
||||
var publicUrl = process.env.PUBLIC_URL;
|
||||
return Promise.all(fileNames.map(function (fileName) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
fs_extra_1.readFile(fileName, "utf8", function (err, data) {
|
||||
fs_extra_1.readFile(fileName, "utf8", function (err, data) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var fm, html;
|
||||
return __generator(this, function (_a) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
var fm = front_matter_1.default(data);
|
||||
// add target attribute to external links
|
||||
// todo: check path
|
||||
var html = marked_1.default(fm.body.replace(/\]\(\//g, "](" + publicUrl + "/"));
|
||||
fm = front_matter_1.default(data);
|
||||
html = marked_1.default(fm.body.replace(/\]\(\//g, "](" + publicUrl + "/"));
|
||||
html = html.replace(/a href="http/g, 'a target="_blank" rel="noopener noreferrer" href="http');
|
||||
resolve(__assign(__assign(__assign({}, itemInfoFromFilename(fileName)), checkAttributes(fileName, fm.attributes)), { fileName: fileName, body: html }));
|
||||
}
|
||||
return [2 /*return*/];
|
||||
});
|
||||
}); });
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
9
public/config.json
Normal file
9
public/config.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"quadrants": {
|
||||
"languages-and-frameworks": "Languages & Frameworks",
|
||||
"methods-and-patterns": "Methods & Patterns",
|
||||
"platforms-and-aoe-services": "Platforms & Operations",
|
||||
"tools": "Tools"
|
||||
},
|
||||
"rings":["all", "adopt", "trial", "assess", "hold"]
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { copyFileSync, mkdirSync, existsSync } from "fs";
|
||||
import { copyFileSync, mkdirSync, existsSync, readFileSync } from "fs";
|
||||
import { createRadar } from "./generateJson/radar";
|
||||
import { quadrants } from "../src/config";
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = "production";
|
||||
@@ -22,8 +21,9 @@ process.on("unhandledRejection", (err) => {
|
||||
|
||||
copyFileSync("build/index.html", "build/overview.html");
|
||||
copyFileSync("build/index.html", "build/help-and-about-tech-radar.html");
|
||||
|
||||
quadrants.forEach((quadrant) => {
|
||||
const rawConf = readFileSync("build/config.json", "utf-8");
|
||||
const config = JSON.parse(rawConf);
|
||||
Object.keys(config.quadrants).forEach((quadrant) => {
|
||||
const destFolder = `build/${quadrant}`;
|
||||
copyFileSync("build/index.html", `${destFolder}.html`);
|
||||
if (!existsSync(destFolder)) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { readFile } from "fs-extra";
|
||||
import { readFileSync } from "fs";
|
||||
import * as path from "path";
|
||||
import frontMatter from "front-matter";
|
||||
// @ts-ignore esModuleInterop is activated in tsconfig.scripts.json, but IDE typescript uses default typescript config
|
||||
@@ -6,8 +7,8 @@ import marked from "marked";
|
||||
import highlight from "highlight.js";
|
||||
|
||||
import { radarPath, getAllMarkdownFiles } from "./file";
|
||||
import { quadrants, rings } from "../../src/config";
|
||||
import { Item, Revision, ItemAttributes, Radar } from "../../src/model";
|
||||
import { appBuild } from "../paths";
|
||||
|
||||
type FMAttributes = ItemAttributes;
|
||||
|
||||
@@ -29,12 +30,16 @@ export const createRadar = async (): Promise<Radar> => {
|
||||
};
|
||||
|
||||
const checkAttributes = (fileName: string, attributes: FMAttributes) => {
|
||||
if (attributes.ring && !rings.includes(attributes.ring)) {
|
||||
const rawConf = readFileSync(path.resolve(appBuild, 'config.json'), 'utf-8');
|
||||
const config = JSON.parse(rawConf);
|
||||
|
||||
if (attributes.ring && !config.rings.includes(attributes.ring)) {
|
||||
throw new Error(
|
||||
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${rings}`
|
||||
`Error: ${fileName} has an illegal value for 'ring' - must be one of ${config.rings}`
|
||||
);
|
||||
}
|
||||
|
||||
const quadrants = Object.keys(config.quadrants);
|
||||
if (attributes.quadrant && !quadrants.includes(attributes.quadrant)) {
|
||||
throw new Error(
|
||||
`Error: ${fileName} has an illegal value for 'quadrant' - must be one of ${quadrants}`
|
||||
@@ -50,7 +55,7 @@ const createRevisionsFromFiles = (fileNames: string[]) => {
|
||||
fileNames.map(
|
||||
(fileName) =>
|
||||
new Promise<Revision>((resolve, reject) => {
|
||||
readFile(fileName, "utf8", (err, data) => {
|
||||
readFile(fileName, "utf8", async (err, data) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from "react-router-dom";
|
||||
import { Item } from "../model";
|
||||
import { Messages, MessagesProvider } from "../context/MessagesContext";
|
||||
import { ConfigData } from "../config";
|
||||
|
||||
interface Params {
|
||||
page: string;
|
||||
@@ -40,9 +41,11 @@ const useQuery = () => new URLSearchParams(useLocation().search);
|
||||
const RouterWithPageParam = ({
|
||||
items,
|
||||
releases,
|
||||
config,
|
||||
}: {
|
||||
items: Item[];
|
||||
releases: string[];
|
||||
config: ConfigData;
|
||||
}) => {
|
||||
const { page } = useParams<Params>();
|
||||
const query = useQuery();
|
||||
@@ -53,6 +56,7 @@ const RouterWithPageParam = ({
|
||||
search={query.get("search") || ""}
|
||||
items={items}
|
||||
releases={releases}
|
||||
config={config}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -79,8 +83,11 @@ export default function App() {
|
||||
const messages = useFetch<Messages>(
|
||||
`${process.env.PUBLIC_URL}/messages.json`
|
||||
);
|
||||
const config = useFetch<ConfigData>(
|
||||
`${process.env.PUBLIC_URL}/config.json`
|
||||
);
|
||||
|
||||
if (data) {
|
||||
if (data && config) {
|
||||
const { items, releases } = data;
|
||||
return (
|
||||
<MessagesProvider messages={messages}>
|
||||
@@ -93,7 +100,7 @@ export default function App() {
|
||||
<HeaderWithPageParam />
|
||||
</div>
|
||||
<div className={classNames("page__content")}>
|
||||
<RouterWithPageParam items={items} releases={releases} />
|
||||
<RouterWithPageParam config={config} items={items} releases={releases} />
|
||||
</div>
|
||||
<div className="page__footer">
|
||||
<FooterWithPageParam items={items} />
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { MouseEventHandler } from "react";
|
||||
import classNames from "classnames";
|
||||
import "./badge.scss";
|
||||
import { Ring } from "../../config";
|
||||
type BadgeProps = {
|
||||
onClick?: MouseEventHandler;
|
||||
big?: boolean;
|
||||
type: "big" | "all" | "empty" | Ring;
|
||||
type: "big" | "all" | "empty" | string;
|
||||
};
|
||||
|
||||
export default function Badge({
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from "react";
|
||||
import { formatRelease } from "../../date";
|
||||
import { featuredOnly, Item } from "../../model";
|
||||
import HeroHeadline from "../HeroHeadline/HeroHeadline";
|
||||
import QuadrantGrid from "../QuadrantGrid/QuadrantGrid";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import { radarName, radarNameShort } from "../../config";
|
||||
import { ConfigData, radarName, radarNameShort } from "../../config";
|
||||
import { MomentInput } from "moment";
|
||||
import { useMessages } from "../../context/MessagesContext";
|
||||
|
||||
@@ -13,6 +12,7 @@ type PageIndexProps = {
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
items: Item[];
|
||||
config: ConfigData;
|
||||
releases: MomentInput[];
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ export default function PageIndex({
|
||||
leaving,
|
||||
onLeave,
|
||||
items,
|
||||
config,
|
||||
releases,
|
||||
}: PageIndexProps) {
|
||||
const { pageIndex } = useMessages();
|
||||
@@ -35,7 +36,7 @@ export default function PageIndex({
|
||||
{radarName}
|
||||
</HeroHeadline>
|
||||
</div>
|
||||
<QuadrantGrid items={featuredOnly(items)} />
|
||||
<QuadrantGrid items={featuredOnly(items)} config={config} />
|
||||
<div className="publish-date">
|
||||
{publishedLabel} {formatRelease(newestRelease)}
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import SetTitle from "../SetTitle";
|
||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||
import { useAnimations } from "./useAnimations";
|
||||
import "./item-page.scss";
|
||||
import { translate } from "../../config";
|
||||
import { ConfigData, translate } from "../../config";
|
||||
import {
|
||||
groupByQuadrants,
|
||||
Item,
|
||||
@@ -29,11 +29,12 @@ const getItemsInRing = (pageName: string, items: Item[]) => {
|
||||
type Props = {
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
config: ConfigData;
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
|
||||
const PageItem: React.FC<Props> = ({ pageName, items, leaving, onLeave }) => {
|
||||
const PageItem: React.FC<Props> = ({ pageName, items, config, leaving, onLeave }) => {
|
||||
const { pageItem } = useMessages();
|
||||
const quadrantOverview = pageItem?.quadrantOverview || 'Quadrant Overview';
|
||||
|
||||
@@ -57,7 +58,7 @@ const PageItem: React.FC<Props> = ({ pageName, items, leaving, onLeave }) => {
|
||||
className="item-page__header"
|
||||
style={getAnimationState("navHeader")}
|
||||
>
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
<h3 className="headline">{translate(config, item.quadrant)}</h3>
|
||||
</div>
|
||||
<ItemList
|
||||
items={itemsInRing}
|
||||
|
||||
@@ -6,12 +6,13 @@ import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import ItemRevisions from "../ItemRevisions/ItemRevisions";
|
||||
|
||||
import { translate } from "../../config";
|
||||
import { ConfigData, translate } from "../../config";
|
||||
import { groupByQuadrants, Item } from "../../model";
|
||||
|
||||
type PageItemMobileProps = {
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
config: ConfigData;
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
@@ -19,6 +20,7 @@ type PageItemMobileProps = {
|
||||
export default function PageItemMobile({
|
||||
pageName,
|
||||
items,
|
||||
config,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: PageItemMobileProps) {
|
||||
@@ -47,7 +49,7 @@ export default function PageItemMobile({
|
||||
<div className="mobile-item-page__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
<h3 className="headline">{translate(config, item.quadrant)}</h3>
|
||||
<h1 className="hero-headline hero-headline--inverse">
|
||||
{item.title}
|
||||
</h1>
|
||||
@@ -73,7 +75,7 @@ export default function PageItemMobile({
|
||||
<ItemList items={itemsInRing} activeItem={item}>
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h3 className="headline">{translate(item.quadrant)}</h3>
|
||||
<h3 className="headline">{translate(config, item.quadrant)}</h3>
|
||||
</div>
|
||||
<div className="split__right">
|
||||
<Link className="icon-link" pageName={item.quadrant}>
|
||||
|
||||
@@ -8,8 +8,8 @@ import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
import Flag from "../Flag/Flag";
|
||||
import { groupByFirstLetter, Item } from "../../model";
|
||||
import { ConfigData, translate } from "../../config";
|
||||
import { useMessages } from "../../context/MessagesContext";
|
||||
import { translate, Ring } from "../../config";
|
||||
|
||||
const containsSearchTerm = (text = "", term = "") => {
|
||||
// TODO search refinement
|
||||
@@ -20,9 +20,10 @@ const containsSearchTerm = (text = "", term = "") => {
|
||||
};
|
||||
|
||||
type PageOverviewProps = {
|
||||
rings: readonly ("all" | Ring)[];
|
||||
rings: readonly ("all" | string)[];
|
||||
search: string;
|
||||
items: Item[];
|
||||
config: ConfigData;
|
||||
leaving: boolean;
|
||||
onLeave: () => void;
|
||||
};
|
||||
@@ -31,10 +32,11 @@ export default function PageOverview({
|
||||
rings,
|
||||
search: searchProp,
|
||||
items,
|
||||
config,
|
||||
leaving,
|
||||
onLeave,
|
||||
}: PageOverviewProps) {
|
||||
const [ring, setRing] = useState<Ring | "all">("all");
|
||||
const [ring, setRing] = useState<string | "all">("all");
|
||||
const [search, setSearch] = useState(searchProp);
|
||||
const { pageOverview } = useMessages();
|
||||
const title = pageOverview?.title || 'Technologies Overview';
|
||||
@@ -46,7 +48,7 @@ export default function PageOverview({
|
||||
setSearch(searchProp);
|
||||
}, [rings, searchProp]);
|
||||
|
||||
const handleRingClick = (ring: Ring) => () => {
|
||||
const handleRingClick = (ring: string) => () => {
|
||||
setRing(ring);
|
||||
};
|
||||
|
||||
@@ -134,7 +136,7 @@ export default function PageOverview({
|
||||
<div className="split__right">
|
||||
<div className="nav nav--relations">
|
||||
<div className="nav__item">
|
||||
{translate(item.quadrant)}
|
||||
{translate(config, item.quadrant)}
|
||||
</div>
|
||||
<div className="nav__item">
|
||||
<Badge type={item.ring}>{item.ring}</Badge>
|
||||
|
||||
@@ -5,7 +5,7 @@ import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||
import Fadeable from "../Fadeable/Fadeable";
|
||||
import SetTitle from "../SetTitle";
|
||||
|
||||
import { translate } from "../../config";
|
||||
import { ConfigData, translate } from "../../config";
|
||||
import { featuredOnly, groupByQuadrants, Item } from "../../model";
|
||||
|
||||
type PageQuadrantProps = {
|
||||
@@ -13,6 +13,7 @@ type PageQuadrantProps = {
|
||||
onLeave: () => void;
|
||||
pageName: string;
|
||||
items: Item[];
|
||||
config: ConfigData;
|
||||
};
|
||||
|
||||
export default function PageQuadrant({
|
||||
@@ -20,15 +21,16 @@ export default function PageQuadrant({
|
||||
onLeave,
|
||||
pageName,
|
||||
items,
|
||||
config,
|
||||
}: PageQuadrantProps) {
|
||||
const groups = groupByQuadrants(featuredOnly(items));
|
||||
return (
|
||||
<Fadeable leaving={leaving} onLeave={onLeave}>
|
||||
<SetTitle title={translate(pageName)} />
|
||||
<SetTitle title={translate(config, pageName)} />
|
||||
<HeadlineGroup>
|
||||
<HeroHeadline>{translate(pageName)}</HeroHeadline>
|
||||
<HeroHeadline>{translate(config, pageName)}</HeroHeadline>
|
||||
</HeadlineGroup>
|
||||
<QuadrantSection groups={groups} quadrantName={pageName} big />
|
||||
<QuadrantSection groups={groups} quadrantName={pageName} config={config} big />
|
||||
</Fadeable>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import React from "react";
|
||||
import { groupByQuadrants, Item, Group } from "../../model";
|
||||
import { quadrants } from "../../config";
|
||||
import { ConfigData } from "../../config";
|
||||
import QuadrantSection from "../QuadrantSection/QuadrantSection";
|
||||
import "./quadrant-grid.scss";
|
||||
const renderQuadrant = (quadrantName: string, groups: Group) => {
|
||||
const renderQuadrant = (quadrantName: string, groups: Group, config: ConfigData) => {
|
||||
return (
|
||||
<div key={quadrantName} className="quadrant-grid__quadrant">
|
||||
<QuadrantSection quadrantName={quadrantName} groups={groups} />
|
||||
<QuadrantSection quadrantName={quadrantName} groups={groups} config={config} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function QuadrantGrid({ items }: { items: Item[] }) {
|
||||
export default function QuadrantGrid({ items, config }: { items: Item[], config: ConfigData }) {
|
||||
const groups = groupByQuadrants(items);
|
||||
return (
|
||||
<div className="quadrant-grid">
|
||||
{quadrants.map((quadrantName) => renderQuadrant(quadrantName, groups))}
|
||||
{Object.keys(config.quadrants).map((quadrantName: string) => renderQuadrant(quadrantName, groups, config))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from "react";
|
||||
import { translate, rings, Ring, showEmptyRings } from "../../config";
|
||||
import { translate, showEmptyRings, ConfigData } from "../../config";
|
||||
import Badge from "../Badge/Badge";
|
||||
import Link from "../Link/Link";
|
||||
import ItemList from "../ItemList/ItemList";
|
||||
@@ -7,7 +6,7 @@ import Flag from "../Flag/Flag";
|
||||
import { Group } from "../../model";
|
||||
import "./quadrant-section.scss";
|
||||
const renderList = (
|
||||
ringName: Ring,
|
||||
ringName: string,
|
||||
quadrantName: string,
|
||||
groups: Group,
|
||||
big: boolean
|
||||
@@ -42,7 +41,7 @@ const renderList = (
|
||||
};
|
||||
|
||||
const renderRing = (
|
||||
ringName: Ring,
|
||||
ringName: string,
|
||||
quadrantName: string,
|
||||
groups: Group,
|
||||
big: boolean
|
||||
@@ -65,10 +64,12 @@ const renderRing = (
|
||||
export default function QuadrantSection({
|
||||
quadrantName,
|
||||
groups,
|
||||
config,
|
||||
big = false,
|
||||
}: {
|
||||
quadrantName: string;
|
||||
groups: Group;
|
||||
config: ConfigData;
|
||||
big?: boolean;
|
||||
}) {
|
||||
return (
|
||||
@@ -76,7 +77,7 @@ export default function QuadrantSection({
|
||||
<div className="quadrant-section__header">
|
||||
<div className="split">
|
||||
<div className="split__left">
|
||||
<h4 className="headline">{translate(quadrantName)}</h4>
|
||||
<h4 className="headline">{translate(config, quadrantName)}</h4>
|
||||
</div>
|
||||
{!big && (
|
||||
<div className="split__right">
|
||||
@@ -89,7 +90,7 @@ export default function QuadrantSection({
|
||||
</div>
|
||||
</div>
|
||||
<div className="quadrant-section__rings">
|
||||
{rings.map((ringName) =>
|
||||
{config.rings.map((ringName: string) =>
|
||||
renderRing(ringName, quadrantName, groups, big)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import PageIndex from "./PageIndex/PageIndex";
|
||||
import PageOverview from "./PageOverview/PageOverview";
|
||||
import PageHelp from "./PageHelp/PageHelp";
|
||||
@@ -6,10 +6,9 @@ import PageQuadrant from "./PageQuadrant/PageQuadrant";
|
||||
import PageItem from "./PageItem/PageItem";
|
||||
import PageItemMobile from "./PageItemMobile/PageItemMobile";
|
||||
import {
|
||||
quadrants,
|
||||
ConfigData,
|
||||
getItemPageNames,
|
||||
isMobileViewport,
|
||||
rings,
|
||||
} from "../config";
|
||||
import { Item } from "../model";
|
||||
|
||||
@@ -18,6 +17,7 @@ type RouterProps = {
|
||||
items: Item[];
|
||||
releases: string[];
|
||||
search: string;
|
||||
config: ConfigData;
|
||||
};
|
||||
|
||||
enum page {
|
||||
@@ -30,7 +30,7 @@ enum page {
|
||||
notFound,
|
||||
}
|
||||
|
||||
const getPageByName = (items: Item[], pageName: string): page => {
|
||||
const getPageByName = (items: Item[], pageName: string, config: ConfigData): page => {
|
||||
if (pageName === "index") {
|
||||
return page.index;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const getPageByName = (items: Item[], pageName: string): page => {
|
||||
if (pageName === "help-and-about-tech-radar") {
|
||||
return page.help;
|
||||
}
|
||||
if (quadrants.includes(pageName)) {
|
||||
if (Object.keys(config.quadrants).includes(pageName)) {
|
||||
return page.quadrant;
|
||||
}
|
||||
if (getItemPageNames(items).includes(pageName)) {
|
||||
@@ -55,6 +55,7 @@ export default function Router({
|
||||
items,
|
||||
releases,
|
||||
search,
|
||||
config
|
||||
}: RouterProps) {
|
||||
const [statePageName, setStatePageName] = useState(pageName);
|
||||
const [leaving, setLeaving] = useState(false);
|
||||
@@ -62,14 +63,14 @@ export default function Router({
|
||||
|
||||
useEffect(() => {
|
||||
const nowLeaving =
|
||||
getPageByName(items, pageName) !== getPageByName(items, statePageName);
|
||||
getPageByName(items, pageName, config) !== getPageByName(items, statePageName, config);
|
||||
if (nowLeaving) {
|
||||
setLeaving(true);
|
||||
setNextPageName(pageName);
|
||||
} else {
|
||||
setStatePageName(pageName);
|
||||
}
|
||||
}, [pageName, items, statePageName]);
|
||||
}, [pageName, items, config, statePageName]);
|
||||
|
||||
const handlePageLeave = () => {
|
||||
setStatePageName(nextPageName);
|
||||
@@ -82,12 +83,13 @@ export default function Router({
|
||||
}, 0);
|
||||
};
|
||||
|
||||
switch (getPageByName(items, statePageName)) {
|
||||
switch (getPageByName(items, statePageName, config)) {
|
||||
case page.index:
|
||||
return (
|
||||
<PageIndex
|
||||
leaving={leaving}
|
||||
items={items}
|
||||
config={config}
|
||||
onLeave={handlePageLeave}
|
||||
releases={releases}
|
||||
/>
|
||||
@@ -96,7 +98,8 @@ export default function Router({
|
||||
return (
|
||||
<PageOverview
|
||||
items={items}
|
||||
rings={rings}
|
||||
config={config}
|
||||
rings={config.rings}
|
||||
search={search}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
@@ -110,6 +113,7 @@ export default function Router({
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
items={items}
|
||||
config={config}
|
||||
pageName={statePageName}
|
||||
/>
|
||||
);
|
||||
@@ -117,6 +121,7 @@ export default function Router({
|
||||
return (
|
||||
<PageItemMobile
|
||||
items={items}
|
||||
config={config}
|
||||
pageName={statePageName}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
@@ -126,6 +131,7 @@ export default function Router({
|
||||
return (
|
||||
<PageItem
|
||||
items={items}
|
||||
config={config}
|
||||
pageName={statePageName}
|
||||
leaving={leaving}
|
||||
onLeave={handlePageLeave}
|
||||
|
||||
@@ -1,34 +1,19 @@
|
||||
import { Item } from "./model";
|
||||
|
||||
export interface ConfigData {
|
||||
quadrants: { [key: string]: string };
|
||||
rings: string[];
|
||||
}
|
||||
|
||||
export const radarName =
|
||||
process.env.REACT_APP_RADAR_NAME || "AOE Technology Radar";
|
||||
export const radarNameShort = radarName;
|
||||
|
||||
export const quadrants = [
|
||||
"languages-and-frameworks",
|
||||
"methods-and-patterns",
|
||||
"platforms-and-aoe-services",
|
||||
"tools",
|
||||
];
|
||||
|
||||
export const rings = ["all", "adopt", "trial", "assess", "hold"] as const;
|
||||
|
||||
export type Ring = typeof rings[number];
|
||||
|
||||
export const getItemPageNames = (items: Item[]) =>
|
||||
items.map((item) => `${item.quadrant}/${item.name}`);
|
||||
|
||||
export const showEmptyRings = false;
|
||||
|
||||
const messages: { [k: string]: string } = {
|
||||
"languages-and-frameworks": "Languages & Frameworks",
|
||||
"methods-and-patterns": "Methods & Patterns",
|
||||
"platforms-and-aoe-services": "Platforms & Operations",
|
||||
tools: "Tools",
|
||||
};
|
||||
|
||||
export const translate = (key: string) => messages[key] || "-";
|
||||
|
||||
export function isMobileViewport() {
|
||||
// return false for server side rendering
|
||||
if (typeof window == "undefined") return false;
|
||||
@@ -43,3 +28,7 @@ export function isMobileViewport() {
|
||||
export function assetUrl(file: string) {
|
||||
return process.env.PUBLIC_URL + "/" + file;
|
||||
}
|
||||
|
||||
export function translate(config: ConfigData, key: string) {
|
||||
return config.quadrants[key] || "-";
|
||||
}
|
||||
@@ -6,11 +6,6 @@ interface Quadrant {
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Ring {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Paragraph {
|
||||
headline: string;
|
||||
values: string[];
|
||||
@@ -20,7 +15,7 @@ interface PageHelp {
|
||||
paragraphs: Paragraph[];
|
||||
quadrantsPreDescription?: string;
|
||||
quadrants: Quadrant[];
|
||||
rings: Ring[];
|
||||
rings: {name: string, description: string }[];
|
||||
ringsPreDescription?: string;
|
||||
sourcecodeLink?: {
|
||||
href: string;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Ring } from "./config";
|
||||
|
||||
export type ItemAttributes = {
|
||||
name: string;
|
||||
ring: Ring;
|
||||
ring: string;
|
||||
quadrant: string;
|
||||
title: string;
|
||||
featured?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user