diff --git a/.gitignore b/.gitignore index f09d4d638..719f5ba7d 100644 --- a/.gitignore +++ b/.gitignore @@ -483,4 +483,7 @@ webapp/node_modules/ webapp/public/.well-known* # Auto-generated solution file from Visual Studio -webapi/CopilotChatWebApi.sln \ No newline at end of file +webapi/CopilotChatWebApi.sln + +# Tesseract OCR language data files +*.traineddata \ No newline at end of file diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 6fc5f0f5b..c89bffefe 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -7,16 +7,12 @@ import * as React from 'react'; import { FC, useEffect } from 'react'; import { UserSettingsMenu } from './components/header/UserSettingsMenu'; import { PluginGallery } from './components/open-api-plugins/PluginGallery'; -import BackendProbe from './components/views/BackendProbe'; -import { ChatView } from './components/views/ChatView'; -import Loading from './components/views/Loading'; -import { Login } from './components/views/Login'; +import { BackendProbe, ChatView, Error, Loading, Login } from './components/views'; import { useChat } from './libs/hooks'; -import { AlertType } from './libs/models/AlertType'; import { useAppDispatch, useAppSelector } from './redux/app/hooks'; import { RootState } from './redux/app/store'; import { FeatureKeys } from './redux/features/app/AppState'; -import { addAlert, setActiveUserInfo, setServiceOptions } from './redux/features/app/appSlice'; +import { setActiveUserInfo, setServiceOptions } from './redux/features/app/appSlice'; import { semanticKernelDarkTheme, semanticKernelLightTheme } from './styles'; export const useClasses = makeStyles({ @@ -51,6 +47,8 @@ export const useClasses = makeStyles({ enum AppState { ProbeForBackend, + SettingUserInfo, + ErrorLoadingUserInfo, LoadingChats, Chat, SigningOut, @@ -70,21 +68,24 @@ const App: FC = () => { useEffect(() => { if (isAuthenticated) { - let isActiveUserInfoSet = activeUserInfo !== undefined; - if (!isActiveUserInfoSet) { - const account = instance.getActiveAccount(); - if (!account) { - dispatch(addAlert({ type: AlertType.Error, message: 'Unable to get active logged in account.' })); + if (appState === AppState.SettingUserInfo) { + if (activeUserInfo === undefined) { + const account = instance.getActiveAccount(); + if (!account) { + setAppState(AppState.ErrorLoadingUserInfo); + } else { + dispatch( + setActiveUserInfo({ + id: account.homeAccountId, + email: account.username, // username in an AccountInfo object is the email address + username: account.name ?? account.username, + }), + ); + setAppState(AppState.LoadingChats); + } } else { - dispatch( - setActiveUserInfo({ - id: account.homeAccountId, - email: account.username, // username in an AccountInfo object is the email address - username: account.name ?? account.username, - }), - ); + setAppState(AppState.LoadingChats); } - isActiveUserInfoSet = true; } if (appState === AppState.LoadingChats) { @@ -143,10 +144,16 @@ const App: FC = () => { { - setAppState(AppState.LoadingChats); + setAppState(AppState.SettingUserInfo); }} /> )} + {appState === AppState.SettingUserInfo && ( + + )} + {appState === AppState.ErrorLoadingUserInfo && ( + + )} {appState === AppState.LoadingChats && } {appState === AppState.Chat && } diff --git a/webapp/src/components/views/BackendProbe.tsx b/webapp/src/components/views/BackendProbe.tsx index 266963b86..f160ed859 100644 --- a/webapp/src/components/views/BackendProbe.tsx +++ b/webapp/src/components/views/BackendProbe.tsx @@ -2,13 +2,15 @@ import { Body1, Spinner, Title3 } from '@fluentui/react-components'; import { FC, useEffect } from 'react'; +import { useSharedClasses } from '../../styles'; interface IData { uri: string; onBackendFound: () => void; } -const BackendProbe: FC = ({ uri, onBackendFound }) => { +export const BackendProbe: FC = ({ uri, onBackendFound }) => { + const classes = useSharedClasses(); useEffect(() => { const timer = setInterval(() => { const requestUrl = new URL('healthz', uri); @@ -31,7 +33,7 @@ const BackendProbe: FC = ({ uri, onBackendFound }) => { }); return ( -
+
Looking for your backend @@ -45,5 +47,3 @@ const BackendProbe: FC = ({ uri, onBackendFound }) => {
); }; - -export default BackendProbe; diff --git a/webapp/src/components/views/Error.tsx b/webapp/src/components/views/Error.tsx new file mode 100644 index 000000000..13c53c629 --- /dev/null +++ b/webapp/src/components/views/Error.tsx @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. + +import { Subtitle2 } from '@fluentui/react-components'; +import { ErrorCircleRegular } from '@fluentui/react-icons'; +import { FC } from 'react'; +import { useSharedClasses } from '../../styles'; + +interface IErrorProps { + text: string; +} + +export const Error: FC = ({ text }) => { + const classes = useSharedClasses(); + return ( +
+ + {text} +
+ ); +}; diff --git a/webapp/src/components/views/Loading.tsx b/webapp/src/components/views/Loading.tsx index de36a3057..3a9640206 100644 --- a/webapp/src/components/views/Loading.tsx +++ b/webapp/src/components/views/Loading.tsx @@ -2,17 +2,17 @@ import { Spinner } from '@fluentui/react-components'; import { FC } from 'react'; +import { useSharedClasses } from '../../styles'; interface ILoadingProps { text: string; } -const Loading: FC = ({ text }) => { +export const Loading: FC = ({ text }) => { + const classes = useSharedClasses(); return ( -
+
); }; - -export default Loading; diff --git a/webapp/src/components/views/index.ts b/webapp/src/components/views/index.ts new file mode 100644 index 000000000..5fcc763a8 --- /dev/null +++ b/webapp/src/components/views/index.ts @@ -0,0 +1,6 @@ +export * from './BackendProbe'; +export * from './ChatView'; +export * from './Error'; +export * from './Loading'; +export * from './Login'; +export * from './MissingEnvVariablesError'; diff --git a/webapp/src/styles.tsx b/webapp/src/styles.tsx index 54e964334..e6374aad0 100644 --- a/webapp/src/styles.tsx +++ b/webapp/src/styles.tsx @@ -74,6 +74,17 @@ export const SharedStyles: Record = { }, }; +export const useSharedClasses = makeStyles({ + informativeView: { + display: 'flex', + flexDirection: 'column', + ...shorthands.padding('80px'), + alignItems: 'center', + ...shorthands.gap(tokens.spacingVerticalXL), + marginTop: tokens.spacingVerticalXXXL, + }, +}); + export const useDialogClasses = makeStyles({ root: { height: '515px',