Fix: Offboarding-Checkliste nach Abteilung filtern, Rollen-Filterung pro Team
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -38,9 +38,14 @@ class OffboardingProtocol {
|
|||||||
e.first_name as employee_first_name,
|
e.first_name as employee_first_name,
|
||||||
e.last_name as employee_last_name,
|
e.last_name as employee_last_name,
|
||||||
e.email as employee_email,
|
e.email as employee_email,
|
||||||
|
e.department as employee_department,
|
||||||
r.name as employee_role_name,
|
r.name as employee_role_name,
|
||||||
cu.username as created_by_username,
|
cu.username as created_by_username,
|
||||||
uu.username as updated_by_username
|
uu.username as updated_by_username,
|
||||||
|
(SELECT d.id FROM onboarding_departments d
|
||||||
|
WHERE LOWER(d.name) LIKE LOWER('%' || COALESCE(e.department,'') || '%')
|
||||||
|
AND COALESCE(e.department,'') != ''
|
||||||
|
ORDER BY d.id LIMIT 1) as employee_department_id
|
||||||
FROM offboarding_protocols op
|
FROM offboarding_protocols op
|
||||||
INNER JOIN users e ON op.employee_user_id = e.id
|
INNER JOIN users e ON op.employee_user_id = e.id
|
||||||
INNER JOIN roles r ON e.role_id = r.id
|
INNER JOIN roles r ON e.role_id = r.id
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ const TEAM_ICONS = { it: '💻', hr: '🧑💼', buchhaltung: '💶' };
|
|||||||
const TEAM_LABELS = { it: 'IT', hr: 'HR / Personal', buchhaltung: 'Buchhaltung' };
|
const TEAM_LABELS = { it: 'IT', hr: 'HR / Personal', buchhaltung: 'Buchhaltung' };
|
||||||
const TAG_COLORS = { critical: '#ef4444', important: '#f59e0b', normal: '#64748b' };
|
const TAG_COLORS = { critical: '#ef4444', important: '#f59e0b', normal: '#64748b' };
|
||||||
|
|
||||||
|
const ROLE_TO_TEAM = { hr_personal: 'hr', buchhaltung: 'buchhaltung', produktion: 'it' };
|
||||||
|
|
||||||
const STATUS_LABEL = { pending: 'Ausstehend', in_progress: 'In Bearbeitung', completed: 'Abgeschlossen' };
|
const STATUS_LABEL = { pending: 'Ausstehend', in_progress: 'In Bearbeitung', completed: 'Abgeschlossen' };
|
||||||
const STATUS_COLOR = { pending: '#f59e0b', in_progress: '#3b82f6', completed: '#34d399' };
|
const STATUS_COLOR = { pending: '#f59e0b', in_progress: '#3b82f6', completed: '#34d399' };
|
||||||
|
|
||||||
@@ -29,18 +31,22 @@ function StatusBadge({ status }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProcessChecklist({ processes, checkedItems, onChange, disabled }) {
|
function ProcessChecklist({ processes, checkedItems, onChange, disabled, userTeam = null }) {
|
||||||
if (!processes?.length) return (
|
if (!processes?.length) return (
|
||||||
<p style={{ color: 'var(--text-muted)', fontSize: 13 }}>Keine Prozesse konfiguriert.</p>
|
<p style={{ color: 'var(--text-muted)', fontSize: 13 }}>Keine Prozesse konfiguriert.</p>
|
||||||
);
|
);
|
||||||
|
|
||||||
const teams = ['it', 'hr', 'buchhaltung'];
|
// Wenn userTeam gesetzt: nur dieses Team anzeigen
|
||||||
|
const visibleTeams = userTeam ? [userTeam] : ['it', 'hr', 'buchhaltung'];
|
||||||
|
const visibleProcesses = userTeam ? processes.filter(p => p.responsible_team === userTeam) : processes;
|
||||||
|
|
||||||
|
const teams = visibleTeams;
|
||||||
const teamMap = {};
|
const teamMap = {};
|
||||||
teams.forEach(t => { teamMap[t] = []; });
|
teams.forEach(t => { teamMap[t] = []; });
|
||||||
processes.forEach(p => { if (teamMap[p.responsible_team]) teamMap[p.responsible_team].push(p); });
|
visibleProcesses.forEach(p => { if (teamMap[p.responsible_team]) teamMap[p.responsible_team].push(p); });
|
||||||
|
|
||||||
const total = processes.length;
|
const total = visibleProcesses.length;
|
||||||
const checked = processes.filter(p => !!checkedItems[`proc_${p.id}`]).length;
|
const checked = visibleProcesses.filter(p => !!checkedItems[`proc_${p.id}`]).length;
|
||||||
const pct = total > 0 ? Math.round(checked / total * 100) : 0;
|
const pct = total > 0 ? Math.round(checked / total * 100) : 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -134,17 +140,17 @@ export default function OffboardingDetailPage() {
|
|||||||
const [assetReturns, setAssetReturns] = useState([]);
|
const [assetReturns, setAssetReturns] = useState([]);
|
||||||
|
|
||||||
const canManage = isAdmin() || isSuperAdmin();
|
const canManage = isAdmin() || isSuperAdmin();
|
||||||
const canAct = canViewLifecycle(); // hr + support + admin dürfen handeln
|
const canAct = canViewLifecycle();
|
||||||
|
const userTeam = (canManage) ? null : (ROLE_TO_TEAM[user?.role_name] || null);
|
||||||
|
|
||||||
useEffect(() => { load(); }, [id]);
|
useEffect(() => { load(); }, [id]);
|
||||||
|
|
||||||
const load = async () => {
|
const load = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [p, procs] = await Promise.all([
|
const p = await offboardingService.getById(id);
|
||||||
offboardingService.getById(id),
|
const deptId = p.employee_department_id || undefined;
|
||||||
onboardingProcessService.getChecklistProcesses('offboarding').catch(() => []),
|
const procs = await onboardingProcessService.getChecklistProcesses('offboarding', deptId).catch(() => []);
|
||||||
]);
|
|
||||||
setProtocol(p);
|
setProtocol(p);
|
||||||
setChecklist(p.checklist_data ? JSON.parse(p.checklist_data) : {});
|
setChecklist(p.checklist_data ? JSON.parse(p.checklist_data) : {});
|
||||||
setNotes(p.notes || '');
|
setNotes(p.notes || '');
|
||||||
@@ -228,8 +234,9 @@ export default function OffboardingDetailPage() {
|
|||||||
if (loading) return <div className="main-content"><LoadingSpinner /></div>;
|
if (loading) return <div className="main-content"><LoadingSpinner /></div>;
|
||||||
if (!protocol) return null;
|
if (!protocol) return null;
|
||||||
|
|
||||||
const total = processes.length;
|
const visibleProcs = userTeam ? processes.filter(p => p.responsible_team === userTeam) : processes;
|
||||||
const checked = processes.filter(p => !!checklist[`proc_${p.id}`]).length;
|
const total = visibleProcs.length;
|
||||||
|
const checked = visibleProcs.filter(p => !!checklist[`proc_${p.id}`]).length;
|
||||||
const pct = total > 0 ? Math.round(checked / total * 100) : 0;
|
const pct = total > 0 ? Math.round(checked / total * 100) : 0;
|
||||||
const statusColor = STATUS_COLOR[protocol.status] || '#6b7280';
|
const statusColor = STATUS_COLOR[protocol.status] || '#6b7280';
|
||||||
const initials = (protocol.employee_name || protocol.employee_email || '??').slice(0, 2).toUpperCase();
|
const initials = (protocol.employee_name || protocol.employee_email || '??').slice(0, 2).toUpperCase();
|
||||||
@@ -411,6 +418,7 @@ export default function OffboardingDetailPage() {
|
|||||||
checkedItems={checklist}
|
checkedItems={checklist}
|
||||||
onChange={setChecklist}
|
onChange={setChecklist}
|
||||||
disabled={protocol.status === 'completed'}
|
disabled={protocol.status === 'completed'}
|
||||||
|
userTeam={userTeam}
|
||||||
/>
|
/>
|
||||||
{protocol.status !== 'completed' && canAct && (
|
{protocol.status !== 'completed' && canAct && (
|
||||||
<div style={{ marginTop: 16, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
|
<div style={{ marginTop: 16, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user