Visual apperance
This commit is contained in:
30
src/components/Chart.css
Normal file
30
src/components/Chart.css
Normal file
@@ -0,0 +1,30 @@
|
||||
.dot {
|
||||
stroke: #fff;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
.selected {
|
||||
fill: rgb(223 110 38);
|
||||
}
|
||||
|
||||
.axis-label {
|
||||
font-size: 12px;
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
.axis text {
|
||||
font-size: 11px;
|
||||
fill: #666;
|
||||
}
|
||||
|
||||
.axis path,
|
||||
.axis line {
|
||||
fill: none;
|
||||
stroke: #ddd;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.grid line {
|
||||
stroke: #e0e0e0;
|
||||
stroke-dasharray: 2, 2;
|
||||
}
|
||||
19
src/components/Legend.css
Normal file
19
src/components/Legend.css
Normal file
@@ -0,0 +1,19 @@
|
||||
.legend {
|
||||
.box {
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 4px;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 16px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
28
src/components/Legend.tsx
Normal file
28
src/components/Legend.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { colorScheme } from "../config";
|
||||
import "./Legend.css";
|
||||
|
||||
const labels = {
|
||||
0: "Keine Erfahrung",
|
||||
1: "Grundkenntnisse",
|
||||
2: "Geübte Anwendung",
|
||||
3: "Sichere Praxisanwendung",
|
||||
4: "Fachwissen und Erfahrung",
|
||||
};
|
||||
|
||||
export default function Legend() {
|
||||
return (
|
||||
<div className="legend">
|
||||
<ul>
|
||||
{Object.entries(labels).map(([level, label]) => (
|
||||
<li key={level}>
|
||||
<span
|
||||
className="box"
|
||||
style={{ backgroundColor: `${colorScheme[level]}` }}
|
||||
></span>
|
||||
<span className="label">{label}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
22
src/components/QRCode.css
Normal file
22
src/components/QRCode.css
Normal file
@@ -0,0 +1,22 @@
|
||||
.qr-code-container {
|
||||
background-color: rgb(223 110 37);
|
||||
flex-basis: 20%;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
|
||||
filter: drop-shadow(0 8px 8px hsl(202.5deg 20% 76.5%));
|
||||
|
||||
color: #fff;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
background-color: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
16
src/components/QRCode.tsx
Normal file
16
src/components/QRCode.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import "./QRCode.css";
|
||||
|
||||
export default function QRCode() {
|
||||
return (
|
||||
<div className="qr-code-container">
|
||||
<h1>Scan me!</h1>
|
||||
<p>
|
||||
Scanne den Code und zeige, welche KI-Skills du mitbringst — ganz
|
||||
egal, auf welchem Level du bist.
|
||||
</p>
|
||||
<div className="qr-code bracket-frame">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/4/41/QR_Code_Example.svg" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import * as d3 from "d3";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { colorScheme, config } from "../config";
|
||||
|
||||
import "./Chart.css";
|
||||
|
||||
interface QuestionGroupChartProps {
|
||||
question: string;
|
||||
groupData: { response: string }[];
|
||||
@@ -9,6 +11,31 @@ interface QuestionGroupChartProps {
|
||||
xScale: d3.ScaleBand<string>;
|
||||
}
|
||||
|
||||
function makeDot(
|
||||
shape: "rect" | "circle",
|
||||
g: d3.Selection<SVGGElement, unknown, null, undefined>,
|
||||
dotX: number,
|
||||
dotY: number
|
||||
) {
|
||||
if (shape === "circle") {
|
||||
return g
|
||||
.append("circle")
|
||||
.attr("class", "dot")
|
||||
.attr("cx", dotX)
|
||||
.attr("cy", dotY)
|
||||
.attr("r", config.dotRadius);
|
||||
} else if (shape === "rect") {
|
||||
return g
|
||||
.append("rect")
|
||||
.attr("class", "dot")
|
||||
.attr("x", dotX - config.dotRadius)
|
||||
.attr("y", dotY - config.dotRadius)
|
||||
.attr("width", config.dotRadius * 2)
|
||||
.attr("height", config.dotRadius * 2);
|
||||
}
|
||||
throw new Error(`Unsupported shape: ${shape}`);
|
||||
}
|
||||
|
||||
export function QuestionGroupChart({
|
||||
question,
|
||||
groupData,
|
||||
@@ -23,27 +50,14 @@ export function QuestionGroupChart({
|
||||
|
||||
// Group responses by category (within this group only)
|
||||
const responseGroups = d3.group(groupData, (d) => d.response);
|
||||
const maxCount =
|
||||
d3.max(Array.from(responseGroups.values(), (values) => values.length)) ||
|
||||
0;
|
||||
const maxRows = Math.ceil(maxCount / config.columnsPerGroup);
|
||||
|
||||
const chartHeight =
|
||||
maxRows * (config.dotRadius * 2 + config.dotSpacing) +
|
||||
config.margin.bottom;
|
||||
const chartWidth =
|
||||
xScale.range()[1] + config.margin.left + config.margin.right;
|
||||
const chartHeight = 200;
|
||||
const chartWidth = xScale.range()[1];
|
||||
|
||||
const svg = d3
|
||||
.select(svgRef.current)
|
||||
.attr("width", chartWidth)
|
||||
.attr("height", chartHeight);
|
||||
const g = svg
|
||||
.append("g")
|
||||
.attr(
|
||||
"transform",
|
||||
`translate(${config.margin.left},${config.margin.top})`
|
||||
);
|
||||
const g = svg.append("g");
|
||||
|
||||
// Dots
|
||||
responses.forEach((response) => {
|
||||
@@ -58,36 +72,25 @@ export function QuestionGroupChart({
|
||||
(col - (config.columnsPerGroup - 1) / 2) *
|
||||
(config.dotRadius * 2 + config.dotSpacing);
|
||||
const dotY =
|
||||
chartHeight -
|
||||
config.margin.top -
|
||||
config.margin.bottom -
|
||||
(row + 1) * (config.dotRadius * 2 + config.dotSpacing);
|
||||
chartHeight - (row + 1) * (config.dotRadius * 2 + config.dotSpacing);
|
||||
|
||||
g.append("circle")
|
||||
.attr("class", "dot")
|
||||
.attr("cx", dotX)
|
||||
.attr("cy", dotY)
|
||||
.attr("r", config.dotRadius)
|
||||
.attr("fill", colorScheme[response] || "#666");
|
||||
makeDot("rect", g, dotX, dotY).attr(
|
||||
"fill",
|
||||
colorScheme[response] || "#666"
|
||||
);
|
||||
});
|
||||
});
|
||||
}, [groupData, responses, xScale, question]);
|
||||
|
||||
/*
|
||||
// Calculate height for container
|
||||
const responseGroups = d3.group(groupData, (d) => d.response);
|
||||
const maxCount =
|
||||
d3.max(Array.from(responseGroups.values(), (values) => values.length)) || 0;
|
||||
const maxRows = Math.ceil(maxCount / config.columnsPerGroup);
|
||||
const chartHeight =
|
||||
maxRows * (config.dotRadius * 2 + config.dotSpacing) +
|
||||
config.margin.top +
|
||||
config.margin.bottom;
|
||||
*/
|
||||
return (
|
||||
<div className="question-group">
|
||||
<div className="question-title">{question}</div>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user