fix:user invitations

This commit is contained in:
Ida Dittrich 2026-02-16 09:31:26 +01:00
parent 7366da06ac
commit 1fe4f4cad9

View file

@ -8,7 +8,6 @@
import React, { useState, useEffect, useMemo } from 'react';
import { useInvitations, type Invitation, type InvitationCreate } from '../../hooks/useInvitations';
import { useUserMandates, type Mandate, type Role } from '../../hooks/useUserMandates';
import { useFeatureAccess, type FeatureInstance } from '../../hooks/useFeatureAccess';
import { FormGeneratorTable } from '../../components/FormGenerator/FormGeneratorTable';
import { FormGeneratorForm, type AttributeDefinition } from '../../components/FormGenerator/FormGeneratorForm';
import { FaPlus, FaSync, FaEnvelopeOpenText, FaBuilding, FaCopy, FaLink } from 'react-icons/fa';
@ -29,12 +28,10 @@ export const AdminInvitationsPage: React.FC = () => {
} = useInvitations();
const { fetchMandates, fetchRoles } = useUserMandates();
const { fetchInstances } = useFeatureAccess();
// State
const [mandates, setMandates] = useState<Mandate[]>([]);
const [selectedMandateId, setSelectedMandateId] = useState<string>('');
const [featureInstances, setFeatureInstances] = useState<FeatureInstance[]>([]);
const [roles, setRoles] = useState<Role[]>([]);
const [showCreateModal, setShowCreateModal] = useState(false);
const [showUrlModal, setShowUrlModal] = useState<Invitation | null>(null);
@ -61,18 +58,13 @@ export const AdminInvitationsPage: React.FC = () => {
}).catch(() => setBackendAttributes([]));
}, [fetchMandates]);
// Load invitations, feature instances, and roles when mandate changes
// Load invitations and roles when mandate changes (same roles as AdminUserMandatesPage: user, viewer, admin)
useEffect(() => {
if (selectedMandateId) {
fetchInvitations(selectedMandateId, { includeExpired: showExpired, includeUsed: showUsed });
fetchInstances(selectedMandateId).then(instances => {
setFeatureInstances(instances);
});
fetchRoles(selectedMandateId).then(fetchedRoles => {
setRoles(fetchedRoles);
});
fetchRoles(selectedMandateId).then(setRoles);
}
}, [selectedMandateId, showExpired, showUsed, fetchInvitations, fetchInstances, fetchRoles]);
}, [selectedMandateId, showExpired, showUsed, fetchInvitations, fetchRoles]);
// Format timestamp
const formatDate = (timestamp: number) => {
@ -164,29 +156,20 @@ export const AdminInvitationsPage: React.FC = () => {
},
], [roles]);
// Form attributes from backend - merge with dynamic instance and role options
// Form attributes - same role options as AdminUserMandatesPage (user, viewer, admin)
const createFields: AttributeDefinition[] = useMemo(() => {
const excludedFields = ['id', 'mandateId', 'token', 'createdBy', 'createdAt', 'expiresAt', 'currentUses', 'inviteUrl'];
const excludedFields = ['id', 'mandateId', 'token', 'createdBy', 'createdAt', 'expiresAt', 'currentUses', 'inviteUrl', 'featureInstanceId'];
// Feature instance options
const instanceOptions = featureInstances.map(i => ({
value: i.id,
label: i.label || `${i.featureCode} (${i.id.slice(0, 8)}...)`
}));
// Instance-level roles (with featureInstanceId)
// Mandate-level roles (user, viewer, admin) - same as when adding mandate members
const roleOptions = roles
.filter(r => !!r.featureInstanceId) // Only instance-level roles
.map(r => ({ value: r.id, label: `${r.roleLabel} (${featureInstances.find(i => i.id === r.featureInstanceId)?.label || r.featureCode || ''})` }));
.filter(r => !r.featureInstanceId)
.map(r => ({ value: r.id, label: r.roleLabel }));
const fields = backendAttributes
.filter(attr => !excludedFields.includes(attr.name))
.map(attr => ({
...attr,
// Override options with dynamic data
options: attr.name === 'roleIds' ? roleOptions
: attr.name === 'featureInstanceId' ? instanceOptions
: attr.options,
options: attr.name === 'roleIds' ? roleOptions : attr.options,
})) as AttributeDefinition[];
// Add helper field expiresInHours if not in model but fields exist
@ -194,8 +177,14 @@ export const AdminInvitationsPage: React.FC = () => {
fields.push({ name: 'expiresInHours', label: 'Gültigkeitsdauer (Stunden)', type: 'number',
required: true, default: 72 } as any);
}
return fields;
}, [roles, featureInstances, backendAttributes]);
// Override required for targetUsername and email (both required for invitations)
return fields.map(f => {
if (f.name === 'targetUsername' || f.name === 'email') {
return { ...f, required: true };
}
return f;
});
}, [roles, backendAttributes]);
// Handle create invitation
const handleCreateInvitation = async (data: InvitationCreate) => {
@ -326,7 +315,6 @@ export const AdminInvitationsPage: React.FC = () => {
<button
className={styles.primaryButton}
onClick={() => setShowCreateModal(true)}
disabled={roles.length === 0}
>
<FaPlus /> Neue Einladung
</button>
@ -358,7 +346,6 @@ export const AdminInvitationsPage: React.FC = () => {
<button
className={styles.primaryButton}
onClick={() => setShowCreateModal(true)}
disabled={roles.length === 0}
>
<FaPlus /> Erste Einladung erstellen
</button>
@ -414,8 +401,11 @@ export const AdminInvitationsPage: React.FC = () => {
</button>
</div>
<div className={styles.modalContent}>
{roles.length === 0 ? (
<p>Keine Rollen verfügbar. Erstellen Sie zuerst Rollen für diesen Mandanten.</p>
{roles.filter(r => !r.featureInstanceId).length === 0 ? (
<div className={styles.loadingContainer}>
<div className={styles.spinner} />
<span>Lade Rollen...</span>
</div>
) : createFields.length === 0 ? (
<div className={styles.loadingContainer}>
<div className={styles.spinner} />