Files
breakpilot-compliance/admin-compliance/app/sdk/rollenkonzept/_hooks/useOrgRoles.ts
T
Benjamin Admin 4c92b17617 feat: Rollenkonzept module + Document Generator review integration (Phase 4-5)
- New /sdk/rollenkonzept/ module with 3 tabs (Rollen, Zuordnung, Reviews)
- 7 standard compliance roles (DSB, GF, IT-Leiter, HR, Marketing, Compliance, Einkauf)
- Inline role editing with test email via Mailpit
- Document-to-role mapping table (editable per tenant)
- Review list with status filters and approve/reject workflow
- ReviewAssignmentPanel in Document Generator preview tab
- "Zur Pruefung senden" button creates reviews + sends notification emails
- Approval notification sent to all affected roles after document sign-off
- Sidebar navigation link added

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-03 13:09:32 +02:00

85 lines
2.9 KiB
TypeScript

import { useState, useEffect, useCallback } from 'react'
import { useSDK } from '@/lib/sdk'
import type { OrgRole, DefaultRole, RoleMapping } from '../_types'
const API_BASE = '/api/sdk/v1/compliance/org-roles'
async function apiFetch<T>(url: string, init?: RequestInit): Promise<T> {
const res = await fetch(url, {
headers: { 'Content-Type': 'application/json' },
...init,
})
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
return res.json()
}
export function useOrgRoles() {
const { projectId } = useSDK()
const [roles, setRoles] = useState<OrgRole[]>([])
const [defaults, setDefaults] = useState<DefaultRole[]>([])
const [mapping, setMapping] = useState<RoleMapping[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const loadRoles = useCallback(async () => {
try {
setLoading(true)
const qs = projectId ? `?project_id=${projectId}` : ''
const [rolesData, defaultsData, mappingData] = await Promise.all([
apiFetch<OrgRole[]>(`${API_BASE}${qs}`),
apiFetch<DefaultRole[]>(`${API_BASE}/defaults`),
apiFetch<RoleMapping[]>(`${API_BASE}/mapping`),
])
setRoles(rolesData)
setDefaults(defaultsData)
setMapping(mappingData)
setError(null)
} catch (e) {
setError(e instanceof Error ? e.message : 'Fehler beim Laden')
} finally {
setLoading(false)
}
}, [projectId])
useEffect(() => { loadRoles() }, [loadRoles])
const seedRoles = useCallback(async () => {
const qs = projectId ? `?project_id=${projectId}` : ''
await apiFetch(`${API_BASE}/seed${qs}`, { method: 'POST' })
await loadRoles()
}, [projectId, loadRoles])
const updateRole = useCallback(async (roleId: string, data: Partial<OrgRole>) => {
const updated = await apiFetch<OrgRole>(`${API_BASE}/${roleId}`, {
method: 'PUT',
body: JSON.stringify(data),
})
setRoles(prev => prev.map(r => r.id === roleId ? updated : r))
return updated
}, [])
const sendTestEmail = useCallback(async (roleId: string) => {
return apiFetch<{ sent: boolean; email: string }>(`${API_BASE}/${roleId}/send-test`, {
method: 'POST',
})
}, [])
const updateMapping = useCallback(async (entries: { document_type: string; role_key: string; is_primary: boolean }[]) => {
await apiFetch(`${API_BASE}/mapping`, {
method: 'PUT',
body: JSON.stringify({ entries }),
})
await loadRoles()
}, [loadRoles])
// Get role by key (merge defaults with actual role data)
const getRoleByKey = useCallback((key: string): OrgRole | DefaultRole | undefined => {
return roles.find(r => r.role_key === key) || defaults.find(d => d.role_key === key)
}, [roles, defaults])
return {
roles, defaults, mapping, loading, error,
loadRoles, seedRoles, updateRole, sendTestEmail, updateMapping, getRoleByKey,
}
}