{"CACHEDAT":"2026-04-14 05:11:11","SLUG":"v1-vgWpMoxmnh","MARKDOWN":"Erster Anlauf Komptenzmodell Builder mit / von Claude\n\n ![](/api/attachments.redirect?id=cbaf9d90-bd2d-4049-8066-77506e4bd0f7 \" =640x654\")\n\nzu verfeinern:\n\n* Versatz (nicht bei 0° anfangen)\n* Entscheidung, ob Domains oder Goals die gleiche Größe haben sollen\n* Domain raus, Küchenstücke bis zum Kern\n* Outcomes nach Wahl als konzentrische Unterteilungen der Kuchenstücke\n * Entscheidung, ob unterschiedliche Größen oder zackiger Rand\n\n oder radiale Elemente in einem äußeren Ring\n* keine Unterteilungen, sondern mit Farbschattierungen arbeiten\n* Ausrichtung Beschriftung.\n\n→ eierlegende Wollmichsau machbar\n\n\n```javascript\nimport React, { useState } from 'react';\r\nimport { ChevronRight, Plus, Minus, Download } from 'lucide-react';\r\n\r\nexport default function CompetenceBuilder() {\r\n const [step, setStep] = useState(1);\r\n const [config, setConfig] = useState({\r\n numDomains: 4,\r\n domainArrangement: 'radial',\r\n domains: [\r\n { name: 'Domain 1', color: '#3498DB', goals: [] },\r\n { name: 'Domain 2', color: '#2ECC71', goals: [] },\r\n { name: 'Domain 3', color: '#F39C12', goals: [] },\r\n { name: 'Domain 4', color: '#E74C3C', goals: [] }\r\n ]\r\n });\r\n\r\n const updateDomainCount = (count) => {\r\n const colors = ['#3498DB', '#2ECC71', '#F39C12', '#E74C3C', '#9B59B6', '#1ABC9C', '#E67E22', '#34495E'];\r\n const newDomains = Array.from({ length: count }, (_, i) => \r\n config.domains[i] || { \r\n name: `Domain ${i + 1}`, \r\n color: colors[i % colors.length],\r\n goals: []\r\n }\r\n );\r\n setConfig({ ...config, numDomains: count, domains: newDomains });\r\n };\r\n\r\n const updateDomainName = (index, name) => {\r\n const newDomains = [...config.domains];\r\n newDomains[index].name = name;\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const updateDomainColor = (index, color) => {\r\n const newDomains = [...config.domains];\r\n newDomains[index].color = color;\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const addGoal = (domainIndex) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals.push({\r\n name: `Goal ${newDomains[domainIndex].goals.length + 1}`,\r\n outcomes: []\r\n });\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const updateGoalName = (domainIndex, goalIndex, name) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals[goalIndex].name = name;\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const removeGoal = (domainIndex, goalIndex) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals.splice(goalIndex, 1);\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const addOutcome = (domainIndex, goalIndex) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals[goalIndex].outcomes.push(\r\n `Outcome ${newDomains[domainIndex].goals[goalIndex].outcomes.length + 1}`\r\n );\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const updateOutcomeName = (domainIndex, goalIndex, outcomeIndex, name) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals[goalIndex].outcomes[outcomeIndex] = name;\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const removeOutcome = (domainIndex, goalIndex, outcomeIndex) => {\r\n const newDomains = [...config.domains];\r\n newDomains[domainIndex].goals[goalIndex].outcomes.splice(outcomeIndex, 1);\r\n setConfig({ ...config, domains: newDomains });\r\n };\r\n\r\n const generateVisualization = () => {\r\n setStep(4);\r\n };\r\n\r\n const downloadSVG = () => {\r\n const svgElement = document.getElementById('competence-visualization');\r\n if (!svgElement) return;\r\n \r\n const svgData = new XMLSerializer().serializeToString(svgElement);\r\n const blob = new Blob([svgData], { type: 'image/svg+xml' });\r\n const url = URL.createObjectURL(blob);\r\n const link = document.createElement('a');\r\n link.href = url;\r\n link.download = 'kompetenzmodell.svg';\r\n link.click();\r\n URL.revokeObjectURL(url);\r\n };\r\n\r\n const RadialVisualization = () => {\r\n const centerX = 500;\r\n const centerY = 500;\r\n const innerRadius = 80;\r\n const domainWidth = 120;\r\n const goalWidth = 100;\r\n const outcomeWidth = 80;\r\n \r\n const angleStep = (2 * Math.PI) / config.domains.length;\r\n\r\n return (\r\n \r\n {/* Center Circle */}\r\n \r\n \r\n Kompetenz-\r\n \r\n \r\n modell\r\n \r\n\r\n {config.domains.map((domain, domainIdx) => {\r\n const startAngle = domainIdx * angleStep - Math.PI / 2;\r\n const endAngle = (domainIdx + 1) * angleStep - Math.PI / 2;\r\n const midAngle = (startAngle + endAngle) / 2;\r\n\r\n // Domain arc\r\n const domainInner = innerRadius;\r\n const domainOuter = innerRadius + domainWidth;\r\n \r\n const domainPath = describeArc(centerX, centerY, domainInner, domainOuter, startAngle, endAngle);\r\n\r\n // Goals\r\n const goalInner = domainOuter;\r\n const goalOuter = domainOuter + goalWidth;\r\n\r\n // Outcomes\r\n const outcomeInner = goalOuter;\r\n const outcomeOuter = goalOuter + outcomeWidth;\r\n\r\n const totalGoals = domain.goals.length || 1;\r\n const goalAngleStep = (endAngle - startAngle) / totalGoals;\r\n\r\n return (\r\n \r\n {/* Domain segment */}\r\n \r\n \r\n {/* Domain label */}\r\n \r\n {domain.name}\r\n \r\n\r\n {/* Goals */}\r\n {domain.goals.length > 0 ? domain.goals.map((goal, goalIdx) => {\r\n const goalStartAngle = startAngle + goalIdx * goalAngleStep;\r\n const goalEndAngle = startAngle + (goalIdx + 1) * goalAngleStep;\r\n const goalMidAngle = (goalStartAngle + goalEndAngle) / 2;\r\n\r\n const goalPath = describeArc(centerX, centerY, goalInner, goalOuter, goalStartAngle, goalEndAngle);\r\n\r\n const totalOutcomes = goal.outcomes.length || 1;\r\n const outcomeAngleStep = (goalEndAngle - goalStartAngle) / totalOutcomes;\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n {goal.name.length > 15 ? goal.name.substring(0, 15) + '...' : goal.name}\r\n \r\n\r\n {/* Outcomes */}\r\n {goal.outcomes.length > 0 && goal.outcomes.map((outcome, outcomeIdx) => {\r\n const outcomeStartAngle = goalStartAngle + outcomeIdx * outcomeAngleStep;\r\n const outcomeEndAngle = goalStartAngle + (outcomeIdx + 1) * outcomeAngleStep;\r\n const outcomeMidAngle = (outcomeStartAngle + outcomeEndAngle) / 2;\r\n\r\n const outcomePath = describeArc(centerX, centerY, outcomeInner, outcomeOuter, outcomeStartAngle, outcomeEndAngle);\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n {outcome.length > 12 ? outcome.substring(0, 12) + '...' : outcome}\r\n \r\n \r\n );\r\n })}\r\n \r\n );\r\n }) : (\r\n \r\n )}\r\n \r\n );\r\n })}\r\n \r\n );\r\n };\r\n\r\n const ConcentricVisualization = () => {\r\n const centerX = 500;\r\n const centerY = 500;\r\n const innerRadius = 80;\r\n const ringWidth = 100;\r\n \r\n return (\r\n \r\n {/* Center Circle */}\r\n \r\n \r\n Kompetenz-\r\n \r\n \r\n modell\r\n \r\n\r\n {config.domains.map((domain, domainIdx) => {\r\n const domainRadius = innerRadius + (domainIdx + 1) * ringWidth;\r\n const angleStep = (2 * Math.PI) / (domain.goals.length || 8);\r\n\r\n return (\r\n \r\n {/* Domain ring */}\r\n \r\n \r\n {/* Domain label */}\r\n \r\n {domain.name}\r\n \r\n\r\n {/* Goals as segments */}\r\n {domain.goals.length > 0 ? domain.goals.map((goal, goalIdx) => {\r\n const angle = goalIdx * angleStep - Math.PI / 2;\r\n const x = centerX + Math.cos(angle) * domainRadius;\r\n const y = centerY + Math.sin(angle) * domainRadius;\r\n\r\n return (\r\n \r\n {/* Goal point */}\r\n \r\n \r\n {/* Goal label */}\r\n \r\n {goal.name.length > 20 ? goal.name.substring(0, 20) + '...' : goal.name}\r\n \r\n\r\n {/* Outcomes as smaller points */}\r\n {goal.outcomes.map((outcome, outcomeIdx) => {\r\n const outcomeAngleOffset = (outcomeIdx - (goal.outcomes.length - 1) / 2) * 0.15;\r\n const outcomeAngle = angle + outcomeAngleOffset;\r\n const outcomeRadius = domainRadius + 25;\r\n const ox = centerX + Math.cos(outcomeAngle) * outcomeRadius;\r\n const oy = centerY + Math.sin(outcomeAngle) * outcomeRadius;\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n );\r\n })}\r\n \r\n );\r\n }) : null}\r\n \r\n );\r\n })}\r\n \r\n );\r\n };\r\n\r\n const describeArc = (x, y, innerRadius, outerRadius, startAngle, endAngle) => {\r\n const innerStart = polarToCartesian(x, y, innerRadius, endAngle);\r\n const innerEnd = polarToCartesian(x, y, innerRadius, startAngle);\r\n const outerStart = polarToCartesian(x, y, outerRadius, endAngle);\r\n const outerEnd = polarToCartesian(x, y, outerRadius, startAngle);\r\n\r\n const largeArcFlag = endAngle - startAngle <= Math.PI ? \"0\" : \"1\";\r\n\r\n return [\r\n \"M\", outerStart.x, outerStart.y,\r\n \"A\", outerRadius, outerRadius, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,\r\n \"L\", innerEnd.x, innerEnd.y,\r\n \"A\", innerRadius, innerRadius, 0, largeArcFlag, 1, innerStart.x, innerStart.y,\r\n \"Z\"\r\n ].join(\" \");\r\n };\r\n\r\n const polarToCartesian = (centerX, centerY, radius, angleInRadians) => {\r\n return {\r\n x: centerX + (radius * Math.cos(angleInRadians)),\r\n y: centerY + (radius * Math.sin(angleInRadians))\r\n };\r\n };\r\n\r\n return (\r\n
\r\n
\r\n

Kompetenzmodell Builder

\r\n \r\n {/* Progress Steps */}\r\n
\r\n {['Struktur', 'Domains', 'Goals & Outcomes', 'Visualisierung'].map((label, idx) => (\r\n
\r\n
idx + 1 ? 'bg-green-500 text-white' : \r\n step === idx + 1 ? 'bg-blue-500 text-white' : \r\n 'bg-gray-300 text-gray-600'\r\n }`}>\r\n {idx + 1}\r\n
\r\n {label}\r\n {idx < 3 && }\r\n
\r\n ))}\r\n
\r\n\r\n {/* Step 1: Grundstruktur */}\r\n {step === 1 && (\r\n
\r\n

Schritt 1: Grundstruktur

\r\n \r\n
\r\n
\r\n \r\n updateDomainCount(parseInt(e.target.value))}\r\n className=\"w-32 px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent\"\r\n />\r\n
\r\n\r\n
\r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n \r\n
\r\n )}\r\n\r\n {/* Step 2: Domains konfigurieren */}\r\n {step === 2 && (\r\n
\r\n

Schritt 2: Domains benennen

\r\n \r\n
\r\n {config.domains.map((domain, idx) => (\r\n
\r\n
\r\n updateDomainName(idx, e.target.value)}\r\n className=\"w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500\"\r\n placeholder=\"Domain Name\"\r\n />\r\n
\r\n updateDomainColor(idx, e.target.value)}\r\n className=\"w-16 h-10 rounded cursor-pointer\"\r\n />\r\n
\r\n ))}\r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n
\r\n )}\r\n\r\n {/* Step 3: Goals und Outcomes */}\r\n {step === 3 && (\r\n
\r\n

Schritt 3: Goals & Outcomes definieren

\r\n \r\n
\r\n {config.domains.map((domain, domainIdx) => (\r\n
\r\n
\r\n

\r\n {domain.name}\r\n

\r\n \r\n
\r\n\r\n
\r\n {domain.goals.map((goal, goalIdx) => (\r\n
\r\n
\r\n updateGoalName(domainIdx, goalIdx, e.target.value)}\r\n className=\"flex-1 px-3 py-2 border border-gray-300 rounded-md\"\r\n placeholder=\"Goal Name\"\r\n />\r\n \r\n
\r\n\r\n
\r\n \r\n {goal.outcomes.map((outcome, outcomeIdx) => (\r\n
\r\n updateOutcomeName(domainIdx, goalIdx, outcomeIdx, e.target.value)}\r\n className=\"flex-1 px-3 py-1 text-sm border border-gray-300 rounded\"\r\n placeholder=\"Outcome Name\"\r\n />\r\n \r\n
\r\n ))}\r\n
\r\n
\r\n ))}\r\n
\r\n\r\n {domain.goals.length === 0 && (\r\n

Noch keine Goals definiert

\r\n )}\r\n
\r\n ))}\r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n
\r\n )}\r\n\r\n {/* Step 4: Visualisierung */}\r\n {step === 4 && (\r\n
\r\n

Schritt 4: Visualisierung

\r\n \r\n
\r\n {config.domainArrangement === 'radial' ? : }\r\n
\r\n\r\n
\r\n \r\n \r\n
\r\n
\r\n )}\r\n
\r\n
\r\n );\r\n}\n```\n\n\nVersionen\n\n\n ![](/api/attachments.redirect?id=1999f7af-dcdf-44fb-9fb9-5799490cdc48 \" =753x763\")","HTML":"

Erster Anlauf Komptenzmodell Builder mit / von Claude

\n

\"\"

\n

zu verfeinern:

\n\n

oder radiale Elemente in einem äußeren Ring

\n\n

→ eierlegende Wollmichsau machbar

\n
import React, { useState } from 'react';
import { ChevronRight, Plus, Minus, Download } from 'lucide-react';

export default function CompetenceBuilder() {
  const [step, setStep] = useState(1);
  const [config, setConfig] = useState({
    numDomains: 4,
    domainArrangement: 'radial',
    domains: [
      { name: 'Domain 1', color: '#3498DB', goals: [] },
      { name: 'Domain 2', color: '#2ECC71', goals: [] },
      { name: 'Domain 3', color: '#F39C12', goals: [] },
      { name: 'Domain 4', color: '#E74C3C', goals: [] }
    ]
  });

  const updateDomainCount = (count) => {
    const colors = ['#3498DB', '#2ECC71', '#F39C12', '#E74C3C', '#9B59B6', '#1ABC9C', '#E67E22', '#34495E'];
    const newDomains = Array.from({ length: count }, (_, i) => 
      config.domains[i] || { 
        name: `Domain ${i + 1}`, 
        color: colors[i % colors.length],
        goals: []
      }
    );
    setConfig({ ...config, numDomains: count, domains: newDomains });
  };

  const updateDomainName = (index, name) => {
    const newDomains = [...config.domains];
    newDomains[index].name = name;
    setConfig({ ...config, domains: newDomains });
  };

  const updateDomainColor = (index, color) => {
    const newDomains = [...config.domains];
    newDomains[index].color = color;
    setConfig({ ...config, domains: newDomains });
  };

  const addGoal = (domainIndex) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals.push({
      name: `Goal ${newDomains[domainIndex].goals.length + 1}`,
      outcomes: []
    });
    setConfig({ ...config, domains: newDomains });
  };

  const updateGoalName = (domainIndex, goalIndex, name) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals[goalIndex].name = name;
    setConfig({ ...config, domains: newDomains });
  };

  const removeGoal = (domainIndex, goalIndex) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals.splice(goalIndex, 1);
    setConfig({ ...config, domains: newDomains });
  };

  const addOutcome = (domainIndex, goalIndex) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals[goalIndex].outcomes.push(
      `Outcome ${newDomains[domainIndex].goals[goalIndex].outcomes.length + 1}`
    );
    setConfig({ ...config, domains: newDomains });
  };

  const updateOutcomeName = (domainIndex, goalIndex, outcomeIndex, name) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals[goalIndex].outcomes[outcomeIndex] = name;
    setConfig({ ...config, domains: newDomains });
  };

  const removeOutcome = (domainIndex, goalIndex, outcomeIndex) => {
    const newDomains = [...config.domains];
    newDomains[domainIndex].goals[goalIndex].outcomes.splice(outcomeIndex, 1);
    setConfig({ ...config, domains: newDomains });
  };

  const generateVisualization = () => {
    setStep(4);
  };

  const downloadSVG = () => {
    const svgElement = document.getElementById('competence-visualization');
    if (!svgElement) return;
    
    const svgData = new XMLSerializer().serializeToString(svgElement);
    const blob = new Blob([svgData], { type: 'image/svg+xml' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = 'kompetenzmodell.svg';
    link.click();
    URL.revokeObjectURL(url);
  };

  const RadialVisualization = () => {
    const centerX = 500;
    const centerY = 500;
    const innerRadius = 80;
    const domainWidth = 120;
    const goalWidth = 100;
    const outcomeWidth = 80;
    
    const angleStep = (2 * Math.PI) / config.domains.length;

    return (
      <svg id="competence-visualization" width="1000" height="1000" viewBox="0 0 1000 1000">
        {/* Center Circle */}
        <circle cx={centerX} cy={centerY} r={innerRadius} fill="#f8f9fa" stroke="#dee2e6" strokeWidth="2" />
        <text x={centerX} y={centerY} textAnchor="middle" dominantBaseline="middle" fontSize="16" fontWeight="bold" fill="#495057">
          Kompetenz-
        </text>
        <text x={centerX} y={centerY + 20} textAnchor="middle" dominantBaseline="middle" fontSize="16" fontWeight="bold" fill="#495057">
          modell
        </text>

        {config.domains.map((domain, domainIdx) => {
          const startAngle = domainIdx * angleStep - Math.PI / 2;
          const endAngle = (domainIdx + 1) * angleStep - Math.PI / 2;
          const midAngle = (startAngle + endAngle) / 2;

          // Domain arc
          const domainInner = innerRadius;
          const domainOuter = innerRadius + domainWidth;
          
          const domainPath = describeArc(centerX, centerY, domainInner, domainOuter, startAngle, endAngle);

          // Goals
          const goalInner = domainOuter;
          const goalOuter = domainOuter + goalWidth;

          // Outcomes
          const outcomeInner = goalOuter;
          const outcomeOuter = goalOuter + outcomeWidth;

          const totalGoals = domain.goals.length || 1;
          const goalAngleStep = (endAngle - startAngle) / totalGoals;

          return (
            <g key={domainIdx}>
              {/* Domain segment */}
              <path d={domainPath} fill={domain.color} stroke="white" strokeWidth="3" opacity="0.8" />
              
              {/* Domain label */}
              <text
                x={centerX + Math.cos(midAngle) * (domainInner + domainWidth / 2)}
                y={centerY + Math.sin(midAngle) * (domainInner + domainWidth / 2)}
                textAnchor="middle"
                dominantBaseline="middle"
                fontSize="14"
                fontWeight="bold"
                fill="white"
                transform={`rotate(${(midAngle * 180 / Math.PI + 90)}, ${centerX + Math.cos(midAngle) * (domainInner + domainWidth / 2)}, ${centerY + Math.sin(midAngle) * (domainInner + domainWidth / 2)})`}
              >
                {domain.name}
              </text>

              {/* Goals */}
              {domain.goals.length > 0 ? domain.goals.map((goal, goalIdx) => {
                const goalStartAngle = startAngle + goalIdx * goalAngleStep;
                const goalEndAngle = startAngle + (goalIdx + 1) * goalAngleStep;
                const goalMidAngle = (goalStartAngle + goalEndAngle) / 2;

                const goalPath = describeArc(centerX, centerY, goalInner, goalOuter, goalStartAngle, goalEndAngle);

                const totalOutcomes = goal.outcomes.length || 1;
                const outcomeAngleStep = (goalEndAngle - goalStartAngle) / totalOutcomes;

                return (
                  <g key={goalIdx}>
                    <path d={goalPath} fill={domain.color} stroke="white" strokeWidth="2" opacity="0.6" />
                    
                    <text
                      x={centerX + Math.cos(goalMidAngle) * (goalInner + goalWidth / 2)}
                      y={centerY + Math.sin(goalMidAngle) * (goalInner + goalWidth / 2)}
                      textAnchor="middle"
                      dominantBaseline="middle"
                      fontSize="11"
                      fill="white"
                      transform={`rotate(${(goalMidAngle * 180 / Math.PI + 90)}, ${centerX + Math.cos(goalMidAngle) * (goalInner + goalWidth / 2)}, ${centerY + Math.sin(goalMidAngle) * (goalInner + goalWidth / 2)})`}
                    >
                      {goal.name.length > 15 ? goal.name.substring(0, 15) + '...' : goal.name}
                    </text>

                    {/* Outcomes */}
                    {goal.outcomes.length > 0 && goal.outcomes.map((outcome, outcomeIdx) => {
                      const outcomeStartAngle = goalStartAngle + outcomeIdx * outcomeAngleStep;
                      const outcomeEndAngle = goalStartAngle + (outcomeIdx + 1) * outcomeAngleStep;
                      const outcomeMidAngle = (outcomeStartAngle + outcomeEndAngle) / 2;

                      const outcomePath = describeArc(centerX, centerY, outcomeInner, outcomeOuter, outcomeStartAngle, outcomeEndAngle);

                      return (
                        <g key={outcomeIdx}>
                          <path d={outcomePath} fill={domain.color} stroke="white" strokeWidth="2" opacity="0.4" />
                          
                          <text
                            x={centerX + Math.cos(outcomeMidAngle) * (outcomeInner + outcomeWidth / 2)}
                            y={centerY + Math.sin(outcomeMidAngle) * (outcomeInner + outcomeWidth / 2)}
                            textAnchor="middle"
                            dominantBaseline="middle"
                            fontSize="9"
                            fill="white"
                            transform={`rotate(${(outcomeMidAngle * 180 / Math.PI + 90)}, ${centerX + Math.cos(outcomeMidAngle) * (outcomeInner + outcomeWidth / 2)}, ${centerY + Math.sin(outcomeMidAngle) * (outcomeInner + outcomeWidth / 2)})`}
                          >
                            {outcome.length > 12 ? outcome.substring(0, 12) + '...' : outcome}
                          </text>
                        </g>
                      );
                    })}
                  </g>
                );
              }) : (
                <path d={describeArc(centerX, centerY, goalInner, outcomeOuter, startAngle, endAngle)} fill={domain.color} stroke="white" strokeWidth="2" opacity="0.3" />
              )}
            </g>
          );
        })}
      </svg>
    );
  };

  const ConcentricVisualization = () => {
    const centerX = 500;
    const centerY = 500;
    const innerRadius = 80;
    const ringWidth = 100;
    
    return (
      <svg id="competence-visualization" width="1000" height="1000" viewBox="0 0 1000 1000">
        {/* Center Circle */}
        <circle cx={centerX} cy={centerY} r={innerRadius} fill="#f8f9fa" stroke="#dee2e6" strokeWidth="2" />
        <text x={centerX} y={centerY} textAnchor="middle" dominantBaseline="middle" fontSize="16" fontWeight="bold" fill="#495057">
          Kompetenz-
        </text>
        <text x={centerX} y={centerY + 20} textAnchor="middle" dominantBaseline="middle" fontSize="16" fontWeight="bold" fill="#495057">
          modell
        </text>

        {config.domains.map((domain, domainIdx) => {
          const domainRadius = innerRadius + (domainIdx + 1) * ringWidth;
          const angleStep = (2 * Math.PI) / (domain.goals.length || 8);

          return (
            <g key={domainIdx}>
              {/* Domain ring */}
              <circle
                cx={centerX}
                cy={centerY}
                r={domainRadius}
                fill="none"
                stroke={domain.color}
                strokeWidth={ringWidth - 10}
                opacity="0.3"
              />
              
              {/* Domain label */}
              <text
                x={centerX}
                y={centerY - domainRadius - 10}
                textAnchor="middle"
                fontSize="14"
                fontWeight="bold"
                fill={domain.color}
              >
                {domain.name}
              </text>

              {/* Goals as segments */}
              {domain.goals.length > 0 ? domain.goals.map((goal, goalIdx) => {
                const angle = goalIdx * angleStep - Math.PI / 2;
                const x = centerX + Math.cos(angle) * domainRadius;
                const y = centerY + Math.sin(angle) * domainRadius;

                return (
                  <g key={goalIdx}>
                    {/* Goal point */}
                    <circle cx={x} cy={y} r="8" fill={domain.color} stroke="white" strokeWidth="2" />
                    
                    {/* Goal label */}
                    <text
                      x={x + Math.cos(angle) * 30}
                      y={y + Math.sin(angle) * 30}
                      textAnchor="middle"
                      dominantBaseline="middle"
                      fontSize="10"
                      fill={domain.color}
                      fontWeight="600"
                    >
                      {goal.name.length > 20 ? goal.name.substring(0, 20) + '...' : goal.name}
                    </text>

                    {/* Outcomes as smaller points */}
                    {goal.outcomes.map((outcome, outcomeIdx) => {
                      const outcomeAngleOffset = (outcomeIdx - (goal.outcomes.length - 1) / 2) * 0.15;
                      const outcomeAngle = angle + outcomeAngleOffset;
                      const outcomeRadius = domainRadius + 25;
                      const ox = centerX + Math.cos(outcomeAngle) * outcomeRadius;
                      const oy = centerY + Math.sin(outcomeAngle) * outcomeRadius;

                      return (
                        <g key={outcomeIdx}>
                          <line x1={x} y1={y} x2={ox} y2={oy} stroke={domain.color} strokeWidth="1" opacity="0.5" />
                          <circle cx={ox} cy={oy} r="4" fill={domain.color} opacity="0.7" stroke="white" strokeWidth="1" />
                        </g>
                      );
                    })}
                  </g>
                );
              }) : null}
            </g>
          );
        })}
      </svg>
    );
  };

  const describeArc = (x, y, innerRadius, outerRadius, startAngle, endAngle) => {
    const innerStart = polarToCartesian(x, y, innerRadius, endAngle);
    const innerEnd = polarToCartesian(x, y, innerRadius, startAngle);
    const outerStart = polarToCartesian(x, y, outerRadius, endAngle);
    const outerEnd = polarToCartesian(x, y, outerRadius, startAngle);

    const largeArcFlag = endAngle - startAngle <= Math.PI ? "0" : "1";

    return [
      "M", outerStart.x, outerStart.y,
      "A", outerRadius, outerRadius, 0, largeArcFlag, 0, outerEnd.x, outerEnd.y,
      "L", innerEnd.x, innerEnd.y,
      "A", innerRadius, innerRadius, 0, largeArcFlag, 1, innerStart.x, innerStart.y,
      "Z"
    ].join(" ");
  };

  const polarToCartesian = (centerX, centerY, radius, angleInRadians) => {
    return {
      x: centerX + (radius * Math.cos(angleInRadians)),
      y: centerY + (radius * Math.sin(angleInRadians))
    };
  };

  return (
    <div className="min-h-screen bg-gray-50 p-8">
      <div className="max-w-6xl mx-auto">
        <h1 className="text-3xl font-bold text-gray-800 mb-8">Kompetenzmodell Builder</h1>
        
        {/* Progress Steps */}
        <div className="flex items-center justify-center mb-8 space-x-4">
          {['Struktur', 'Domains', 'Goals & Outcomes', 'Visualisierung'].map((label, idx) => (
            <div key={idx} className="flex items-center">
              <div className={`w-10 h-10 rounded-full flex items-center justify-center font-semibold ${
                step > idx + 1 ? 'bg-green-500 text-white' : 
                step === idx + 1 ? 'bg-blue-500 text-white' : 
                'bg-gray-300 text-gray-600'
              }`}>
                {idx + 1}
              </div>
              <span className="ml-2 text-sm font-medium text-gray-700">{label}</span>
              {idx < 3 && <ChevronRight className="ml-4 text-gray-400" size={20} />}
            </div>
          ))}
        </div>

        {/* Step 1: Grundstruktur */}
        {step === 1 && (
          <div className="bg-white rounded-lg shadow-lg p-8">
            <h2 className="text-2xl font-bold text-gray-800 mb-6">Schritt 1: Grundstruktur</h2>
            
            <div className="space-y-6">
              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2">
                  Anzahl der Domains (Ebene 1)
                </label>
                <input 
                  type="number" 
                  min="2" 
                  max="8" 
                  value={config.numDomains}
                  onChange={(e) => updateDomainCount(parseInt(e.target.value))}
                  className="w-32 px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-gray-700 mb-2">
                  Anordnung der Domains
                </label>
                <div className="space-y-2">
                  <label className="flex items-center">
                    <input 
                      type="radio" 
                      name="arrangement" 
                      value="radial"
                      checked={config.domainArrangement === 'radial'}
                      onChange={(e) => setConfig({ ...config, domainArrangement: e.target.value })}
                      className="mr-2"
                    />
                    <span>Radial (Kuchenstücke)</span>
                  </label>
                  <label className="flex items-center">
                    <input 
                      type="radio" 
                      name="arrangement" 
                      value="concentric"
                      checked={config.domainArrangement === 'concentric'}
                      onChange={(e) => setConfig({ ...config, domainArrangement: e.target.value })}
                      className="mr-2"
                    />
                    <span>Konzentrisch (Ringe)</span>
                  </label>
                </div>
              </div>
            </div>

            <button 
              onClick={() => setStep(2)}
              className="mt-8 px-6 py-3 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600 transition"
            >
              Weiter zu Domains
            </button>
          </div>
        )}

        {/* Step 2: Domains konfigurieren */}
        {step === 2 && (
          <div className="bg-white rounded-lg shadow-lg p-8">
            <h2 className="text-2xl font-bold text-gray-800 mb-6">Schritt 2: Domains benennen</h2>
            
            <div className="space-y-4">
              {config.domains.map((domain, idx) => (
                <div key={idx} className="flex items-center space-x-4 p-4 bg-gray-50 rounded-lg">
                  <div className="flex-1">
                    <input 
                      type="text"
                      value={domain.name}
                      onChange={(e) => updateDomainName(idx, e.target.value)}
                      className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500"
                      placeholder="Domain Name"
                    />
                  </div>
                  <input 
                    type="color"
                    value={domain.color}
                    onChange={(e) => updateDomainColor(idx, e.target.value)}
                    className="w-16 h-10 rounded cursor-pointer"
                  />
                </div>
              ))}
            </div>

            <div className="flex space-x-4 mt-8">
              <button 
                onClick={() => setStep(1)}
                className="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-lg hover:bg-gray-400 transition"
              >
                Zurück
              </button>
              <button 
                onClick={() => setStep(3)}
                className="px-6 py-3 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600 transition"
              >
                Weiter zu Goals & Outcomes
              </button>
            </div>
          </div>
        )}

        {/* Step 3: Goals und Outcomes */}
        {step === 3 && (
          <div className="bg-white rounded-lg shadow-lg p-8">
            <h2 className="text-2xl font-bold text-gray-800 mb-6">Schritt 3: Goals & Outcomes definieren</h2>
            
            <div className="space-y-6">
              {config.domains.map((domain, domainIdx) => (
                <div key={domainIdx} className="border-2 rounded-lg p-6" style={{ borderColor: domain.color }}>
                  <div className="flex items-center justify-between mb-4">
                    <h3 className="text-xl font-bold" style={{ color: domain.color }}>
                      {domain.name}
                    </h3>
                    <button 
                      onClick={() => addGoal(domainIdx)}
                      className="flex items-center px-4 py-2 bg-green-500 text-white rounded-lg hover:bg-green-600 transition"
                    >
                      <Plus size={16} className="mr-2" />
                      Goal hinzufügen
                    </button>
                  </div>

                  <div className="space-y-4">
                    {domain.goals.map((goal, goalIdx) => (
                      <div key={goalIdx} className="bg-gray-50 rounded-lg p-4">
                        <div className="flex items-center space-x-2 mb-3">
                          <input 
                            type="text"
                            value={goal.name}
                            onChange={(e) => updateGoalName(domainIdx, goalIdx, e.target.value)}
                            className="flex-1 px-3 py-2 border border-gray-300 rounded-md"
                            placeholder="Goal Name"
                          />
                          <button 
                            onClick={() => removeGoal(domainIdx, goalIdx)}
                            className="p-2 bg-red-500 text-white rounded hover:bg-red-600"
                          >
                            <Minus size={16} />
                          </button>
                        </div>

                        <div className="ml-4 space-y-2">
                          <button 
                            onClick={() => addOutcome(domainIdx, goalIdx)}
                            className="text-sm px-3 py-1 bg-blue-100 text-blue-700 rounded hover:bg-blue-200"
                          >
                            + Outcome hinzufügen
                          </button>
                          {goal.outcomes.map((outcome, outcomeIdx) => (
                            <div key={outcomeIdx} className="flex items-center space-x-2">
                              <input 
                                type="text"
                                value={outcome}
                                onChange={(e) => updateOutcomeName(domainIdx, goalIdx, outcomeIdx, e.target.value)}
                                className="flex-1 px-3 py-1 text-sm border border-gray-300 rounded"
                                placeholder="Outcome Name"
                              />
                              <button 
                                onClick={() => removeOutcome(domainIdx, goalIdx, outcomeIdx)}
                                className="p-1 text-red-500 hover:bg-red-50 rounded"
                              >
                                <Minus size={14} />
                              </button>
                            </div>
                          ))}
                        </div>
                      </div>
                    ))}
                  </div>

                  {domain.goals.length === 0 && (
                    <p className="text-gray-500 text-sm italic">Noch keine Goals definiert</p>
                  )}
                </div>
              ))}
            </div>

            <div className="flex space-x-4 mt-8">
              <button 
                onClick={() => setStep(2)}
                className="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-lg hover:bg-gray-400 transition"
              >
                Zurück
              </button>
              <button 
                onClick={generateVisualization}
                className="px-6 py-3 bg-green-500 text-white font-semibold rounded-lg hover:bg-green-600 transition"
              >
                Visualisierung generieren
              </button>
            </div>
          </div>
        )}

        {/* Step 4: Visualisierung */}
        {step === 4 && (
          <div className="bg-white rounded-lg shadow-lg p-8">
            <h2 className="text-2xl font-bold text-gray-800 mb-6">Schritt 4: Visualisierung</h2>
            
            <div className="flex justify-center mb-6">
              {config.domainArrangement === 'radial' ? <RadialVisualization /> : <ConcentricVisualization />}
            </div>

            <div className="flex justify-center space-x-4 mt-8">
              <button 
                onClick={() => setStep(3)}
                className="px-6 py-3 bg-gray-300 text-gray-700 font-semibold rounded-lg hover:bg-gray-400 transition"
              >
                Zurück zur Konfiguration
              </button>
              <button 
                onClick={downloadSVG}
                className="flex items-center px-6 py-3 bg-blue-500 text-white font-semibold rounded-lg hover:bg-blue-600 transition"
              >
                <Download size={16} className="mr-2" />
                SVG herunterladen
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}
\n

Versionen

\n

\"\"

","UPDATEDAT":"2025-10-16T13:50:35.667Z","ID":"6cb8b1d1-827d-45fe-901c-88407fe66b1a","TITLE":"v1"}