diff --git a/src/App.tsx b/src/App.tsx index e8e244f..091da5f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,11 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; -import { ProtectedRoute } from "./auth/ProtectedRoute"; + import Login from './pages/Login'; +import Register from './pages/Register'; + +import { AuthProvider } from './auth/auth-provider'; +import { ProtectedRoute } from './auth/ProtectedRoute'; import Home from './pages/Home'; -import Dashboard from './pages/Home/Dashboard/Dashboard'; -import { AuthProvider } from './auth/Hooks/auth-provider'; -import RegisterOrganisation from './pages/Register/RegisterOrganisation'; -import RegisterPricing from './pages/Register/RegisterPricing'; -import RegisterSummary from './pages/Register/RegisterSummary'; -import Mitglieder from './pages/Home/Mitglieder/Mitglieder'; -import Organisation from './pages/Home/Organisation/Organisation'; -import Dateien from './pages/Home/Dateien/Dateien'; function App() { return ( @@ -18,22 +14,12 @@ function App() { {/* Public route */} } /> - } /> - } /> - } /> - - {/* Protected routes */} + } /> - }> - {/* Child route of Home - note the relative path */} - } /> - } /> - } /> - } /> - + }> diff --git a/src/api.ts b/src/api.ts index 3d596b8..f14245a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,126 +1,44 @@ // api.ts import axios from 'axios'; -const API_URL = import.meta.env?.VITE_API_URL || 'http://127.0.0.1:8000'; - const api = axios.create({ - baseURL: API_URL, - headers: { - 'Content-Type': 'application/json', - }, + baseURL: 'http://127.0.0.1:8000', + withCredentials: true }); -export async function authorizedGet(endpoint: string, getToken: () => Promise) { - try { - const token = await getToken(); - // Ensure that token is not null or undefined - if (!token) { - throw new Error("No token found"); +// Add a request interceptor to add the auth token +api.interceptors.request.use( + (config) => { + const authData = localStorage.getItem('auth_data'); + if (authData) { + try { + const { accessToken, tokenType } = JSON.parse(authData); + if (accessToken) { + config.headers.Authorization = `${tokenType} ${accessToken}`; + } + } catch (error) { + console.error('Error parsing auth data:', error); + } } - - const response = await api.get(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - - return response; // Axios already wraps the response, no need to call .data here - } catch (err) { - console.error("API request failed:", err); - throw err; + return config; + }, + (error) => { + return Promise.reject(error); } -} +); -export async function authorizedDelete(endpoint: string, getToken: () => Promise) { - try { - const token = await getToken(); - // Ensure that token is not null or undefined - if (!token) { - throw new Error("No token found"); +// Add a response interceptor to handle token expiration +api.interceptors.response.use( + (response) => response, + async (error) => { + if (error.response?.status === 401) { + // Clear invalid token + localStorage.removeItem('auth_data'); + // Redirect to login + window.location.href = '/login'; } - - const response = await api.delete(endpoint, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return response; // Axios already wraps the response, no need to call .data here - } catch (err) { - console.error("API request failed:", err); - throw err; + return Promise.reject(error); } -} - -export async function authorizedPut(endpoint: string, data: any, getToken: () => Promise) { - try { - const token = await getToken(); - // Ensure that token is not null or undefined - if (!token) { - throw new Error("No token found"); - } - - const response = await api.put(endpoint, data, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return response; - } catch (err) { - console.error("API request failed:", err); - throw err; - } -} - -export async function authorizedPost(endpoint: string, getToken: () => Promise, data: any) { - try { - const token = await getToken(); - // Ensure that token is not null or undefined - if (!token) { - throw new Error("No token found"); - } - - const response = await api.post(endpoint, data, { - headers: { - Authorization: `Bearer ${token}`, - }, - }); - return response; - } catch (err) { - console.error("API request failed:", err); - throw err; - } -} - -export async function downloadFile(fileId: number, getToken: () => Promise) { - try { - const token = await getToken(); - if (!token) { - throw new Error("No token found"); - } - - const response = await api.get(`/api/user/files/${fileId}/download`, { - headers: { - Authorization: `Bearer ${token}`, - }, - responseType: 'blob', // Important for file downloads - }); - - // Create a blob URL and trigger download - const blob = new Blob([response.data]); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = response.headers['content-disposition']?.split('filename=')[1] || 'download'; - document.body.appendChild(a); - a.click(); - window.URL.revokeObjectURL(url); - document.body.removeChild(a); - - return response; - } catch (err) { - console.error("File download failed:", err); - throw err; - } -} +); export default api; diff --git a/src/auth/Hooks/delete-user.ts b/src/auth/Hooks/delete-user.ts deleted file mode 100644 index 7b5aa54..0000000 --- a/src/auth/Hooks/delete-user.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { authorizedDelete } from "../../api"; - -async function deleteUser(userId: string, token: string): Promise { - try { - const response = await authorizedDelete(`/api/user/${userId}`, async () => token); - // You can add additional handling here if needed - console.log("User deleted successfully:", response.status); - } catch (error) { - console.error("Failed to delete user:", error); - throw error; - } -} - -export default deleteUser; diff --git a/src/auth/Hooks/get-all-users.ts b/src/auth/Hooks/get-all-users.ts deleted file mode 100644 index 7e4af87..0000000 --- a/src/auth/Hooks/get-all-users.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { useEffect, useState } from 'react'; -import { authorizedGet } from '../../api'; -import { useAuthToken } from './use-auth-token'; - -type User = { - id: string; - name: string; - azure_id: string; - created_at: string; - role: string; - email: string; - phone: string; - position: string; -}; - -export const useOrgUsers = () => { - const [users, setUsers] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const { getToken } = useAuthToken(); - - const fetchUsers = async () => { - setLoading(true); - setError(null); - try { - const response = await authorizedGet("/api/organisation/users", getToken); - console.log(response) - setUsers(response.data) - } catch (err) { - setError("Failed to fetch users"); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchUsers(); - }, []); - - return { users, loading, error, refetch: fetchUsers }; -}; diff --git a/src/auth/Hooks/use-auth-token.ts b/src/auth/Hooks/use-auth-token.ts deleted file mode 100644 index ab031ca..0000000 --- a/src/auth/Hooks/use-auth-token.ts +++ /dev/null @@ -1,39 +0,0 @@ -// src/hooks/useAuthToken.ts -import { useMsal } from "@azure/msal-react"; -import { useCallback } from "react"; -import { InteractionRequiredAuthError } from "@azure/msal-browser"; - -const tokenRequest = { - scopes: ["api://24cd6c8a-b592-4905-a5ba-d5fa9f911154/user_impersonation"], // Adjust scopes as needed -}; - -export function useAuthToken() { - const { instance, accounts } = useMsal(); - - const getToken = useCallback(async () => { - if (accounts.length === 0) throw new Error("No signed-in account found"); - - try { - const response = await instance.acquireTokenSilent({ - ...tokenRequest, - account: accounts[0], - }); - return response.accessToken; - } catch (err: any) { - if (err instanceof InteractionRequiredAuthError) { - console.warn("Silent token failed, redirecting for interactive login..."); - // Trigger full-page redirect to login - instance.acquireTokenRedirect({ - ...tokenRequest, - account: accounts[0], // you might omit this if it's undefined - }); - return ""; // won't reach this anyway - } - - console.error("Token acquisition failed unexpectedly:", err); - throw err; - } - }, [instance, accounts]); - - return { getToken }; -} diff --git a/src/auth/Hooks/use-msal-login.ts b/src/auth/Hooks/use-msal-login.ts deleted file mode 100644 index cf52792..0000000 --- a/src/auth/Hooks/use-msal-login.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useMsal } from "@azure/msal-react"; -import { useState } from "react"; -import { loginRequest } from "../auth-config"; - -export const useMsalLogin = () => { - const { instance } = useMsal(); - const [isLoggingIn, setIsLoggingIn] = useState(false); - const [error, setError] = useState(null); - - const login = async () => { - setIsLoggingIn(true); - setError(null); - try { - console.log("Starting login process..."); - await instance.loginPopup(loginRequest); // or loginRedirect - console.log("Login successful"); - } catch (err: any) { - console.error("Login failed:", err); - setError(err?.message || "Login failed"); - } finally { - setIsLoggingIn(false); - } - }; - - return { - login, - isLoggingIn, - error, - }; -}; diff --git a/src/auth/Hooks/use-user-files.ts b/src/auth/Hooks/use-user-files.ts deleted file mode 100644 index 5fe77ab..0000000 --- a/src/auth/Hooks/use-user-files.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useEffect, useState } from 'react'; -import { authorizedGet } from '../../api'; -import { useAuthToken } from './use-auth-token'; - -type File = { - id: number; - file_name: string; - action: string; - user_id: number; - prompt_id: number; - meta_data: Record | null; - created_at: string; -}; - -export const useUserFiles = () => { - const [files, setFiles] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const { getToken } = useAuthToken(); - - const fetchFiles = async () => { - setLoading(true); - setError(null); - try { - const response = await authorizedGet("/api/user/files", getToken); - setFiles(response.data); - } catch (err) { - setError("Failed to fetch files"); - console.error("Error fetching files:", err); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchFiles(); - }, []); - - return { files, loading, error, refetch: fetchFiles }; -}; \ No newline at end of file diff --git a/src/auth/Hooks/use-user-info.ts b/src/auth/Hooks/use-user-info.ts deleted file mode 100644 index 17f5187..0000000 --- a/src/auth/Hooks/use-user-info.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useMsal } from "@azure/msal-react"; - -interface UserInfo { - name?: string; - email?: string; - oid?: string; - tenantId?: string; - rawClaims?: Record; -} - -export const useUserInfo = (): UserInfo => { - const { instance } = useMsal(); - const account = instance.getActiveAccount(); - - if (!account) return {}; - - const idTokenClaims = account.idTokenClaims as { - name?: string; - preferred_username?: string; - email?: string; - oid?: string; - tid?: string; - [key: string]: any; - }; - - return { - name: idTokenClaims?.name, - email: idTokenClaims?.email || idTokenClaims?.preferred_username, - oid: idTokenClaims?.oid, - tenantId: idTokenClaims?.tid, - rawClaims: idTokenClaims, - }; -}; diff --git a/src/auth/Hooks/use-user-prompts.ts b/src/auth/Hooks/use-user-prompts.ts deleted file mode 100644 index 6aa76c4..0000000 --- a/src/auth/Hooks/use-user-prompts.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useEffect, useState } from 'react'; -import { authorizedGet } from '../../api'; -import { useAuthToken } from './use-auth-token'; - -type Prompt = { - id: string; - user_id: string; - prompt_title: string; - workflow_manager_plan: string; - result: string; - created_at: string; - user_prompt: string; -}; - -export const useUserPrompts = () => { - const [prompts, setPrompts] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const { getToken } = useAuthToken(); - - const fetchPrompts = async () => { - setLoading(true); - setError(null); - try { - const response = await authorizedGet("/api/user/prompts", getToken); - setPrompts(response.data); - } catch (err) { - setError("Failed to fetch prompts"); - console.error("Error fetching prompts:", err); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - fetchPrompts(); - }, []); - - return { prompts, loading, error, refetch: fetchPrompts }; -}; \ No newline at end of file diff --git a/src/auth/ProtectedRoute.tsx b/src/auth/ProtectedRoute.tsx index 51923a8..76c6951 100644 --- a/src/auth/ProtectedRoute.tsx +++ b/src/auth/ProtectedRoute.tsx @@ -14,24 +14,59 @@ export const ProtectedRoute = ({ const { accounts } = useMsal(); const location = useLocation(); const [isChecking, setIsChecking] = useState(true); + const [isAuthenticated, setIsAuthenticated] = useState(false); useEffect(() => { - // Give a small delay to ensure MSAL is fully initialized + const checkAuthentication = async () => { + try { + // Check for MSAL authentication + const hasMsalAccount = accounts.length > 0; + + // Check for backend token + const authData = localStorage.getItem('auth_data'); + let hasBackendToken = false; + + if (authData) { + try { + const parsedAuthData = JSON.parse(authData); + hasBackendToken = !!parsedAuthData.accessToken; + } catch (e) { + console.error('Error parsing auth data:', e); + } + } + + // User is authenticated if either method is valid + setIsAuthenticated(hasMsalAccount || hasBackendToken); + + if (hasBackendToken) { + console.log('Authenticated with backend token'); + } else if (hasMsalAccount) { + console.log('Authenticated with MSAL'); + } + } catch (error) { + console.error('Error checking authentication:', error); + setIsAuthenticated(false); + } finally { + setIsChecking(false); + } + }; + + // Small delay to ensure MSAL is initialized const timer = setTimeout(() => { - setIsChecking(false); + checkAuthentication(); }, 100); return () => clearTimeout(timer); - }, []); + }, [accounts]); // If still checking, show loading if (isChecking) { return
Checking authentication...
; } - // Check if user is authenticated - if (accounts.length === 0) { - console.log("No accounts found, redirecting to login"); + // Check if user is authenticated through either method + if (!isAuthenticated) { + console.log("No valid authentication found, redirecting to login"); return ; } diff --git a/src/auth/Hooks/auth-provider.tsx b/src/auth/auth-provider.tsx similarity index 98% rename from src/auth/Hooks/auth-provider.tsx rename to src/auth/auth-provider.tsx index 0d078bc..fdd76aa 100644 --- a/src/auth/Hooks/auth-provider.tsx +++ b/src/auth/auth-provider.tsx @@ -4,7 +4,7 @@ import { PublicClientApplication, InteractionStatus } from "@azure/msal-browser"; - import { msalConfig } from "../auth-config"; + import { msalConfig } from "./auth-config"; import { MsalProvider } from "@azure/msal-react"; import { ReactNode, useEffect, useState } from "react"; diff --git a/src/components/DashboardConfig/DashboardConfigAgents.module.css b/src/components/DashboardConfig/DashboardConfigAgents.module.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardConfig/DashboardConfigAgents.tsx b/src/components/DashboardConfig/DashboardConfigAgents.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardConfig/DashboardConfigDocument.module.css b/src/components/DashboardConfig/DashboardConfigDocument.module.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardConfig/DashboardConfigDocument.tsx b/src/components/DashboardConfig/DashboardConfigDocument.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardConfig/DashboardConfigSettings.module.css b/src/components/DashboardConfig/DashboardConfigSettings.module.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardConfig/DashboardConfigSettings.tsx b/src/components/DashboardConfig/DashboardConfigSettings.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/DashboardPrompts/DashboardPrompt.module.css b/src/components/DashboardPrompts/DashboardPrompt.module.css deleted file mode 100644 index 9734621..0000000 --- a/src/components/DashboardPrompts/DashboardPrompt.module.css +++ /dev/null @@ -1,51 +0,0 @@ -.dashboard_prompt { - display: flex; - padding: 20px; - flex-direction: column; - align-self: stretch; - border-radius: 30px; - border: 1px solid var(--f-1-f-1-f-1, #F1F1F1); - background: var(--Grayscale-True-White, #FFF); - position: relative; - box-shadow: 0px 2px 6px 0px rgba(194, 194, 194, 0.10); - height: 100%; -} - -.prompt_button_div { - display: flex; - align-self: stretch; - gap: 30px; -} - -.prompt_button { - text-align: center; - font-size: 14px; - font-style: normal; - font-weight: 500; - line-height: normal; - border: none; - background: none; - outline: none; - color: var(--Grayscale-Black, #24262B); -} - -.prompt_button_inactive { - opacity: 50%; -} - -.horizontalLine { - width: 100%; - background-color: black; - height: 2px; - margin-top: 19px; -} - -.horizontalLineLight { - width: calc(100%); - background-color: #F1F1F1; - height: 2px; - margin-top: 39px; - margin-left: -20px; - position: absolute; -} - diff --git a/src/components/DashboardPrompts/DashboardPrompt.tsx b/src/components/DashboardPrompts/DashboardPrompt.tsx deleted file mode 100644 index 414131b..0000000 --- a/src/components/DashboardPrompts/DashboardPrompt.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { useSearchParams } from "react-router-dom"; - -import DashboardPromptArea from './DashboardPromptArea/DashboardPromptArea'; -import DashboardPromptSet from './DashboardPromptSet/DashboardPromptSet'; - -import styles from './DashboardPrompt.module.css'; - -const DashboardPrompt: React.FC = () => { - const [activeTab, setActiveTab] = useState("Prompt Area"); - const [searchParams] = useSearchParams(); - - useEffect(() => { - // If there's an expandedPrompt parameter, switch to the Prompt Set tab - const expandedPrompt = searchParams.get('expandedPrompt'); - const promptId = searchParams.get('promptId'); - - if (expandedPrompt) { - setActiveTab("Prompt Set"); - } else if (promptId) { - setActiveTab("Prompt Area"); - } - }, [searchParams]); - - return ( -
-
- {["Prompt Area", "Prompt Set"].map((tab) => ( -
- - {activeTab === tab &&
} -
- ))} -
-
-
- {activeTab === "Prompt Area" ? : } -
-
- ) -} - -export default DashboardPrompt; \ No newline at end of file diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.module.css b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.module.css deleted file mode 100644 index edd64e3..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.module.css +++ /dev/null @@ -1,37 +0,0 @@ -.promptArea { - display: flex; - flex-direction: column; - height: 100%; - position: relative; -} - -.cancelContainer { - position: absolute; - top: 10px; - right: 10px; - z-index: 10; -} - -.cancelButton { - display: flex; - align-items: center; - gap: 4px; - padding: 8px 16px; - background: white; - border: 1px solid #ddd; - border-radius: 20px; - color: #666; - font-size: 14px; - cursor: pointer; - transition: all 0.2s; - font-family: Arial, Helvetica, sans-serif; -} - -.cancelButton:hover { - background-color: #f5f5f5; - border-color: #ccc; -} - -.cancelIcon { - font-size: 16px; -} \ No newline at end of file diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.tsx b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.tsx deleted file mode 100644 index 5e9f666..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptArea.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useState, useRef } from "react"; -import { useSearchParams, useNavigate } from "react-router-dom"; -import DashboardPromptAreaMessage from './DashboardPromptAreaMessage'; -import DashboardPromptAreaChat from './DashboardPromptAreaChat'; -import { useAuthToken } from "../../../auth/Hooks/use-auth-token"; -import { authorizedGet } from "../../../api"; -import styles from './DashboardPromptArea.module.css'; -import { IoClose } from 'react-icons/io5'; - -function DashboardPromptArea() { - const [messages, setMessages] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [searchParams] = useSearchParams(); - const navigate = useNavigate(); - const messagesEndRef = useRef(null); - const { getToken } = useAuthToken(); - - const handleCancel = () => { - navigate('/dashboard'); - }; - - const fetchMessages = async () => { - setLoading(true); - setError(null); - try { - const response = await authorizedGet("/api/messages/", getToken); - setMessages(response.data); - } catch (err) { - console.error("Error fetching messages:", err); - setError("Failed to fetch messages"); - } finally { - setLoading(false); - } - }; - - const addMessage = (newMessage: any) => { - setMessages((prevMessages) => [...prevMessages, newMessage]); - }; - - return ( -
- {searchParams.get('promptId') && ( -
- -
- )} - - -
-
- ); -} - -export default DashboardPromptArea; diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.module.css b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.module.css deleted file mode 100644 index 02cf78f..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.module.css +++ /dev/null @@ -1,29 +0,0 @@ -.messages_container { - display: flex; - flex-direction: column; - align-items: flex-start; /* Align messages to the left */ - width: 100%; - padding: 10px; - width: calc(100% - 40px); - height: calc(100% - 100px); - overflow-y: auto; - margin-bottom: 12px; - } - - -.message_item { - margin-bottom: 10px; - } - -.message_content { - display: inline-block; /* Ensures the div is only as wide as the message content */ - background-color: var(--Grayscale-Light-Gray, #F9F9F9); /* Background color for the message */ - padding: 9px 14px; - border-radius: 10px; /* Optional: for rounded corners */ - color: #000; - font-family: Arial, Helvetica, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - } \ No newline at end of file diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.tsx b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.tsx deleted file mode 100644 index a368519..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaChat.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useSearchParams } from "react-router-dom"; -import { useAuthToken } from "../../../auth/Hooks/use-auth-token"; -import styles from "./DashboardPromptAreaChat.module.css"; - -function DashboardPromptAreaChat({ messages: propMessages, loading: propLoading, error: propError }: any) { - const [messages, setMessages] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - const [searchParams] = useSearchParams(); - const { getToken } = useAuthToken(); - - const promptId = searchParams.get('promptId'); - const promptText = searchParams.get('promptText'); - - useEffect(() => { - const fetchResponse = async () => { - if (promptId && promptText) { - setLoading(true); - setError(null); - - // Initialize with the user's prompt - setMessages([{ - id: promptId, - content: promptText, - created_at: new Date().toISOString(), - type: 'user' - }]); - - try { - const token = await getToken(); - if (!token) { - throw new Error('No authentication token available'); - } - - console.log('Making request with token:', token); - - const response = await fetch( - `http://localhost:8000/api/prompts/?promptText=${encodeURIComponent(promptText)}`, - { - method: 'GET', - headers: { - 'Authorization': `Bearer ${token}`, - 'Accept': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', - } - } - ); - - console.log('Response status:', response.status); - console.log('Response headers:', Object.fromEntries(response.headers.entries())); - - if (!response.ok) { - const errorText = await response.text(); - console.error('Error response:', errorText); - throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`); - } - - const reader = response.body?.getReader(); - if (!reader) { - throw new Error('No reader available'); - } - - let fullContent = ''; - let buffer = ''; - - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - // Convert the Uint8Array to a string - const chunk = new TextDecoder().decode(value); - console.log('Received chunk:', chunk); - buffer += chunk; - - // Process complete SSE messages - const lines = buffer.split('\n\n'); - buffer = lines.pop() || ''; // Keep the last incomplete chunk in the buffer - - for (const line of lines) { - if (line.startsWith('data: ')) { - try { - const jsonStr = line.slice(6); // Remove 'data: ' prefix - console.log('Processing SSE data:', jsonStr); - const data = JSON.parse(jsonStr); - if (data.content) { - fullContent += data.content; - // Update the message with the accumulated content - setMessages(prevMessages => { - const lastMessage = prevMessages[prevMessages.length - 1]; - if (lastMessage && lastMessage.type === 'assistant') { - return [ - ...prevMessages.slice(0, -1), - { - ...lastMessage, - content: fullContent - } - ]; - } else { - return [ - ...prevMessages, - { - id: `${promptId}-response`, - content: fullContent, - created_at: new Date().toISOString(), - type: 'assistant' - } - ]; - } - }); - } - } catch (err) { - console.error('Error parsing SSE data:', err, 'Raw line:', line); - } - } - } - } - } catch (err) { - console.error('Error in fetchResponse:', err); - setError(err instanceof Error ? err.message : 'Failed to connect to server'); - } finally { - setLoading(false); - } - } else if (propMessages) { - // Use messages from props if no prompt is being processed - setMessages(propMessages); - setLoading(propLoading); - setError(propError); - } else { - setLoading(false); - } - }; - - fetchResponse(); - }, [promptId, promptText, propMessages, propLoading, propError]); - - return ( -
- {loading ? ( -
-

Loading messages...

-
- ) : error ? ( -
-

Error: {error}

-
- ) : ( - <> - {messages && messages.length > 0 ? ( -
- {messages.map((message: any) => ( -
-
- {message.content} -
-
- ))} -
- ) : ( -

No messages found.

- )} - - )} -
- ); -} - -export default DashboardPromptAreaChat; diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.module.css b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.module.css deleted file mode 100644 index fd514b7..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.messageField { - display: flex; - height: 70px; - width: 100%; - border-radius: 12px; - border: 1px solid var(--f-1-f-1-f-1, #F1F1F1); -} - -.inputField { - height: 70px; - padding: 15px 24px 0 15px; - width: calc(100% - 40px); - color: #000; - font-size: 12px; - font-style: normal; - font-weight: 400; - opacity: 0.4; - resize: none; - overflow: hidden; - font-family: Helvetica; - border: none; -} - -.inputButton { - height: 40px; - width: 40px; -} \ No newline at end of file diff --git a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.tsx b/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.tsx deleted file mode 100644 index 3370121..0000000 --- a/src/components/DashboardPrompts/DashboardPromptArea/DashboardPromptAreaMessage.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useState } from "react"; -import { useAuthToken } from "../../../auth/Hooks/use-auth-token"; -import api from "../../../api"; -import styles from "./DashboardPromptAreaMessage.module.css"; - -interface DashboardPromptAreaMessageProps { - addMessage: (newMessage: any) => void; -} - -function DashboardPromptAreaMessage({ addMessage }: DashboardPromptAreaMessageProps) { - const [message, setMessage] = useState(""); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(""); - - const { getToken } = useAuthToken(); - - const sendMessage = async () => { - if (!message.trim()) return; - - setLoading(true); - setError(""); - - try { - const token = await getToken(); - - const response = await api.post( - '/api/messages/create', - { content: message.trim() }, - { - headers: { - Authorization: `Bearer ${token}`, - }, - } - ); - - addMessage(response.data); - setMessage(""); - } catch (error: unknown) { - console.error("Error sending message:", error); - if (error instanceof Error) { - setError(`Error: ${error.message}`); - } else { - setError("An unexpected error occurred"); - } - } finally { - setLoading(false); - } - }; - - return ( -
-