From 7798463e247514e195b32324493de5627a1fa2b9 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 10 Feb 2026 10:56:28 +0100
Subject: [PATCH] enhanced invitation
---
src/App.tsx | 4 ++--
src/hooks/useInvitations.ts | 1 +
src/pages/InvitePage.tsx | 34 +++++++++++++++++++++++-----------
src/pages/Login.tsx | 4 +++-
src/pages/Register.tsx | 7 +++++--
5 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/src/App.tsx b/src/App.tsx
index 65c91e3..de77ea8 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -77,7 +77,6 @@ function App() {
-
{/* ================================================== */}
@@ -94,7 +93,9 @@ function App() {
{/* ================================================== */}
+
+
}>
{/* Dashboard (Root) */}
@@ -190,7 +191,6 @@ function App() {
} />
-
diff --git a/src/hooks/useInvitations.ts b/src/hooks/useInvitations.ts
index e8dbd79..040d95b 100644
--- a/src/hooks/useInvitations.ts
+++ b/src/hooks/useInvitations.ts
@@ -65,6 +65,7 @@ export interface InvitationValidation {
roleIds: string[];
roleLabels?: string[];
targetUsername?: string;
+ email?: string;
}
diff --git a/src/pages/InvitePage.tsx b/src/pages/InvitePage.tsx
index f5cd0ab..85c83bc 100644
--- a/src/pages/InvitePage.tsx
+++ b/src/pages/InvitePage.tsx
@@ -24,6 +24,7 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { useInvitations, type InvitationValidation } from '../hooks/useInvitations';
+import api from '../api';
import { FaCheckCircle, FaTimesCircle, FaSpinner, FaSignInAlt, FaUserPlus } from 'react-icons/fa';
import styles from './InvitePage.module.css';
@@ -35,7 +36,7 @@ export const InvitePage: React.FC = () => {
const navigate = useNavigate();
const { validateInvitation, acceptInvitation } = useInvitations();
- // Check if user has auth token (simplified check)
+ // Check if user has an active session
const isAuthenticated = !!sessionStorage.getItem('auth_authority');
// State
@@ -68,12 +69,11 @@ export const InvitePage: React.FC = () => {
// Check if the target username already has an account
if (result.targetUsername) {
try {
- const resp = await fetch(`/api/local/available?username=${encodeURIComponent(result.targetUsername)}`);
- if (resp.ok) {
- const data = await resp.json();
- // available=true means username is free -> user does NOT exist
- setUserExists(!data.available);
- }
+ const resp = await api.get(`/api/local/available`, {
+ params: { username: result.targetUsername }
+ });
+ // available=true means username is free -> user does NOT exist
+ setUserExists(!resp.data.available);
} catch {
// On error, default to showing both options
setUserExists(null);
@@ -104,6 +104,10 @@ export const InvitePage: React.FC = () => {
setTimeout(() => {
navigate('/');
}, 2000);
+ } else if (result.error?.includes('401') || result.error?.includes('Not authenticated')) {
+ // Session expired — clear auth and redirect to login
+ sessionStorage.removeItem('auth_authority');
+ handleLoginRedirect();
} else {
setError(result.error || 'Fehler beim Annehmen der Einladung');
}
@@ -111,20 +115,28 @@ export const InvitePage: React.FC = () => {
setAccepting(false);
};
- // Handle redirect to login (stores token first)
+ // Handle redirect to login (stores token first, passes invitation data for pre-fill)
const handleLoginRedirect = () => {
if (token) {
localStorage.setItem(PENDING_INVITATION_KEY, token);
}
- navigate('/login', { state: { from: { pathname: `/invite/${token}` } } });
+ navigate('/login', { state: {
+ from: { pathname: `/invite/${token}` },
+ invitationUsername: validation?.targetUsername || '',
+ } });
};
- // Handle redirect to register (stores token first)
+ // Handle redirect to register (stores token first, passes invitation data for pre-fill)
const handleRegisterRedirect = () => {
if (token) {
localStorage.setItem(PENDING_INVITATION_KEY, token);
}
- navigate('/register', { state: { from: { pathname: `/invite/${token}` }, pendingInvitation: true } });
+ navigate('/register', { state: {
+ from: { pathname: `/invite/${token}` },
+ pendingInvitation: true,
+ invitationUsername: validation?.targetUsername || '',
+ invitationEmail: validation?.email || '',
+ } });
};
// Loading state
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
index 9756d96..d06bca0 100644
--- a/src/pages/Login.tsx
+++ b/src/pages/Login.tsx
@@ -12,7 +12,9 @@ import styles from './Login.module.css';
function Login() {
const navigate = useNavigate();
const location = useLocation();
- const [username, setUsername] = useState('');
+ // Pre-fill username from invitation if provided via location.state
+ const invitationUsername = (location.state as any)?.invitationUsername || '';
+ const [username, setUsername] = useState(invitationUsername);
const [password, setPassword] = useState('');
const [usernameFocused, setUsernameFocused] = useState(false);
const [passwordFocused, setPasswordFocused] = useState(false);
diff --git a/src/pages/Register.tsx b/src/pages/Register.tsx
index 2602d10..a3c1403 100644
--- a/src/pages/Register.tsx
+++ b/src/pages/Register.tsx
@@ -19,9 +19,12 @@ function Register() {
const { register, error: registerError, isLoading } = useRegister();
const { error: msalError } = useMsalRegister();
const { checkAvailability, isChecking, error: availabilityError } = useUsernameAvailability();
+ // Pre-fill from invitation if provided via location.state
+ const invitationUsername = (location.state as any)?.invitationUsername || '';
+ const invitationEmail = (location.state as any)?.invitationEmail || '';
const [formData, setFormData] = useState({
- username: '',
- email: '',
+ username: invitationUsername,
+ email: invitationEmail,
fullName: ''
});
const [validationError, setValidationError] = useState(null);