Update changes
Dieser Commit ist enthalten in:
@ -11,6 +11,7 @@ import { FieldEncryption } from '../services/encryption'
|
|||||||
import { emailService } from '../services/emailService'
|
import { emailService } from '../services/emailService'
|
||||||
import { logger } from '../utils/logger'
|
import { logger } from '../utils/logger'
|
||||||
import { createDepartmentResolver } from '../utils/department'
|
import { createDepartmentResolver } from '../utils/department'
|
||||||
|
import { decodeHtmlEntities } from '../utils/html'
|
||||||
|
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
@ -205,6 +206,23 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
|
|||||||
primaryUnitId: emp.primary_unit_id,
|
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 = {
|
const employee: Employee = {
|
||||||
id: emp.id,
|
id: emp.id,
|
||||||
firstName: emp.first_name,
|
firstName: emp.first_name,
|
||||||
@ -213,9 +231,9 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
|
|||||||
photo: emp.photo,
|
photo: emp.photo,
|
||||||
position: emp.position,
|
position: emp.position,
|
||||||
officialTitle: emp.official_title || undefined,
|
officialTitle: emp.official_title || undefined,
|
||||||
department: departmentInfo.label || emp.department,
|
department: finalLabel,
|
||||||
departmentDescription: departmentInfo.description,
|
departmentDescription: departmentInfo.description,
|
||||||
departmentTasks: departmentInfo.tasks,
|
departmentTasks: finalTasks,
|
||||||
email: emp.email,
|
email: emp.email,
|
||||||
phone: emp.phone,
|
phone: emp.phone,
|
||||||
mobile: emp.mobile,
|
mobile: emp.mobile,
|
||||||
@ -298,6 +316,23 @@ router.get('/public', authenticate, async (req: AuthRequest, res, next) => {
|
|||||||
primaryUnitId: emp.primary_unit_id,
|
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 = {
|
const employee: Employee = {
|
||||||
id: emp.id,
|
id: emp.id,
|
||||||
firstName: emp.first_name,
|
firstName: emp.first_name,
|
||||||
@ -306,9 +341,9 @@ router.get('/public', authenticate, async (req: AuthRequest, res, next) => {
|
|||||||
photo: emp.photo,
|
photo: emp.photo,
|
||||||
position: emp.position,
|
position: emp.position,
|
||||||
officialTitle: emp.official_title || undefined,
|
officialTitle: emp.official_title || undefined,
|
||||||
department: departmentInfo.label || emp.department,
|
department: finalLabel,
|
||||||
departmentDescription: departmentInfo.description,
|
departmentDescription: departmentInfo.description,
|
||||||
departmentTasks: departmentInfo.tasks,
|
departmentTasks: finalTasks,
|
||||||
email: emp.email,
|
email: emp.email,
|
||||||
phone: emp.phone,
|
phone: emp.phone,
|
||||||
mobile: emp.mobile,
|
mobile: emp.mobile,
|
||||||
|
|||||||
@ -53,16 +53,21 @@ const splitPathAndTask = (value?: string | null): { path: string; task?: string
|
|||||||
const normalized = normalizeDepartment(value)
|
const normalized = normalizeDepartment(value)
|
||||||
if (!normalized) return { path: '' }
|
if (!normalized) return { path: '' }
|
||||||
const separator = ' -> '
|
const separator = ' -> '
|
||||||
const lastIndex = normalized.lastIndexOf(separator)
|
const segments = normalized.split(separator).map(s => s.trim()).filter(Boolean)
|
||||||
if (lastIndex === -1) {
|
if (segments.length <= 1) {
|
||||||
return { path: normalized }
|
return { path: normalized }
|
||||||
}
|
}
|
||||||
const path = normalized.slice(0, lastIndex)
|
const lastSeg = segments[segments.length - 1]
|
||||||
const task = normalized.slice(lastIndex + separator.length)
|
// Heuristik: Wenn das letzte Segment wie ein Organisationscode aussieht (z. B. "Abt 4", "Dez 41", "SG 41.1"),
|
||||||
return {
|
// ist es Teil der Hierarchie und KEIN Aufgaben-Text. In dem Fall nichts abtrennen.
|
||||||
path: path || normalized,
|
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
|
||||||
task: task || undefined
|
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 = (
|
export const formatDepartmentWithDescription = (
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren