added functionality to see all users as a user
This commit is contained in:
parent
b827c3e00b
commit
8ca6f28bcd
12 changed files with 256 additions and 53 deletions
|
|
@ -11,7 +11,7 @@ import { AuthProvider } from './auth/authProvider';
|
||||||
import { ProtectedRoute } from './auth/ProtectedRoute';
|
import { ProtectedRoute } from './auth/ProtectedRoute';
|
||||||
import Home from './pages/Home';
|
import Home from './pages/Home';
|
||||||
import Dateien from './pages/Dateien/Dateien';
|
import Dateien from './pages/Dateien/Dateien';
|
||||||
import TeamBereich from './pages/Mitglieder/TeamBereich';
|
import TeamBereich from './pages/TeamBereich/TeamBereich';
|
||||||
import Dashboard from './pages/Dashboard';
|
import Dashboard from './pages/Dashboard';
|
||||||
import Einstellungen from './pages/Einstellungen/Einstellungen';
|
import Einstellungen from './pages/Einstellungen/Einstellungen';
|
||||||
// Import the global light theme CSS variables as default
|
// Import the global light theme CSS variables as default
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,34 @@ const WorkflowStatusDisplay: React.FC<WorkflowStatusDisplayProps> = ({
|
||||||
currentWorkflowId,
|
currentWorkflowId,
|
||||||
workflowStatus,
|
workflowStatus,
|
||||||
workflowCompleted,
|
workflowCompleted,
|
||||||
onStartNewWorkflow
|
onStartNewWorkflow,
|
||||||
|
handleRetry,
|
||||||
|
shouldShowRetryButton
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{currentWorkflowId && !workflowCompleted && (
|
{currentWorkflowId && shouldShowRetryButton() && (
|
||||||
|
<motion.div
|
||||||
|
className={styles.workflow_status}
|
||||||
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.8 }}
|
||||||
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||||
|
>
|
||||||
|
<div className={styles.retry_container}>
|
||||||
|
<span className={styles.failed_message}>
|
||||||
|
Workflow failed.
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={handleRetry}
|
||||||
|
className={styles.retry_button}
|
||||||
|
>
|
||||||
|
Nochmal versuchen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
{currentWorkflowId && !workflowCompleted && !shouldShowRetryButton() && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className={styles.workflow_status}
|
className={styles.workflow_status}
|
||||||
initial={{ opacity: 0, scale: 0.8 }}
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
|
|
||||||
|
|
@ -328,20 +328,62 @@
|
||||||
|
|
||||||
.workflow_status {
|
.workflow_status {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
align-items: center;
|
||||||
color: var(--color-secondary);
|
padding: 10px;
|
||||||
|
margin: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow_status p {
|
.workflow_status p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-secondary);
|
font-size: 14px;
|
||||||
font-size: 13px;
|
color: var(--color-gray);
|
||||||
font-style: italic;
|
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.retry_container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background-color: var(--color-red-disabled);
|
||||||
|
border: 1px solid var(--color-red);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failed_message {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-red);
|
||||||
|
font-family: var(--font-family);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry_button {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-bg);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry_button:hover {
|
||||||
|
background-color: var(--color-primary-hover);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.retry_button:disabled {
|
||||||
|
background-color: var(--color-gray-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
.completion_message {
|
.completion_message {
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
background-color: var(--color-secondary-disabled);
|
background-color: var(--color-secondary-disabled);
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,12 @@ const DashboardChatArea: React.FC<DashboardChatAreaProps> = ({
|
||||||
handleFileAttach,
|
handleFileAttach,
|
||||||
handleFileRemove,
|
handleFileRemove,
|
||||||
handleFilesSelect,
|
handleFilesSelect,
|
||||||
|
handleRetry,
|
||||||
|
|
||||||
// Workflow state
|
// Workflow state
|
||||||
isWorkflowRunning,
|
isWorkflowRunning,
|
||||||
isStoppingWorkflow
|
isStoppingWorkflow,
|
||||||
|
shouldShowRetryButton
|
||||||
} = useChatLogic({
|
} = useChatLogic({
|
||||||
selectedPrompt,
|
selectedPrompt,
|
||||||
onPromptUsed,
|
onPromptUsed,
|
||||||
|
|
@ -73,6 +75,8 @@ const DashboardChatArea: React.FC<DashboardChatAreaProps> = ({
|
||||||
messagesLoading={messagesLoading}
|
messagesLoading={messagesLoading}
|
||||||
onStartNewWorkflow={startNewWorkflow}
|
onStartNewWorkflow={startNewWorkflow}
|
||||||
messagesEndRef={messagesEndRef}
|
messagesEndRef={messagesEndRef}
|
||||||
|
handleRetry={handleRetry}
|
||||||
|
shouldShowRetryButton={shouldShowRetryButton}
|
||||||
/>
|
/>
|
||||||
<ChatInput
|
<ChatInput
|
||||||
inputValue={inputValue}
|
inputValue={inputValue}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ const MessageList: React.FC<MessageListProps> = ({
|
||||||
messagesError,
|
messagesError,
|
||||||
messagesLoading,
|
messagesLoading,
|
||||||
onStartNewWorkflow,
|
onStartNewWorkflow,
|
||||||
messagesEndRef
|
messagesEndRef,
|
||||||
|
handleRetry,
|
||||||
|
shouldShowRetryButton
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -65,6 +67,8 @@ const MessageList: React.FC<MessageListProps> = ({
|
||||||
workflowStatus={workflowStatus}
|
workflowStatus={workflowStatus}
|
||||||
workflowCompleted={workflowCompleted}
|
workflowCompleted={workflowCompleted}
|
||||||
onStartNewWorkflow={onStartNewWorkflow}
|
onStartNewWorkflow={onStartNewWorkflow}
|
||||||
|
handleRetry={handleRetry}
|
||||||
|
shouldShowRetryButton={shouldShowRetryButton}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div ref={messagesEndRef} />
|
<div ref={messagesEndRef} />
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,55 @@ export const useChatLogic = ({
|
||||||
|
|
||||||
const isStoppingWorkflow = currentWorkflowId ? stoppingWorkflows.has(currentWorkflowId) : false;
|
const isStoppingWorkflow = currentWorkflowId ? stoppingWorkflows.has(currentWorkflowId) : false;
|
||||||
|
|
||||||
|
const handleRetry = async () => {
|
||||||
|
if (!currentWorkflowId || !messages.length) {
|
||||||
|
console.error('No workflow ID or messages available for retry');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the last user message to retry
|
||||||
|
const userMessages = messages.filter(msg => msg.role === 'user');
|
||||||
|
const lastUserMessage = userMessages[userMessages.length - 1];
|
||||||
|
|
||||||
|
if (!lastUserMessage) {
|
||||||
|
console.error('No user message found to retry');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Retrying workflow with last user message:', lastUserMessage.content);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Extract file IDs if available from the message
|
||||||
|
const fileIds = lastUserMessage.fileIds || [];
|
||||||
|
|
||||||
|
// Start the workflow again with the same prompt and files
|
||||||
|
const result = await startWorkflow({
|
||||||
|
prompt: lastUserMessage.content,
|
||||||
|
listFileId: fileIds
|
||||||
|
}, currentWorkflowId);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('Workflow retry started successfully');
|
||||||
|
// Reset workflow completion state to resume polling
|
||||||
|
setWorkflowCompleted(false);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to retry workflow:', result.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error retrying workflow:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldShowRetryButton = () => {
|
||||||
|
if (!workflowStatus) return false;
|
||||||
|
|
||||||
|
const statusLower = workflowStatus.status.toLowerCase();
|
||||||
|
return statusLower === 'error' ||
|
||||||
|
statusLower === 'failed' ||
|
||||||
|
statusLower === 'stopped' ||
|
||||||
|
statusLower === 'cancelled';
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
inputValue,
|
inputValue,
|
||||||
|
|
@ -257,9 +306,11 @@ export const useChatLogic = ({
|
||||||
handleFileAttach,
|
handleFileAttach,
|
||||||
handleFileRemove,
|
handleFileRemove,
|
||||||
handleFilesSelect,
|
handleFilesSelect,
|
||||||
|
handleRetry,
|
||||||
|
|
||||||
// Workflow state
|
// Workflow state
|
||||||
isWorkflowRunning,
|
isWorkflowRunning,
|
||||||
isStoppingWorkflow
|
isStoppingWorkflow,
|
||||||
|
shouldShowRetryButton
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -62,6 +62,8 @@ export interface MessageListProps {
|
||||||
messagesLoading: boolean;
|
messagesLoading: boolean;
|
||||||
onStartNewWorkflow: () => void;
|
onStartNewWorkflow: () => void;
|
||||||
messagesEndRef: React.RefObject<HTMLDivElement | null>;
|
messagesEndRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
handleRetry: () => Promise<void>;
|
||||||
|
shouldShowRetryButton: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WorkflowStatusDisplayProps {
|
export interface WorkflowStatusDisplayProps {
|
||||||
|
|
@ -69,4 +71,6 @@ export interface WorkflowStatusDisplayProps {
|
||||||
workflowStatus: WorkflowStatus | null;
|
workflowStatus: WorkflowStatus | null;
|
||||||
workflowCompleted: boolean;
|
workflowCompleted: boolean;
|
||||||
onStartNewWorkflow: () => void;
|
onStartNewWorkflow: () => void;
|
||||||
|
handleRetry: () => Promise<void>;
|
||||||
|
shouldShowRetryButton: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { FaArrowRight } from 'react-icons/fa';
|
import { FaArrowRight } from 'react-icons/fa';
|
||||||
import { AiOutlineDelete } from 'react-icons/ai';
|
import { AiOutlineDelete } from 'react-icons/ai';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
import { useWorkflowOperations, useWorkflowMessages, Workflow } from '../../../../hooks/useWorkflows';
|
import { useWorkflowOperations, useWorkflowMessages, Workflow } from '../../../../hooks/useWorkflows';
|
||||||
import styles from './DashboardChatHistoryItem.module.css';
|
import styles from './DashboardChatHistoryItem.module.css';
|
||||||
|
|
||||||
|
|
@ -50,16 +51,8 @@ function DashboardChatHistoryItem({ workflow, onDelete, onResume }: DashboardCha
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status.toLowerCase()) {
|
switch (status.toLowerCase()) {
|
||||||
case 'completed':
|
|
||||||
case 'finished':
|
|
||||||
case 'done':
|
|
||||||
return 'var(--color-secondary)';
|
|
||||||
case 'running':
|
|
||||||
case 'processing':
|
|
||||||
return 'var(--color-gray)';
|
|
||||||
case 'error':
|
case 'error':
|
||||||
case 'failed':
|
case 'failed':
|
||||||
return 'var(--color-red)';
|
|
||||||
case 'stopped':
|
case 'stopped':
|
||||||
case 'cancelled':
|
case 'cancelled':
|
||||||
return 'var(--color-red)';
|
return 'var(--color-red)';
|
||||||
|
|
@ -70,13 +63,6 @@ function DashboardChatHistoryItem({ workflow, onDelete, onResume }: DashboardCha
|
||||||
|
|
||||||
const getStatusBackgroundColor = (status: string) => {
|
const getStatusBackgroundColor = (status: string) => {
|
||||||
switch (status.toLowerCase()) {
|
switch (status.toLowerCase()) {
|
||||||
case 'completed':
|
|
||||||
case 'finished':
|
|
||||||
case 'done':
|
|
||||||
return 'var(--color-secondary-disabled)';
|
|
||||||
case 'running':
|
|
||||||
case 'processing':
|
|
||||||
return 'var(--color-gray-disabled)';
|
|
||||||
case 'error':
|
case 'error':
|
||||||
case 'failed':
|
case 'failed':
|
||||||
case 'stopped':
|
case 'stopped':
|
||||||
|
|
@ -87,6 +73,59 @@ function DashboardChatHistoryItem({ workflow, onDelete, onResume }: DashboardCha
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shouldShowStatus = (status: string) => {
|
||||||
|
const statusLower = status.toLowerCase();
|
||||||
|
return statusLower === 'error' ||
|
||||||
|
statusLower === 'failed' ||
|
||||||
|
statusLower === 'stopped' ||
|
||||||
|
statusLower === 'cancelled';
|
||||||
|
};
|
||||||
|
|
||||||
|
const isRunning = (status: string) => {
|
||||||
|
const statusLower = status.toLowerCase();
|
||||||
|
return statusLower === 'running' || statusLower === 'processing';
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderStatusIndicator = () => {
|
||||||
|
if (isRunning(workflow.status)) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className={styles.loadingSpinner}
|
||||||
|
animate={{ rotate: 360 }}
|
||||||
|
transition={{
|
||||||
|
duration: 1,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: "linear"
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: '16px',
|
||||||
|
height: '16px',
|
||||||
|
border: '2px solid var(--color-gray-disabled)',
|
||||||
|
borderTop: '2px solid var(--color-secondary)',
|
||||||
|
borderRadius: '50%',
|
||||||
|
display: 'inline-block'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldShowStatus(workflow.status)) {
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={styles.workflowStatus}
|
||||||
|
style={{
|
||||||
|
color: getStatusColor(workflow.status),
|
||||||
|
backgroundColor: getStatusBackgroundColor(workflow.status)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{workflow.status.toUpperCase()}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const truncateMessage = (message: string, maxLength: number = 150) => {
|
const truncateMessage = (message: string, maxLength: number = 150) => {
|
||||||
if (message.length <= maxLength) return message;
|
if (message.length <= maxLength) return message;
|
||||||
return message.substring(0, maxLength) + '...';
|
return message.substring(0, maxLength) + '...';
|
||||||
|
|
@ -101,15 +140,7 @@ function DashboardChatHistoryItem({ workflow, onDelete, onResume }: DashboardCha
|
||||||
Workflow {workflow.id.substring(0, 8)}...
|
Workflow {workflow.id.substring(0, 8)}...
|
||||||
</h3>
|
</h3>
|
||||||
<div className={styles.workflowMeta}>
|
<div className={styles.workflowMeta}>
|
||||||
<span
|
{renderStatusIndicator()}
|
||||||
className={styles.workflowStatus}
|
|
||||||
style={{
|
|
||||||
color: getStatusColor(workflow.status),
|
|
||||||
backgroundColor: getStatusBackgroundColor(workflow.status)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{workflow.status.toUpperCase()}
|
|
||||||
</span>
|
|
||||||
{workflow.currentRound && (
|
{workflow.currentRound && (
|
||||||
<span className={styles.workflowRound}>
|
<span className={styles.workflowRound}>
|
||||||
Round {workflow.currentRound}
|
Round {workflow.currentRound}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
border: 1px solid var(--color-gray-disabled);
|
|
||||||
background: var(--color-bg);
|
background: var(--color-bg);
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0px 2px 6px 0px rgba(194, 194, 194, 0.10);
|
box-shadow: 0px 2px 6px 0px rgba(194, 194, 194, 0.10);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import styles from './TeamBereich.module.css'
|
|
||||||
|
|
||||||
import MitgliederItem from '../../components/Mitglieder/MitgliederItem';
|
|
||||||
import { IoPersonAddSharp } from "react-icons/io5";
|
|
||||||
import { useOrgUsers } from '../../hooks/useUsers';
|
|
||||||
|
|
||||||
function TeamBereich () {
|
|
||||||
|
|
||||||
return (
|
|
||||||
<h1>Team-Bereich</h1>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TeamBereich;
|
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
.membersList {
|
.membersList {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin-top: 30px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: auto; /* Enable vertical scrolling */
|
overflow-y: auto; /* Enable vertical scrolling */
|
||||||
/* Space for the header line */
|
/* Space for the header line */
|
||||||
60
src/pages/TeamBereich/TeamBereich.tsx
Normal file
60
src/pages/TeamBereich/TeamBereich.tsx
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import styles from './TeamBereich.module.css'
|
||||||
|
|
||||||
|
import MitgliederItem from '../../components/Mitglieder/MitgliederItem';
|
||||||
|
import { IoPersonAddSharp } from "react-icons/io5";
|
||||||
|
import { useOrgUsers } from '../../hooks/useUsers';
|
||||||
|
|
||||||
|
function TeamBereich () {
|
||||||
|
const { users, loading, error, refetch } = useOrgUsers();
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className={styles.mitgliederContainer}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h1>Team-Bereich</h1>
|
||||||
|
</div>
|
||||||
|
<p>Lade Mitglieder...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className={styles.mitgliederContainer}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h1>Team-Bereich</h1>
|
||||||
|
</div>
|
||||||
|
<p>Fehler beim Laden der Mitglieder: {error}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.mitgliederContainer}>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h1>Team-Bereich</h1>
|
||||||
|
<button className={styles.mitglieder_hinzufügen_button}>
|
||||||
|
<IoPersonAddSharp className={styles.add_icon} />
|
||||||
|
Mitglied hinzufügen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className={styles.horizontalLineLight}></div>
|
||||||
|
<ul className={styles.membersList}>
|
||||||
|
{users.map((user) => (
|
||||||
|
<MitgliederItem
|
||||||
|
key={user.id}
|
||||||
|
user={user}
|
||||||
|
refetchUsers={refetch}
|
||||||
|
totalUsers={users.length}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
{users.length === 0 && (
|
||||||
|
<p>Keine Mitglieder gefunden.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TeamBereich;
|
||||||
|
|
||||||
Loading…
Reference in a new issue