Dieser Commit ist enthalten in:
Claude Project Manager
2025-10-16 09:05:11 +02:00
Ursprung 4d509d255f
Commit 4b5fec1903
2 geänderte Dateien mit 51 neuen und 11 gelöschten Zeilen

Datei anzeigen

@ -11,6 +11,7 @@ import { FieldEncryption } from '../services/encryption'
import { emailService } from '../services/emailService'
import { logger } from '../utils/logger'
import { createDepartmentResolver } from '../utils/department'
import { decodeHtmlEntities } from '../utils/html'
const router = Router()
@ -205,6 +206,23 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
primaryUnitId: emp.primary_unit_id,
})
// Prefer freshly stored department path if it differs from resolver (e.g., after profile update but before assignment sync)
const storedDeptRaw = (decodeHtmlEntities(emp.department) ?? emp.department ?? '').trim()
const splitStored = (() => {
const sep = ' -> '
const idx = storedDeptRaw.lastIndexOf(sep)
if (idx === -1) return { path: storedDeptRaw, task: undefined as string | undefined }
return { path: storedDeptRaw.slice(0, idx) || storedDeptRaw, task: storedDeptRaw.slice(idx + sep.length) || undefined }
})()
const resolvedLabel = (departmentInfo.label || '').trim()
const last = (s: string) => s.split(' -> ').map(p => p.trim()).filter(Boolean).pop() || ''
const storedLast = last(splitStored.path)
const resolvedLast = last(resolvedLabel)
const finalLabel = (storedLast && resolvedLast && storedLast !== resolvedLast)
? splitStored.path
: (resolvedLabel || splitStored.path)
const finalTasks = splitStored.task || departmentInfo.tasks
const employee: Employee = {
id: emp.id,
firstName: emp.first_name,
@ -213,9 +231,9 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: departmentInfo.label || emp.department,
department: finalLabel,
departmentDescription: departmentInfo.description,
departmentTasks: departmentInfo.tasks,
departmentTasks: finalTasks,
email: emp.email,
phone: emp.phone,
mobile: emp.mobile,
@ -298,6 +316,23 @@ router.get('/public', authenticate, async (req: AuthRequest, res, next) => {
primaryUnitId: emp.primary_unit_id,
})
// Prefer freshly stored department path if it differs from resolver (e.g., after profile update but before assignment sync)
const storedDeptRaw = (decodeHtmlEntities(emp.department) ?? emp.department ?? '').trim()
const splitStored = (() => {
const sep = ' -> '
const idx = storedDeptRaw.lastIndexOf(sep)
if (idx === -1) return { path: storedDeptRaw, task: undefined as string | undefined }
return { path: storedDeptRaw.slice(0, idx) || storedDeptRaw, task: storedDeptRaw.slice(idx + sep.length) || undefined }
})()
const resolvedLabel = (departmentInfo.label || '').trim()
const last = (s: string) => s.split(' -> ').map(p => p.trim()).filter(Boolean).pop() || ''
const storedLast = last(splitStored.path)
const resolvedLast = last(resolvedLabel)
const finalLabel = (storedLast && resolvedLast && storedLast !== resolvedLast)
? splitStored.path
: (resolvedLabel || splitStored.path)
const finalTasks = splitStored.task || departmentInfo.tasks
const employee: Employee = {
id: emp.id,
firstName: emp.first_name,
@ -306,9 +341,9 @@ router.get('/public', authenticate, async (req: AuthRequest, res, next) => {
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: departmentInfo.label || emp.department,
department: finalLabel,
departmentDescription: departmentInfo.description,
departmentTasks: departmentInfo.tasks,
departmentTasks: finalTasks,
email: emp.email,
phone: emp.phone,
mobile: emp.mobile,

Datei anzeigen

@ -53,16 +53,21 @@ const splitPathAndTask = (value?: string | null): { path: string; task?: string
const normalized = normalizeDepartment(value)
if (!normalized) return { path: '' }
const separator = ' -> '
const lastIndex = normalized.lastIndexOf(separator)
if (lastIndex === -1) {
const segments = normalized.split(separator).map(s => s.trim()).filter(Boolean)
if (segments.length <= 1) {
return { path: normalized }
}
const path = normalized.slice(0, lastIndex)
const task = normalized.slice(lastIndex + separator.length)
return {
path: path || normalized,
task: task || undefined
const lastSeg = segments[segments.length - 1]
// Heuristik: Wenn das letzte Segment wie ein Organisationscode aussieht (z. B. "Abt 4", "Dez 41", "SG 41.1"),
// ist es Teil der Hierarchie und KEIN Aufgaben-Text. In dem Fall nichts abtrennen.
const CODE_LIKE_REGEX = /^(dir|lka\s*nrw|lstab|za(\s*\d+)?|abt\.?\s*\d+|abteilung\s*\d+|dez\.?\s*\d+[a-z]?|dezernat\s*\d+[a-z]?|sg\s*\d+(?:\.\d+)?|td\s*\d+(?:\.\d+)?)$/i
if (CODE_LIKE_REGEX.test(lastSeg)) {
return { path: normalized }
}
// Andernfalls letzte Komponente als Aufgaben-Text behandeln
const path = segments.slice(0, -1).join(separator)
const task = lastSeg
return { path: path || normalized, task: task || undefined }
}
export const formatDepartmentWithDescription = (