diff --git a/app/frontend/index.html b/app/frontend/index.html
index a7aa7e9eff..a7b97445e2 100644
--- a/app/frontend/index.html
+++ b/app/frontend/index.html
@@ -3,8 +3,9 @@
onClick(value)}>
+
onClick(value)} style={{ backgroundColor: bgColor }}>
{text}
);
diff --git a/app/frontend/src/components/Example/ExampleList.tsx b/app/frontend/src/components/Example/ExampleList.tsx
index 49c35cbd2d..fb77cd1e33 100644
--- a/app/frontend/src/components/Example/ExampleList.tsx
+++ b/app/frontend/src/components/Example/ExampleList.tsx
@@ -3,7 +3,7 @@ import { Example } from "./Example";
import styles from "./Example.module.css";
const DEFAULT_EXAMPLES: string[] = [
- "What is included in my Northwind Health Plus plan that is not in standard?",
+ "What is included in my Northwind Health Plus plan?",
"What happens in a performance review?",
"What does a Product Manager do?"
];
@@ -20,11 +20,15 @@ interface Props {
}
export const ExampleList = ({ onExampleClicked, useGPT4V }: Props) => {
+ const backgroundColors = ["#4ec0ad", "#f36f4c", "#e3e0d1"];
+
return (
{(useGPT4V ? GPT4V_EXAMPLES : DEFAULT_EXAMPLES).map((question, i) => (
-
+
))}
diff --git a/app/frontend/src/components/LoginButton/LoginButton.module.css b/app/frontend/src/components/LoginButton/LoginButton.module.css
index f808ac94cf..a10eb4cc19 100644
--- a/app/frontend/src/components/LoginButton/LoginButton.module.css
+++ b/app/frontend/src/components/LoginButton/LoginButton.module.css
@@ -1,5 +1,20 @@
-.loginButton {
+/* .loginButton {
border-radius: 5px;
padding: 30px 30px;
font-weight: 100;
+} */
+
+.loginButton {
+ background-color: #ff6b4a;
+ color: white;
+ font-size: 1rem;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ margin-top: 20px;
+}
+
+.loginButton:hover {
+ background-color: #e05a3d;
}
diff --git a/app/frontend/src/components/LoginButton/LoginButton.tsx b/app/frontend/src/components/LoginButton/LoginButton.tsx
index 2f65e9089d..4c2cd72e27 100644
--- a/app/frontend/src/components/LoginButton/LoginButton.tsx
+++ b/app/frontend/src/components/LoginButton/LoginButton.tsx
@@ -5,7 +5,12 @@ import styles from "./LoginButton.module.css";
import { getRedirectUri, loginRequest } from "../../authConfig";
import { appServicesToken, appServicesLogout } from "../../authConfig";
-export const LoginButton = () => {
+interface LoginButtonProps {
+ onLogin?: () => void;
+ onLogout?: () => void;
+}
+
+export const LoginButton: React.FC
= ({ onLogin, onLogout }) => {
const { instance } = useMsal();
const activeAccount = instance.getActiveAccount();
const isLoggedIn = (activeAccount || appServicesToken) != null;
@@ -21,6 +26,10 @@ export const LoginButton = () => {
...loginRequest,
redirectUri: getRedirectUri()
})
+ .then(() => {
+ localStorage.setItem("isLoggedIn", "true");
+ onLogin && onLogin(); // Notify MainLayout about login
+ })
.catch(error => console.log(error));
};
const handleLogoutPopup = () => {
@@ -30,6 +39,10 @@ export const LoginButton = () => {
mainWindowRedirectUri: "/", // redirects the top level app after logout
account: instance.getActiveAccount()
})
+ .then(() => {
+ localStorage.removeItem("isLoggedIn");
+ onLogout && onLogout(); // Notify MainLayout about logout
+ })
.catch(error => console.log(error));
} else {
appServicesLogout();
@@ -37,10 +50,12 @@ export const LoginButton = () => {
};
const logoutText = `Logout\n${activeAccount?.username ?? appServicesToken?.user_claims?.preferred_username}`;
return (
-
+ //
+ {isLoggedIn ? logoutText : "Get Started"}
);
};
diff --git a/app/frontend/src/components/QuestionInput/QuestionInput.module.css b/app/frontend/src/components/QuestionInput/QuestionInput.module.css
index 419523e453..d70ea91c1a 100644
--- a/app/frontend/src/components/QuestionInput/QuestionInput.module.css
+++ b/app/frontend/src/components/QuestionInput/QuestionInput.module.css
@@ -1,21 +1,91 @@
-.questionInputContainer {
- border-radius: 8px;
+/* .questionInputContainer {
+ border-radius: 0.5rem;
box-shadow:
- 0px 8px 16px rgba(0, 0, 0, 0.14),
- 0px 0px 2px rgba(0, 0, 0, 0.12);
- height: 90px;
+ 0px 0.5rem 1rem rgba(0, 0, 0, 0.14),
+ 0px 0px 0.125rem rgba(0, 0, 0, 0.12);
width: 100%;
- padding: 15px;
+ padding: 0.8rem;
background: white;
}
.questionInputTextArea {
width: 100%;
- line-height: 40px;
+ line-height: 2.5rem;
}
.questionInputButtonsContainer {
display: flex;
flex-direction: column;
justify-content: flex-end;
+} */
+
+@media (min-width: 992px) {
+ .questionInputContainer {
+ height: 5.625rem;
+ }
+}
+/* Container wrapping the entire input field */
+.questionInputContainer {
+ width: 63%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 16px;
+ background-color: #f2f2f2; /* Light gray background */
+ position: fixed;
+ bottom: 0;
+
+}
+
+div > input{
+ background-color: #f2f2f2 !important;
+}
+
+/* Wrapper for input + send button */
+.inputWrapper {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ background-color: #f2f2f2; /* Slightly darker than outer container */
+ border-radius: 30px; /* Rounded edges */
+ padding: 5px 15px;
+ box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+/* Input field */
+.questionInputTextArea {
+ flex-grow: 1;
+ background: #f2f2f2;
+ border: none;
+ font-size: 16px;
+ font-family: 'Poppins', sans-serif;
+ color: #333;
+ padding: 12px;
+ border-radius: 25px;
+}
+
+/* Hide Fluent UI default border */
+.questionInputTextArea:focus {
+ outline: none;
+}
+
+/* Send Button */
+.sendButton {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 10px;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: background 0.2s ease-in-out;
+}
+
+.sendButton:hover {
+ background-color: #e0e0e0;
+}
+
+/* Send Icon */
+.sendIcon {
+ color: #3a3a3a; /* Dark gray */
+ font-size: 24px;
}
diff --git a/app/frontend/src/components/QuestionInput/QuestionInput.tsx b/app/frontend/src/components/QuestionInput/QuestionInput.tsx
index f0667e37f8..febe55b492 100644
--- a/app/frontend/src/components/QuestionInput/QuestionInput.tsx
+++ b/app/frontend/src/components/QuestionInput/QuestionInput.tsx
@@ -60,24 +60,45 @@ export const QuestionInput = ({ onSend, disabled, placeholder, clearOnSend, init
}
return (
+ //
+ //
+ //
+ //
+ // } disabled={sendQuestionDisabled} onClick={sendQuestion} />
+ //
+ //
+ // {showSpeechInput && }
+ //
+
+ {/* Styled Input Field */}
-
-
- } disabled={sendQuestionDisabled} onClick={sendQuestion} />
-
+ {/* Send Button */}
+
+
- {showSpeechInput &&
}
-
+
+
+
);
};
diff --git a/app/frontend/src/components/SplashScreen/SplashScreen.module.css b/app/frontend/src/components/SplashScreen/SplashScreen.module.css
new file mode 100644
index 0000000000..648bb9ed73
--- /dev/null
+++ b/app/frontend/src/components/SplashScreen/SplashScreen.module.css
@@ -0,0 +1,61 @@
+.splashScreen {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ width: 100vw; /* Ensure full width */
+ background-color: #f0f0f0;
+ overflow: hidden; /* Prevent scrollbars */
+}
+
+.imageContainer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+}
+
+.splashImage {
+ width: 100%;
+ height: 100%;
+ object-fit: cover; /* Cover the entire screen without distortion */
+ display: block;
+}
+
+.textOverlay {
+ position: absolute;
+ top: 50%;
+ left: 26%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ color: white;
+ width: 80%;
+}
+
+.title {
+ font-size: 2.5rem;
+ font-weight: 400;
+}
+
+.bold {
+ font-weight: 700;
+}
+
+.subtitle {
+ font-size: 1.2rem;
+ margin-top: 10px;
+}
+
+.getStartedButton {
+ background-color: #ff6b4a;
+ color: white;
+ font-size: 1rem;
+ padding: 12px 24px;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ margin-top: 20px;
+}
+
+.getStartedButton:hover {
+ background-color: #e05a3d;
+}
diff --git a/app/frontend/src/components/SplashScreen/SplashScreen.tsx b/app/frontend/src/components/SplashScreen/SplashScreen.tsx
new file mode 100644
index 0000000000..bbb306763b
--- /dev/null
+++ b/app/frontend/src/components/SplashScreen/SplashScreen.tsx
@@ -0,0 +1,25 @@
+import styles from "./SplashScreen.module.css";
+import { LoginButton } from "../../components/LoginButton";
+
+interface SplashScreenProps {
+ onLogin?: () => void;
+}
+
+export const SplashScreen: React.FC
= ({ onLogin }) => {
+ return (
+
+
+
+ {/* Text overlay */}
+
+
+
+
+
+ );
+};
diff --git a/app/frontend/src/components/SplashScreen/index.tsx b/app/frontend/src/components/SplashScreen/index.tsx
new file mode 100644
index 0000000000..101d34aac6
--- /dev/null
+++ b/app/frontend/src/components/SplashScreen/index.tsx
@@ -0,0 +1 @@
+export * from "./SplashScreen";
\ No newline at end of file
diff --git a/app/frontend/src/index.css b/app/frontend/src/index.css
index 44e9bf2eea..b2e86f52ca 100644
--- a/app/frontend/src/index.css
+++ b/app/frontend/src/index.css
@@ -1,5 +1,6 @@
* {
box-sizing: border-box;
+ font-family: 'Poppins', sans-serif;
}
html,
diff --git a/app/frontend/src/index.tsx b/app/frontend/src/index.tsx
index edd2f5eded..398f946220 100644
--- a/app/frontend/src/index.tsx
+++ b/app/frontend/src/index.tsx
@@ -4,46 +4,36 @@ import { createHashRouter, RouterProvider } from "react-router-dom";
import { initializeIcons } from "@fluentui/react";
import { MsalProvider } from "@azure/msal-react";
import { PublicClientApplication, EventType, AccountInfo } from "@azure/msal-browser";
-import { msalConfig, useLogin } from "./authConfig";
+import { msalConfig } from "./authConfig"; // Remove useLogin here
import "./index.css";
-
import Layout from "./pages/layout/Layout";
import Chat from "./pages/chat/Chat";
-var layout;
-if (useLogin) {
- var msalInstance = new PublicClientApplication(msalConfig);
-
- // Default to using the first account if no account is active on page load
- if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
- // Account selection logic is app dependent. Adjust as needed for different use cases.
- msalInstance.setActiveAccount(msalInstance.getActiveAccount());
- }
+// ✅ Always create MSAL instance
+const msalInstance = new PublicClientApplication(msalConfig);
- // Listen for sign-in event and set active account
- msalInstance.addEventCallback(event => {
- if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
- const account = event.payload as AccountInfo;
- msalInstance.setActiveAccount(account);
- }
- });
-
- layout = (
-
-
-
- );
-} else {
- layout = ;
+// ✅ Select the first available account if no active account exists
+const accounts = msalInstance.getAllAccounts();
+if (!msalInstance.getActiveAccount() && accounts.length > 0) {
+ msalInstance.setActiveAccount(accounts[0]);
}
+// ✅ Listen for sign-in events and set active account
+msalInstance.addEventCallback(event => {
+ if (event.eventType === EventType.LOGIN_SUCCESS && event.payload) {
+ const account = event.payload as AccountInfo;
+ msalInstance.setActiveAccount(account);
+ }
+});
+
+// ✅ Wrap the entire app in MsalProvider
initializeIcons();
const router = createHashRouter([
{
path: "/",
- element: layout,
+ element: , // Layout will handle authentication logic
children: [
{
index: true,
@@ -63,6 +53,8 @@ const router = createHashRouter([
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
-
+
+
+
);
diff --git a/app/frontend/src/pages/chat/Chat.module.css b/app/frontend/src/pages/chat/Chat.module.css
index 4aaaa1ecb2..5719e6e7a8 100644
--- a/app/frontend/src/pages/chat/Chat.module.css
+++ b/app/frontend/src/pages/chat/Chat.module.css
@@ -29,10 +29,11 @@
}
.chatEmptyStateTitle {
- font-size: 4rem;
- font-weight: 600;
+ font-size: 40px;
+ font-weight: 300;
+ line-height: '45px';
margin-top: 0;
- margin-bottom: 30px;
+ margin-bottom: 1.875rem;
}
.chatEmptyStateSubtitle {
diff --git a/app/frontend/src/pages/chat/Chat.tsx b/app/frontend/src/pages/chat/Chat.tsx
index abd8725e04..1d30bef709 100644
--- a/app/frontend/src/pages/chat/Chat.tsx
+++ b/app/frontend/src/pages/chat/Chat.tsx
@@ -331,6 +331,10 @@ const Chat = () => {
return (
+
+
+
+
{showUserUpload &&
}
setIsConfigPanelOpen(!isConfigPanelOpen)} />
@@ -339,9 +343,9 @@ const Chat = () => {
{!lastQuestionRef.current ? (
-
-
Chat with your data
- Ask anything or try an example
+ {/* */}
+ What can i help you with?
+ {/* Ask anything or try an example */}
) : (
@@ -413,7 +417,7 @@ const Chat = () => {
makeApiRequest(question)}
showSpeechInput={showSpeechInput}
diff --git a/app/frontend/src/pages/layout/Layout.module.css b/app/frontend/src/pages/layout/Layout.module.css
index ebb860e216..4adad6b3c2 100644
--- a/app/frontend/src/pages/layout/Layout.module.css
+++ b/app/frontend/src/pages/layout/Layout.module.css
@@ -1,7 +1,7 @@
.layout {
display: flex;
- flex-direction: column;
- height: 100%;
+ /* flex-direction: column; */
+ height: 100vh;
}
.header {
@@ -76,3 +76,156 @@
.githubLogo {
height: 20px;
}
+/* Sidebar */
+.sidebar {
+ width: 200px;
+ background-color: #ffffff;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ position: fixed;
+ height: 100%;
+ padding-top: 20px;
+ border-right: 1px solid #e0e0e0;
+}
+
+/* Logo Container */
+.logoContainer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ padding-bottom: 10px;
+}
+
+.logo {
+ width: 80%;
+ height: auto;
+}
+
+
+/* Powered By Text */
+.poweredBy {
+ font-size: 12px;
+ color: #888;
+ margin-top: 10px;
+ margin-left: 10px;
+}
+
+.poweredBy a {
+ text-decoration: none !important; /* Removes underline for any linked text */
+ color: inherit; /* Keeps default color */
+}
+
+
+/* Main Content */
+.mainContent {
+ margin-left: 200px; /* Offset for sidebar */
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+/* Header */
+.header {
+ background-color: #ffffff;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 15px 20px;
+ border-bottom: 1px solid #e0e0e0;
+}
+
+.headerTitle {
+ font-size: 20px;
+ font-weight: bold;
+ color: #053c23;
+ /* color: #1abc9c; */
+}
+
+/* Info Icon */
+.infoIcon {
+ font-size: 24px;
+ color: #1abc9c;
+ cursor: pointer;
+ margin-top: 10px;
+}
+
+/* Page Content */
+.pageContent {
+ flex-grow: 1;
+ padding: 20px;
+ overflow-y: auto;
+}
+
+
+.landingPage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ background: url("/image.png") no-repeat center center;
+ background-size: cover;
+ color: white;
+ text-align: center;
+ flex-direction: column;
+ position: relative;
+}
+
+.content {
+ z-index: 2;
+ text-align: center;
+ max-width: 500px;
+}
+
+.boldText {
+ font-weight: bold;
+}
+
+.subText {
+ font-size: 1.2rem;
+ margin-bottom: 20px;
+}
+
+.getStartedButton {
+ background-color: #ff7f50;
+ color: white;
+ padding: 12px 24px;
+ font-size: 1.1rem;
+ border-radius: 8px;
+ cursor: pointer;
+ border: none;
+ transition: 0.3s ease;
+}
+
+.getStartedButton:hover {
+ background-color: #ff6333;
+}
+
+.overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5); /* Dark overlay for better readability */
+ z-index: 1;
+}
+
+.logoutContainer
+{
+ display:flex;
+}
+
+.logoutButton
+{
+ margin-top: 1px;
+ margin-left: 10px;
+ background-color: #f36f4c;
+ color: white;
+ border: 5px;
+ border-radius: 5px;
+ width: 10vh;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/app/frontend/src/pages/layout/Layout.tsx b/app/frontend/src/pages/layout/Layout.tsx
index 157a6ffd3a..a757f02928 100644
--- a/app/frontend/src/pages/layout/Layout.tsx
+++ b/app/frontend/src/pages/layout/Layout.tsx
@@ -1,54 +1,101 @@
-import { Outlet, NavLink, Link } from "react-router-dom";
+import { Outlet, Link, useNavigate } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { useMsal } from "@azure/msal-react";
+import { EventType } from "@azure/msal-browser"; // ✅ Import correct event type
+import styles from "./Layout.module.css";
+import { LoginButton } from "../../components/LoginButton";
+import { SplashScreen } from "../../components/SplashScreen";
+import { Info24Regular } from "@fluentui/react-icons"; // Fluent UI info icon
+import { appServicesToken, appServicesLogout } from "../../authConfig";
-import github from "../../assets/github.svg";
+const Layout = () => {
+ const { instance, accounts } = useMsal();
+ const navigate = useNavigate();
+ const [isLoggedIn, setIsLoggedIn] = useState(null);
-import styles from "./Layout.module.css";
+ useEffect(() => {
+ const checkAuthStatus = () => {
+ const activeAccount = instance.getActiveAccount();
+ if (activeAccount || appServicesToken) {
+ setIsLoggedIn(true);
+ } else {
+ setIsLoggedIn(false);
+ }
+ };
-import { useLogin } from "../../authConfig";
+ checkAuthStatus();
+
+ // ✅ Use MSAL event listener to detect login state change
+ const accountListener = instance.addEventCallback((event) => {
+ if (event.eventType === EventType.LOGIN_SUCCESS) {
+ setIsLoggedIn(true);
+ navigate("/", { replace: true }); // ✅ Navigate only after login
+ } else if (event.eventType === EventType.LOGOUT_SUCCESS) {
+ setIsLoggedIn(false);
+ navigate("/"); // ✅ Go back to splash screen on logout
+ }
+ });
-import { LoginButton } from "../../components/LoginButton";
+ return () => {
+ if (accountListener) {
+ instance.removeEventCallback(accountListener);
+ }
+ };
+ }, [instance, accounts, navigate]);
-const Layout = () => {
- return (
+ const handleLogout = () => {
+ const activeAccount = instance.getActiveAccount();
+ if (activeAccount) {
+ instance.logoutPopup({
+ mainWindowRedirectUri: "/", // Redirects app after logout
+ account: activeAccount,
+ });
+ } else {
+ appServicesLogout();
+ }
+ setIsLoggedIn(false);
+ navigate("/"); // ✅ Ensure user is redirected to login after logout
+ };
+
+ if (isLoggedIn === null) {
+ return ; // ✅ Shows splash screen initially
+ }
+
+ return isLoggedIn ? (