Compare commits
2 Commits
461820cb70
...
3498e45ab2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3498e45ab2 | ||
|
|
a690718192 |
10
index.html
10
index.html
@@ -2,8 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<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" />
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link rel="preconnect" href="https://rsms.me/" />
|
<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"
|
href="https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,100..900;1,100..900&display=swap"
|
||||||
rel="stylesheet"
|
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>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
1165
package-lock.json
generated
1165
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,12 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@mui/icons-material": "^7.2.0",
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0"
|
"react-dom": "^19.1.0",
|
||||||
|
"sass": "^1.89.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.30.1",
|
"@eslint/js": "^9.30.1",
|
||||||
|
|||||||
BIN
public/aai-favicon.png
Normal file
BIN
public/aai-favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
public/qr_code_ki-skills-umfrage.png
Normal file
BIN
public/qr_code_ki-skills-umfrage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 162 KiB |
15
src/App.tsx
15
src/App.tsx
@@ -4,14 +4,20 @@ import Legend from "./components/Legend";
|
|||||||
import QRCode from "./components/QRCode";
|
import QRCode from "./components/QRCode";
|
||||||
import { QuestionGroupChart } from "./components/QuestionGroupChart";
|
import { QuestionGroupChart } from "./components/QuestionGroupChart";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
import { getSampleData } from "./lib/data";
|
import { CategoryMetadata, fetchCategoryMetadata } from "./lib/metadata";
|
||||||
import { ResponseData } from "./lib/parser";
|
import { fetchGoogleSheet, ResponseData } from "./lib/parser";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [data, setData] = useState<ResponseData[]>([]);
|
const [data, setData] = useState<ResponseData[]>([]);
|
||||||
|
const [categoryMetadata, setCategoryMetadata] = useState<CategoryMetadata[]>(
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// fetchGoogleSheet().then(setData);
|
fetchGoogleSheet().then(setData);
|
||||||
setData(getSampleData());
|
// setData(getSampleData());
|
||||||
|
|
||||||
|
fetchCategoryMetadata().then(setCategoryMetadata);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!data.length) return null;
|
if (!data.length) return null;
|
||||||
@@ -38,6 +44,7 @@ function App() {
|
|||||||
<QuestionGroupChart
|
<QuestionGroupChart
|
||||||
key={question}
|
key={question}
|
||||||
question={question}
|
question={question}
|
||||||
|
metadata={categoryMetadata.find((m) => m.category === question)}
|
||||||
groupData={groupData}
|
groupData={groupData}
|
||||||
responses={sortedResponses}
|
responses={sortedResponses}
|
||||||
xScale={xScale}
|
xScale={xScale}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { colorScheme } from "../config";
|
import { colorScheme } from "../config";
|
||||||
import "./Legend.css";
|
import "../styles/Legend.scss";
|
||||||
|
|
||||||
const labels = {
|
const labels = {
|
||||||
0: "Keine Erfahrung",
|
0: "Keine Erfahrung",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./QRCode.css";
|
import "../styles/QRCode.scss";
|
||||||
|
|
||||||
export default function QRCode() {
|
export default function QRCode() {
|
||||||
return (
|
return (
|
||||||
@@ -9,7 +9,7 @@ export default function QRCode() {
|
|||||||
egal, auf welchem Level du bist.
|
egal, auf welchem Level du bist.
|
||||||
</p>
|
</p>
|
||||||
<div className="qr-code bracket-frame">
|
<div className="qr-code bracket-frame">
|
||||||
<img src="https://upload.wikimedia.org/wikipedia/commons/4/41/QR_Code_Example.svg" />
|
<img src="/qr_code_ki-skills-umfrage.png" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
|
import MaterialIcon from "@mui/material/Icon";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { colorScheme, config } from "../config";
|
import { colorScheme, config } from "../config";
|
||||||
|
|
||||||
|
import { CategoryMetadata } from "../lib/metadata";
|
||||||
|
|
||||||
import "./Chart.css";
|
import "./Chart.css";
|
||||||
|
|
||||||
interface QuestionGroupChartProps {
|
interface QuestionGroupChartProps {
|
||||||
question: string;
|
question: string;
|
||||||
|
metadata?: CategoryMetadata;
|
||||||
groupData: { response: string }[];
|
groupData: { response: string }[];
|
||||||
responses: string[];
|
responses: string[];
|
||||||
xScale: d3.ScaleBand<string>;
|
xScale: d3.ScaleBand<string>;
|
||||||
@@ -38,6 +42,7 @@ function makeDot(
|
|||||||
|
|
||||||
export function QuestionGroupChart({
|
export function QuestionGroupChart({
|
||||||
question,
|
question,
|
||||||
|
metadata,
|
||||||
groupData,
|
groupData,
|
||||||
responses,
|
responses,
|
||||||
xScale,
|
xScale,
|
||||||
@@ -81,13 +86,11 @@ export function QuestionGroupChart({
|
|||||||
return (
|
return (
|
||||||
<div className="question-group">
|
<div className="question-group">
|
||||||
<svg ref={svgRef}></svg>
|
<svg ref={svgRef}></svg>
|
||||||
<div className="question-title">{question}</div>
|
<div className="question-title">
|
||||||
<p>
|
{metadata?.icon && <MaterialIcon>{metadata.icon}</MaterialIcon>}
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
{question}
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
</div>
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
<p>{metadata?.description || question}</p>
|
||||||
commodo.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,3 +23,6 @@ const aaiColors = [
|
|||||||
export const colorScheme = Object.fromEntries(
|
export const colorScheme = Object.fromEntries(
|
||||||
aaiColors.map((color, index) => [index, color])
|
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";
|
||||||
|
|||||||
37
src/lib/metadata.ts
Normal file
37
src/lib/metadata.ts
Normal 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));
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { StrictMode } from "react";
|
import { StrictMode } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import "./index.css";
|
import "./styles/index.scss";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@use "shared" as *;
|
||||||
|
|
||||||
.legend {
|
.legend {
|
||||||
.box {
|
.box {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -6,7 +8,9 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@include shadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
|
@use "shared" as *;
|
||||||
|
|
||||||
.qr-code-container {
|
.qr-code-container {
|
||||||
background-color: hsl(18.6deg 100% 55.1%);
|
@include rounded;
|
||||||
|
|
||||||
|
background-color: $aai-orange;
|
||||||
flex-basis: 20%;
|
flex-basis: 20%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 16px;
|
|
||||||
|
|
||||||
filter: drop-shadow(0 8px 8px hsl(202.5deg 20% 76.5%));
|
|
||||||
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
||||||
50
src/styles/_shared.scss
Normal file
50
src/styles/_shared.scss
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
$aai-blue-dark: hsl(198.5deg 83.5% 19%);
|
||||||
|
$aai-blue-light: hsl(196.8deg 63% 55.5%);
|
||||||
|
|
||||||
|
$aai-green-dark: hsl(181deg 75% 37%);
|
||||||
|
$aai-green-light: hsl(127.5deg 100% 87.5%);
|
||||||
|
|
||||||
|
$aai-grey: hsl(202.5deg 20% 76.5%);
|
||||||
|
$aai-orange: hsl(18.6deg 100% 55.1%);
|
||||||
|
|
||||||
|
@mixin rounded {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin shadow {
|
||||||
|
filter: drop-shadow(0 16px 16px rgb($aai-grey, 80%));
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin no-shadow {
|
||||||
|
filter: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
$border-radius: 16px;
|
||||||
|
|
||||||
|
@mixin button {
|
||||||
|
@include shadow;
|
||||||
|
|
||||||
|
background-color: $aai-action-button;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
border-radius: $border-radius;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
@include no-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
flex: 1 auto;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@use "shared" as *;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -32,11 +34,11 @@ h6 {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #646cff;
|
color: $aai-blue-dark;
|
||||||
text-decoration: inherit;
|
text-decoration: inherit;
|
||||||
}
|
}
|
||||||
a:hover {
|
a:hover {
|
||||||
color: #747bff;
|
color: $aai-blue-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -56,13 +58,13 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
|
@include rounded;
|
||||||
|
@include shadow;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 16px;
|
|
||||||
|
|
||||||
filter: drop-shadow(0 8px 8px hsl(202.5deg 20% 76.5%));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.charts {
|
.charts {
|
||||||
@@ -73,12 +75,14 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.question-group {
|
.question-group {
|
||||||
|
@include rounded;
|
||||||
|
@include shadow;
|
||||||
|
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
filter: drop-shadow(0 2px 2px hsl(202.5deg 20% 76.5%));
|
flex: 1 1 300px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -95,7 +99,6 @@ body {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
max-width: 40ch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.question-title {
|
.question-title {
|
||||||
@@ -103,6 +106,8 @@ body {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.question-title::before {
|
|
||||||
content: "💻 ";
|
.question-title .material-icons {
|
||||||
|
vertical-align: top;
|
||||||
|
margin-right: 1ex;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user