ZA ist drin
Dieser Commit ist enthalten in:
@ -374,8 +374,12 @@ export default function OrganizationEditor() {
|
|||||||
const handleAutoLayout = () => {
|
const handleAutoLayout = () => {
|
||||||
const laneWidth = 420
|
const laneWidth = 420
|
||||||
const xMargin = 40
|
const xMargin = 40
|
||||||
const yStart = 80
|
|
||||||
const yGap = 110
|
const yGap = 110
|
||||||
|
// Zweistufiges Top-Band: Direktion ganz oben, darunter weitere ROOT-Knoten
|
||||||
|
const topYDirector = 10
|
||||||
|
const topYRootRow = topYDirector + yGap
|
||||||
|
// Spaltenstart mit Abstand unterhalb der Top-Row, um Überlappungen zu vermeiden
|
||||||
|
const yStart = topYRootRow + yGap
|
||||||
|
|
||||||
const newNodes: Node[] = nodes.map(n => ({ ...n, position: { ...n.position } }))
|
const newNodes: Node[] = nodes.map(n => ({ ...n, position: { ...n.position } }))
|
||||||
const byId: Record<string, Node> = {}
|
const byId: Record<string, Node> = {}
|
||||||
@ -442,16 +446,36 @@ export default function OrganizationEditor() {
|
|||||||
return va - vb
|
return va - vb
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position Direktion and root-like nodes at the top center
|
// Position Direktion und ROOT-ähnliche Knoten als Top-Zeilen
|
||||||
const rootNodes = lanesMap.get('ROOT') || []
|
const rootNodes = lanesMap.get('ROOT') || []
|
||||||
let globalMinX = xMargin
|
let globalMinX = xMargin
|
||||||
const totalLanes = laneOrder.filter(k => k !== 'ROOT' && k !== 'UNK').length
|
const lanesForWidth = laneOrder.filter(k => k !== 'ROOT' && k !== 'UNK')
|
||||||
const rootX = Math.max(xMargin, (totalLanes * laneWidth) / 2 - 150)
|
const laneCount = Math.max(1, lanesForWidth.length)
|
||||||
let rootY = 10
|
const totalWidth = (laneCount - 1) * laneWidth
|
||||||
rootNodes.forEach(n => {
|
const cardHalfWidth = 150 // angenommene halbe Kartenbreite
|
||||||
n.position = { x: rootX, y: rootY }
|
|
||||||
rootY += yGap
|
// 1) Direktion zentriert ganz oben
|
||||||
|
const directorIndex = rootNodes.findIndex(n => (String(n.data?.type).toLowerCase() === 'direktion') || String(n.data?.code).toUpperCase() === 'DIR')
|
||||||
|
let placedIndices = new Set<number>()
|
||||||
|
if (directorIndex >= 0) {
|
||||||
|
const centerX = xMargin + totalWidth / 2 - cardHalfWidth
|
||||||
|
const x = Math.max(xMargin, centerX)
|
||||||
|
rootNodes[directorIndex].position = { x, y: topYDirector }
|
||||||
|
placedIndices.add(directorIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Übrige ROOT-Knoten in einer zweiten Top-Reihe gleichmäßig verteilen
|
||||||
|
const others: Node[] = rootNodes.filter((_, idx) => !placedIndices.has(idx))
|
||||||
|
if (others.length === 1) {
|
||||||
|
const x = Math.max(xMargin, xMargin + totalWidth / 2 - cardHalfWidth)
|
||||||
|
others[0].position = { x, y: topYRootRow }
|
||||||
|
} else if (others.length > 1) {
|
||||||
|
const step = others.length > 1 ? (totalWidth / (others.length - 1)) : 0
|
||||||
|
others.forEach((n, i) => {
|
||||||
|
const xi = xMargin + i * step - cardHalfWidth
|
||||||
|
n.position = { x: Math.max(xMargin, xi), y: topYRootRow }
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Arrange lanes
|
// Arrange lanes
|
||||||
let laneIndex = 0
|
let laneIndex = 0
|
||||||
|
|||||||
@ -83,6 +83,7 @@ function parseOrganizationFromText(text: string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Abteilung (inkl. Zentralabteilung erkennen)
|
||||||
const abtMatch = line.match(/Abteilung\s+(\d+|Zentralabteilung)/i)
|
const abtMatch = line.match(/Abteilung\s+(\d+|Zentralabteilung)/i)
|
||||||
if (abtMatch) {
|
if (abtMatch) {
|
||||||
const abtNum = abtMatch[1] === 'Zentralabteilung' ? 'ZA' : abtMatch[1]
|
const abtNum = abtMatch[1] === 'Zentralabteilung' ? 'ZA' : abtMatch[1]
|
||||||
@ -100,6 +101,32 @@ function parseOrganizationFromText(text: string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zentralabteilung alleinstehend (ohne Präfix "Abteilung")
|
||||||
|
if (/^Zentralabteilung\b/i.test(line) || /^ZA\b/i.test(line)) {
|
||||||
|
currentAbteilung = ensure({
|
||||||
|
code: 'Abt ZA',
|
||||||
|
name: 'Zentralabteilung',
|
||||||
|
type: 'abteilung',
|
||||||
|
level: 1,
|
||||||
|
parentId: 'DIR',
|
||||||
|
color: colors['ZA'] || '#6b7280',
|
||||||
|
hasFuehrungsstelle: false
|
||||||
|
})
|
||||||
|
currentDezernat = null
|
||||||
|
// nicht continue; nachfolgende Muster können weitere Details liefern
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dezernat ZA N (z. B. "Dez ZA 1" bis "Dez ZA 5")
|
||||||
|
const dezZaMatch = line.match(/^(?:Dezernat|Dez)\s+ZA\s*(\d{1,2})/i)
|
||||||
|
if (dezZaMatch) {
|
||||||
|
const zaNum = dezZaMatch[1].trim()
|
||||||
|
const dezName = line.replace(/^(?:Dezernat|Dez)\s+ZA\s*\d{1,2}\s*-?\s*/i, '').trim() || `Dezernat ZA ${zaNum}`
|
||||||
|
// Ensure Abt ZA exists
|
||||||
|
ensure({ code: 'Abt ZA', name: 'Zentralabteilung', type: 'abteilung', level: 1, parentId: 'DIR', color: colors['ZA'] || '#6b7280', hasFuehrungsstelle: false })
|
||||||
|
currentDezernat = ensure({ code: `Dez ZA ${zaNum}`, name: dezName, type: 'dezernat', level: 2, parentId: 'Abt ZA' })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
const dezMatch = line.match(/^(?:Dezernat|Dez)\s+([\d]+)/i)
|
const dezMatch = line.match(/^(?:Dezernat|Dez)\s+([\d]+)/i)
|
||||||
if (dezMatch) {
|
if (dezMatch) {
|
||||||
const dezNum = dezMatch[1].trim()
|
const dezNum = dezMatch[1].trim()
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren