Skip to content

Commit

Permalink
makes new UX the default UX, share redemptions updated to point to ne…
Browse files Browse the repository at this point in the history
…w path, old dashboard avail through menu (#208)
  • Loading branch information
bkrabach authored Nov 5, 2024
1 parent f03adac commit b80a0c6
Show file tree
Hide file tree
Showing 12 changed files with 121 additions and 59 deletions.
2 changes: 1 addition & 1 deletion workbench-app/src/Constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export const Constants = {
app: {
name: 'Semantic Workbench',
conversationRedirectPath: '/frontdoor',
conversationRedirectPath: '',
defaultTheme: 'light',
defaultBrand: 'local',
autoScrollThreshold: 100,
Expand Down
32 changes: 28 additions & 4 deletions workbench-app/src/components/App/ExperimentalNotice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ import {
Image,
Link,
MessageBar,
MessageBarActions,
MessageBarBody,
MessageBarTitle,
makeStyles,
shorthands,
tokens,
} from '@fluentui/react-components';
import { DismissRegular } from '@fluentui/react-icons';
import React from 'react';
import { useAppDispatch, useAppSelector } from '../../redux/app/hooks';
import { setCompletedFirstRun } from '../../redux/features/app/appSlice';
import { setCompletedFirstRun, setHideExperimentalNotice } from '../../redux/features/app/appSlice';

const useClasses = makeStyles({
surface: {
Expand Down Expand Up @@ -57,9 +59,16 @@ const useClasses = makeStyles({
},
});

export const ExperimentalNotice: React.FC = () => {
interface ExperimentalNoticeProps {
className?: string;
containerAction?: React.ReactElement;
actions?: React.ReactElement | React.ReactElement[];
}

export const ExperimentalNotice: React.FC<ExperimentalNoticeProps> = (props) => {
const { className, containerAction, actions } = props;
const classes = useClasses();
const completedFirstRun = useAppSelector((state) => state.app.completedFirstRun);
const { completedFirstRun, hideExperimentalNotice } = useAppSelector((state) => state.app);
const dispatch = useAppDispatch();
const [showDialog, setShowDialog] = React.useState(!completedFirstRun?.experimental);
const [currentIndex, setCurrentIndex] = React.useState(0);
Expand Down Expand Up @@ -137,6 +146,18 @@ export const ExperimentalNotice: React.FC = () => {
},
];

const defaultContainerAction = (
<Button
appearance="transparent"
onClick={() => dispatch(setHideExperimentalNotice(true))}
icon={<DismissRegular />}
/>
);

if (hideExperimentalNotice) {
return null;
}

return (
<Dialog
open={showDialog}
Expand All @@ -150,12 +171,15 @@ export const ExperimentalNotice: React.FC = () => {
}}
>
<DialogTrigger>
<MessageBar intent="warning" layout="multiline">
<MessageBar className={className} intent="warning" layout="multiline">
<MessageBarBody>
<MessageBarTitle>Experimental App Reminder:</MessageBarTitle>
features <em>will</em> break, data <em>will</em> be lost, data <em>is not</em> secure. &nbsp;
<Link>[details]</Link>
</MessageBarBody>
<MessageBarActions containerAction={containerAction ?? defaultContainerAction}>
{actions}
</MessageBarActions>
</MessageBar>
</DialogTrigger>
<DialogSurface className={classes.surface}>
Expand Down
10 changes: 9 additions & 1 deletion workbench-app/src/components/FrontDoor/Chat/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
useGetConversationParticipantsQuery,
useGetConversationQuery,
} from '../../../services/workbench';
import { ExperimentalNotice } from '../../App/ExperimentalNotice';
import { Loading } from '../../App/Loading';
import { ConversationShare } from '../../Conversations/ConversationShare';
import { InteractHistory } from '../../Conversations/InteractHistory';
Expand Down Expand Up @@ -59,12 +60,16 @@ const useClasses = makeStyles({
flex: '0 0 auto',
},

'&.center': {
overflow: 'visible',
},

'&.after': {
right: 0,
flex: '0 0 auto',
},
},
errorList: {
centerContent: {
position: 'absolute',
top: 0,
left: tokens.spacingHorizontalM,
Expand Down Expand Up @@ -231,6 +236,9 @@ export const Chat: React.FC<ChatProps> = (props) => {
<div className={classes.root}>
<div className={classes.header}>
<div className={mergeClasses(classes.headerControls, 'before')}>{headerBefore}</div>
<div className={mergeClasses(classes.headerControls, 'center')}>
<ExperimentalNotice className={classes.centerContent} />
</div>
<div className={mergeClasses(classes.headerControls, 'after')}>
{otherParticipants.length === 1 && (
<ParticipantAvatarGroup participants={otherParticipants} layout="spread" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
CheckboxUncheckedRegular,
DismissRegular,
FilterRegular,
MailReadRegular,
MailUnreadRegular,
PinOffRegular,
PinRegular,
PlugDisconnectedRegular,
Expand Down Expand Up @@ -71,6 +73,10 @@ const useClasses = makeStyles({
flexDirection: 'row',
alignItems: 'center',
},
bulkActionsLabel: {
marginLeft: tokens.spacingHorizontalM,
color: tokens.colorNeutralForeground2,
},
toolbarTextButton: {
minWidth: 'auto',
paddingLeft: tokens.spacingHorizontalXS,
Expand Down Expand Up @@ -428,22 +434,23 @@ export const ConversationListOptions: React.FC<ConversationListOptionsProps> = (
() => (
<Overflow padding={90}>
<Toolbar size="small">
<Button
className={classes.toolbarTextButton}
appearance="transparent"
disabled={!enableBulkActions.read}
onClick={handleMarkAllAsReadForSelected}
>
Read
</Button>
<Button
className={classes.toolbarTextButton}
appearance="transparent"
disabled={!enableBulkActions.unread}
onClick={handleMarkAsUnreadForSelected}
>
Unread
</Button>
<Tooltip content="Mark selected conversations as read" relationship="label">
<Button
className={classes.toolbarTextButton}
appearance="transparent"
icon={<MailReadRegular />}
disabled={!enableBulkActions.read}
onClick={handleMarkAllAsReadForSelected}
/>
</Tooltip>
<Tooltip content="Mark selected conversations as unread" relationship="label">
<Button
appearance="transparent"
icon={<MailUnreadRegular />}
disabled={!enableBulkActions.unread}
onClick={handleMarkAsUnreadForSelected}
/>
</Tooltip>
<Tooltip content="Remove selected conversations" relationship="label">
<Button
// hide this until implemented
Expand Down Expand Up @@ -538,6 +545,9 @@ export const ConversationListOptions: React.FC<ConversationListOptionsProps> = (
</div>
<div className={classes.bulkActions}>
{bulkSelectForActionsButton}
<Label className={classes.bulkActionsLabel} size="small">
Actions
</Label>
{bulkSelectForActionsToolbar}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export const SiteMenuButton: React.FC = () => {
<MenuItem
icon={<NavigationRegular />}
onClick={() => {
navigate('/');
navigate('/dashboard');
}}
>
Dashboard
Expand Down
2 changes: 2 additions & 0 deletions workbench-app/src/components/FrontDoor/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from 'react';
import { useConversationUtility } from '../../libs/useConversationUtility';
import { useCreateConversation } from '../../libs/useCreateConversation';
import { useAppSelector } from '../../redux/app/hooks';
import { ExperimentalNotice } from '../App/ExperimentalNotice';
import { ConversationsImport } from '../Conversations/ConversationsImport';
import { Chat } from './Chat/Chat';
import { NewConversationForm } from './Controls/NewConversationForm';
Expand Down Expand Up @@ -112,6 +113,7 @@ export const MainContent: React.FC<MainContentProps> = (props) => {
<>
<div className={classes.header}>
{headerBefore}
<ExperimentalNotice />
{headerAfter}
</div>
<div className={classes.body}>
Expand Down
8 changes: 6 additions & 2 deletions workbench-app/src/libs/useConversationUtility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ export const useConversationUtility = () => {
// region Navigation

const navigateToConversation = React.useCallback(
(conversationId?: string) => {
navigate([Constants.app.conversationRedirectPath, conversationId].join('/'));
(conversationId?: string, hash?: string) => {
let path = conversationId ? [Constants.app.conversationRedirectPath, conversationId].join('/') : '';
if (hash) {
path += `#${hash}`;
}
navigate(path);
},
[navigate],
);
Expand Down
8 changes: 6 additions & 2 deletions workbench-app/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ const authenticatedRouter = createBrowserRouter([
children: [
{
index: true,
element: <Dashboard />,
element: <FrontDoor />,
},
{
path: '/frontdoor/:conversationId?',
path: '/:conversationId?',
element: <FrontDoor />,
},
{
path: '/dashboard',
element: <Dashboard />,
},
{
path: '/settings',
element: <Settings />,
Expand Down
1 change: 1 addition & 0 deletions workbench-app/src/redux/features/app/AppState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface AppState {
experimental: boolean;
workflow: boolean;
};
hideExperimentalNotice: boolean;
chatWidthPercent: number;
isDraggingOverBody?: boolean;
activeConversationId?: string;
Expand Down
8 changes: 8 additions & 0 deletions workbench-app/src/redux/features/app/appSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const localStorageKey = {
completedFirstRunApp: 'completedFirstRun:app',
completedFirstRunExperimental: 'completedFirstRun:experimental',
completedFirstRunWorkflow: 'completedFirstRun:workflow',
hideExperimentalNotice: 'hideExperimentalNotice',
};

const initialState: AppState = {
Expand All @@ -27,6 +28,8 @@ const initialState: AppState = {
AppStorage.getInstance().loadObject<boolean>(localStorageKey.completedFirstRunExperimental) ?? false,
workflow: AppStorage.getInstance().loadObject<boolean>(localStorageKey.completedFirstRunWorkflow) ?? false,
},
hideExperimentalNotice:
AppStorage.getInstance().loadObject<boolean>(localStorageKey.hideExperimentalNotice) ?? false,
};

export const appSlice = createSlice({
Expand Down Expand Up @@ -87,6 +90,10 @@ export const appSlice = createSlice({
state.completedFirstRun.workflow = action.payload.workflow;
}
},
setHideExperimentalNotice: (state: AppState, action: PayloadAction<boolean>) => {
AppStorage.getInstance().saveObject(localStorageKey.hideExperimentalNotice, action.payload);
state.hideExperimentalNotice = action.payload;
},
setActiveConversationId: (state: AppState, action: PayloadAction<string | undefined>) => {
if (action.payload === state.activeConversationId) {
return;
Expand All @@ -109,6 +116,7 @@ export const {
clearErrors,
setChatWidthPercent,
setCompletedFirstRun,
setHideExperimentalNotice,
setActiveConversationId,
} = appSlice.actions;

Expand Down
50 changes: 23 additions & 27 deletions workbench-app/src/routes/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.

import { makeStyles, MessageBar, MessageBarBody, MessageBarTitle, tokens } from '@fluentui/react-components';
import { makeStyles, tokens } from '@fluentui/react-components';
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { AppMenu } from '../components/App/AppMenu';
import { useNavigate } from 'react-router-dom';
import { AppView } from '../components/App/AppView';
import { ExperimentalNotice } from '../components/App/ExperimentalNotice';
import { Loading } from '../components/App/Loading';
import { MyAssistants } from '../components/Assistants/MyAssistants';
import { MyConversations } from '../components/Conversations/MyConversations';
import { MyWorkflows } from '../components/Workflows/MyWorkflows';
import { Constants } from '../Constants';
import { useSiteUtility } from '../libs/useSiteUtility';
import { Conversation } from '../models/Conversation';
import { useAppSelector } from '../redux/app/hooks';
Expand All @@ -21,13 +19,18 @@ const useClasses = makeStyles({
root: {
display: 'flex',
flexDirection: 'column',
gap: tokens.spacingVerticalXXXL,
gap: tokens.spacingVerticalM,
},
messageBars: {
display: 'flex',
flexDirection: 'column',
gap: tokens.spacingVerticalS,
},
body: {
display: 'flex',
flexDirection: 'column',
gap: tokens.spacingVerticalXXXL,
},
});

export const Dashboard: React.FC = () => {
Expand Down Expand Up @@ -71,11 +74,9 @@ export const Dashboard: React.FC = () => {
[navigate],
);

const appMenuAction = <AppMenu />;

if (isLoadingAssistants || isLoadingConversations || isLoadingWorkflowDefinitions) {
return (
<AppView title="Dashboard" actions={{ items: [appMenuAction], replaceExisting: true }}>
<AppView title="Dashboard">
<Loading />
</AppView>
);
Expand All @@ -86,33 +87,28 @@ export const Dashboard: React.FC = () => {
conversations?.filter((conversation) => conversation.ownerId !== localUserStateId) || [];

return (
<AppView title="Dashboard" actions={{ items: [appMenuAction], replaceExisting: true }}>
<AppView title="Dashboard">
<div className={classes.root}>
<div className={classes.messageBars}>
<ExperimentalNotice />
<MessageBar intent="info" layout="multiline">
<MessageBarBody>
<MessageBarTitle>New Feature</MessageBarTitle>
Try out the new conversation-first UX. &nbsp;
<Link to={Constants.app.conversationRedirectPath}>[view]</Link>
</MessageBarBody>
</MessageBar>
</div>
<MyAssistants assistants={assistants} />
<MyConversations
conversations={myConversations}
participantId="me"
onCreate={handleConversationCreate}
/>
{conversationsSharedWithMe.length > 0 && (
<div className={classes.body}>
<MyAssistants assistants={assistants} />
<MyConversations
title="Conversations Shared with Me"
conversations={conversationsSharedWithMe}
conversations={myConversations}
participantId="me"
onCreate={handleConversationCreate}
/>
)}
<MyWorkflows workflowDefinitions={workflowDefinitions} />
{conversationsSharedWithMe.length > 0 && (
<MyConversations
title="Conversations Shared with Me"
conversations={conversationsSharedWithMe}
participantId="me"
onCreate={handleConversationCreate}
/>
)}
<MyWorkflows workflowDefinitions={workflowDefinitions} />
</div>
</div>
</AppView>
);
Expand Down
Loading

0 comments on commit b80a0c6

Please sign in to comment.