Category metadata / icon support

This commit is contained in:
Adrian Rumpold
2025-07-10 08:33:42 +02:00
parent 461820cb70
commit a690718192
9 changed files with 1204 additions and 51 deletions

View File

@@ -2,8 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/aai-favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preconnect" href="https://rsms.me/" />
@@ -12,7 +13,12 @@
href="https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet"
/>
<title>Vite + React</title>
<!-- Material Icons font -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/icon?family=Material+Icons"
/>
<title>AI Skills Framework</title>
</head>
<body>
<div id="root"></div>

1165
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,9 +10,12 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.2.0",
"d3": "^7.9.0",
"react": "^19.1.0",
"react-dom": "^19.1.0"
"react-dom": "^19.1.0",
"sass": "^1.89.2"
},
"devDependencies": {
"@eslint/js": "^9.30.1",

BIN
public/aai-favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -4,14 +4,20 @@ import Legend from "./components/Legend";
import QRCode from "./components/QRCode";
import { QuestionGroupChart } from "./components/QuestionGroupChart";
import { config } from "./config";
import { getSampleData } from "./lib/data";
import { ResponseData } from "./lib/parser";
import { CategoryMetadata, fetchCategoryMetadata } from "./lib/metadata";
import { fetchGoogleSheet, ResponseData } from "./lib/parser";
function App() {
const [data, setData] = useState<ResponseData[]>([]);
const [categoryMetadata, setCategoryMetadata] = useState<CategoryMetadata[]>(
[]
);
useEffect(() => {
// fetchGoogleSheet().then(setData);
setData(getSampleData());
fetchGoogleSheet().then(setData);
// setData(getSampleData());
fetchCategoryMetadata().then(setCategoryMetadata);
}, []);
if (!data.length) return null;
@@ -38,6 +44,7 @@ function App() {
<QuestionGroupChart
key={question}
question={question}
metadata={categoryMetadata.find((m) => m.category === question)}
groupData={groupData}
responses={sortedResponses}
xScale={xScale}

View File

@@ -1,11 +1,15 @@
import MaterialIcon from "@mui/material/Icon";
import * as d3 from "d3";
import { useEffect, useRef } from "react";
import { colorScheme, config } from "../config";
import { CategoryMetadata } from "../lib/metadata";
import "./Chart.css";
interface QuestionGroupChartProps {
question: string;
metadata?: CategoryMetadata;
groupData: { response: string }[];
responses: string[];
xScale: d3.ScaleBand<string>;
@@ -38,6 +42,7 @@ function makeDot(
export function QuestionGroupChart({
question,
metadata,
groupData,
responses,
xScale,
@@ -81,13 +86,11 @@ export function QuestionGroupChart({
return (
<div className="question-group">
<svg ref={svgRef}></svg>
<div className="question-title">{question}</div>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo.
</p>
<div className="question-title">
{metadata?.icon && <MaterialIcon>{metadata.icon}</MaterialIcon>}
{question}
</div>
<p>{metadata?.description || question}</p>
</div>
);
}

View File

@@ -23,3 +23,6 @@ const aaiColors = [
export const colorScheme = Object.fromEntries(
aaiColors.map((color, index) => [index, color])
);
export const categoryMetadataUrl =
"https://docs.google.com/spreadsheets/d/e/2PACX-1vT6FQoV_8ET_pmEB5LGlI_ST9AAhsfiZrWydFwIB80G0Lr_kGwVJUzjM6fRPP9Yrx6iVZYMVAPTnLKq/pub?gid=0&single=true&output=csv";

View File

@@ -78,6 +78,8 @@ body {
border: 1px solid #ddd;
border-radius: 8px;
flex: 1 1 380px;
filter: drop-shadow(0 2px 2px hsl(202.5deg 20% 76.5%));
display: flex;
@@ -95,7 +97,6 @@ body {
font-size: 14px;
color: #666;
line-height: 1.4;
max-width: 40ch;
}
.question-title {

37
src/lib/metadata.ts Normal file
View File

@@ -0,0 +1,37 @@
import * as d3 from "d3";
export interface CategoryMetadata {
category: string;
description: string;
icon: string;
}
const sampleMetadataCsv = `title,text,icon
Allgemeines KI-Wissen,Grundlegendes Wissen über Künstliche Intelligenz und deren Anwendung in Organisationen,school
KI-Innovation,"Fähigkeiten zur Entwicklung, Bewertung und Förderung von KI-Innovationen im Unternehmen",science
KI-Geschäftsstrategie,"Verstehen, wie KI strategisch in Geschäftsmodelle integriert und eingesetzt werden kann",business
Stakeholder-Landschaft,"Fähigkeit, relevante Stakeholder für KI-Initiativen zu identifizieren, einzubinden und zu koordinieren",people_alt
KI-Ethik,Kenntnisse über ethische Fragestellungen und verantwortungsvollen KI-Einsatz,local_police
KI-Regulation,Verständnis rechtlicher Rahmenbedingungen und Regulierungen rund um KI,gavel
Datenkompetenz,"Fähigkeit, Daten kritisch zu beurteilen, aufzubereiten und für KI nutzbar zu machen",equalizer
Python-Programmierung,Grundlegende Programmier-kenntnisse zur Umsetzung und Anpassung von KI-Lösungen,data_object
Software Design,"Gestaltung robuster, skalierbarer und wartbarer Softwarelösungen mit KI-Komponenten",code
Maschinelles Lernen,Kenntnisse in maschinellem Lernen zur Entwicklung datengetriebener Modelle,model_training
MLOps / Infrastruktur,Fähigkeiten zum produktiven Einsatz und Betrieb von KI-Systemen in Unternehmen,all_inclusive
GenAI-Kenntnisse,Verständnis generativer KI-Modelle (z.B. Large Language Models) und ihrer praktischen Nutzung,auto_awesome`;
export function fetchCategoryMetadata(): Promise<CategoryMetadata[]> {
const parseCsv = (csv: string): CategoryMetadata[] => {
const parsed = d3.csvParse(csv);
return parsed.map((row) => ({
category: row.title,
description: row.text,
icon: row.icon,
}));
};
/*return fetch(categoryMetadataUrl)
.then((response) => response.text())
.then(parseCsv);*/
return Promise.resolve(parseCsv(sampleMetadataCsv));
}