Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Nov 26, 2024
1 parent e2f590e commit 11914a0
Show file tree
Hide file tree
Showing 22 changed files with 409 additions and 210 deletions.
6 changes: 3 additions & 3 deletions webapp/chat-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.7.1",
"@fortawesome/react-fontawesome": "^0.2.2",
"@reduxjs/toolkit": "^1.9.7",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
Expand All @@ -24,16 +26,14 @@
"@types/react-router-dom": "^5.3.3",
"@types/styled-components": "^5.1.34",
"eslint": "^8.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.8",
"typescript": "^4.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"lint": "eslint \"src/**/*.{ts,tsx,js,jsx}\" --fix",
"type-check": "tsc --noEmit"
},
"eslintConfig": {
Expand Down
7 changes: 3 additions & 4 deletions webapp/chat-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import {store} from './store';
import websocket from './services/websocket';
import {GlobalStyles} from './styles/GlobalStyles';
import ChatInterface from './components/ChatInterface';
import Header from "./components/Header";
import {setTheme} from "./store/slices/uiSlice";
import ThemeProvider from './themes/ThemeProvider';
import {Menu} from "./components/Menu/Menu";

const App: React.FC = () => {
const sessionId = websocket.getSessionId();
Expand All @@ -16,7 +15,7 @@ const App: React.FC = () => {
<ThemeProvider>
<GlobalStyles/>
<div className="App">
<Header onThemeChange={setTheme}/>
<Menu/>
<ChatInterface
sessionId={sessionId}
websocket={websocket}
Expand All @@ -28,4 +27,4 @@ const App: React.FC = () => {
);
};

export default App;
export default App;
30 changes: 17 additions & 13 deletions webapp/chat-app/src/components/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import {useWebSocket} from '../hooks/useWebSocket';
import {addMessage} from '../store/slices/messageSlice';
import MessageList from './MessageList';
import InputArea from './InputArea';
import Header from './Header';
import websocket from "@services/websocket";
import websocket from '@services/websocket';

interface ChatInterfaceProps {
sessionId?: string;
Expand All @@ -20,22 +19,29 @@ const ChatContainer = styled.div`
height: 100vh;
`;

const ChatInterface: React.FC<ChatInterfaceProps> = ({sessionId: propSessionId, websocket, isConnected}) => {
const ChatInterface: React.FC<ChatInterfaceProps> = ({
sessionId: propSessionId,
websocket,
isConnected,
}) => {
const [sessionId] = useState(() => propSessionId || window.location.hash.slice(1) || 'new');
const dispatch = useDispatch();
const ws = useWebSocket(sessionId);

useEffect(() => {
const handleMessage = (data: string) => {
const [id, version, content] = data.split(',');

dispatch(addMessage({
id,
content: content,
version,
type: id.startsWith('u') ? 'user' : 'response',
timestamp: Date.now()
}));
const timestamp = Date.now();

dispatch(
addMessage({
id: `${id}-${timestamp}`, // Make IDs more unique
content: content,
version,
type: id.startsWith('u') ? 'user' : 'response',
timestamp,
})
);
};

websocket.addMessageHandler(handleMessage);
Expand All @@ -44,8 +50,6 @@ const ChatInterface: React.FC<ChatInterfaceProps> = ({sessionId: propSessionId,

return (
<ChatContainer>
<Header onThemeChange={(theme) => {
}}/>
<MessageList/>
<InputArea onSendMessage={(msg) => ws.send(msg)}/>
</ChatContainer>
Expand Down
11 changes: 4 additions & 7 deletions webapp/chat-app/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import {ThemeName} from '../types';
const HeaderContainer = styled.header`
background-color: ${({theme}) => theme.colors.surface};
padding: 1rem;
border-bottom: 1px solid ${props => props.theme.colors.border};
border-bottom: 1px solid ${(props) => props.theme.colors.border};
`;

const ThemeSelect = styled.select`
padding: 0.5rem;
border-radius: ${props => props.theme.sizing.borderRadius.sm};
border: 1px solid ${props => props.theme.colors.border};
border-radius: ${(props) => props.theme.sizing.borderRadius.sm};
border: 1px solid ${(props) => props.theme.colors.border};
`;

interface HeaderProps {
Expand All @@ -24,10 +24,7 @@ const Header: React.FC<HeaderProps> = ({onThemeChange}) => {

return (
<HeaderContainer>
<ThemeSelect
value={currentTheme}
onChange={(e) => setTheme(e.target.value as any)}
>
<ThemeSelect value={currentTheme} onChange={(e) => setTheme(e.target.value as any)}>
<option value="main">Day</option>
<option value="night">Night</option>
<option value="forest">Forest</option>
Expand Down
8 changes: 4 additions & 4 deletions webapp/chat-app/src/components/InputArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import styled from 'styled-components';

const InputContainer = styled.div`
padding: 1rem;
background-color: ${props => props.theme.colors.surface};
border-top: 1px solid ${props => props.theme.colors.border};
background-color: ${(props) => props.theme.colors.surface};
border-top: 1px solid ${(props) => props.theme.colors.border};
`;

const TextArea = styled.textarea`
width: 100%;
padding: 0.5rem;
border-radius: ${props => props.theme.sizing.borderRadius.md};
border: 1px solid ${props => props.theme.colors.border};
border-radius: ${(props) => props.theme.sizing.borderRadius.md};
border: 1px solid ${(props) => props.theme.colors.border};
font-family: inherit;
resize: vertical;
`;
Expand Down
135 changes: 135 additions & 0 deletions webapp/chat-app/src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import React from 'react';
import styled from 'styled-components';
import {useDispatch} from 'react-redux';
import {useTheme} from '../../hooks/useTheme';
import {showModal} from '../../store/slices/uiSlice';
import {ThemeName} from '../../themes/themes';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCog, faHome, faSignInAlt, faSignOutAlt} from '@fortawesome/free-solid-svg-icons';
import {ThemeMenu} from "./ThemeMenu";

const MenuContainer = styled.div`
display: flex;
justify-content: space-between;
padding: ${({theme}) => theme.sizing.spacing.sm};
background-color: ${({theme}) => theme.colors.surface};
border-bottom: 1px solid ${({theme}) => theme.colors.border};
`;

const ToolbarLeft = styled.div`
display: flex;
gap: ${({theme}) => theme.sizing.spacing.md};
`;

const Dropdown = styled.div`
position: relative;
display: inline-block;
&:hover .dropdown-content {
display: block;
}
`;

const DropButton = styled.a`
color: ${({theme}) => theme.colors.text.primary};
padding: ${({theme}) => theme.sizing.spacing.sm};
text-decoration: none;
cursor: pointer;
&:hover {
background-color: ${({theme}) => theme.colors.primary};
color: white;
}
`;

const DropdownContent = styled.div`
display: none;
position: absolute;
background-color: ${({theme}) => theme.colors.surface};
min-width: 160px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
z-index: 1;
`;

const DropdownItem = styled.a`
color: ${({theme}) => theme.colors.text.primary};
padding: ${({theme}) => theme.sizing.spacing.sm};
text-decoration: none;
display: block;
cursor: pointer;
&:hover {
background-color: ${({theme}) => theme.colors.primary};
color: white;
}
`;

export const Menu: React.FC = () => {
const dispatch = useDispatch();
const [, setTheme] = useTheme();

const handleThemeChange = (theme: ThemeName) => {
setTheme(theme);
};

const handleModalOpen = (modalType: string) => {
dispatch(showModal(modalType));
};

return (
<MenuContainer>
<ToolbarLeft>
<DropButton href="/">
<FontAwesomeIcon icon={faHome}/> Home
</DropButton>

<Dropdown>
<DropButton>App</DropButton>
<DropdownContent>
<DropdownItem onClick={() => handleModalOpen('sessions')}>Session List</DropdownItem>
<DropdownItem href="">New</DropdownItem>
</DropdownContent>
</Dropdown>

<Dropdown>
<DropButton>
<FontAwesomeIcon icon={faCog}/> Session
</DropButton>
<DropdownContent>
<DropdownItem onClick={() => handleModalOpen('settings')}>Settings</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('files')}>Files</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('usage')}>Usage</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('threads')}>Threads</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('share')}>Share</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('cancel')}>Cancel</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('delete')}>Delete</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('verbose')}>Show Verbose</DropdownItem>
</DropdownContent>
</Dropdown>

<ThemeMenu/>

<Dropdown>
<DropButton>About</DropButton>
<DropdownContent>
<DropdownItem onClick={() => handleModalOpen('privacy')}>Privacy Policy</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('tos')}>Terms of Service</DropdownItem>
</DropdownContent>
</Dropdown>
</ToolbarLeft>

<Dropdown>
<DropButton>
<FontAwesomeIcon icon={faSignInAlt}/> Login
</DropButton>
<DropdownContent>
<DropdownItem onClick={() => handleModalOpen('user-settings')}>Settings</DropdownItem>
<DropdownItem onClick={() => handleModalOpen('user-usage')}>Usage</DropdownItem>
<DropdownItem>
<FontAwesomeIcon icon={faSignOutAlt}/> Logout
</DropdownItem>
</DropdownContent>
</Dropdown>
</MenuContainer>
);
};
80 changes: 80 additions & 0 deletions webapp/chat-app/src/components/Menu/ThemeMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import styled from 'styled-components';
import {useTheme} from '../../hooks/useTheme';
import {themes} from '../../themes/themes';

const ThemeMenuContainer = styled.div`
position: relative;
display: inline-block;
`;

const ThemeButton = styled.button`
padding: ${({theme}) => theme.sizing.spacing.sm};
color: ${({theme}) => theme.colors.text.primary};
background: ${({theme}) => theme.colors.surface};
border: 1px solid ${({theme}) => theme.colors.border};
border-radius: ${({theme}) => theme.sizing.borderRadius.sm};
&:hover {
background: ${({theme}) => theme.colors.primary};
color: ${({theme}) => theme.colors.background};
}
`;

const ThemeList = styled.div`
position: absolute;
top: 100%;
right: 0;
background: ${({theme}) => theme.colors.surface};
border: 1px solid ${({theme}) => theme.colors.border};
border-radius: ${({theme}) => theme.sizing.borderRadius.sm};
padding: ${({theme}) => theme.sizing.spacing.xs};
z-index: 10;
min-width: 150px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
`;

const ThemeOption = styled.button`
width: 100%;
padding: ${({theme}) => theme.sizing.spacing.sm};
text-align: left;
color: ${({theme}) => theme.colors.text.primary};
background: none;
border: none;
border-radius: ${({theme}) => theme.sizing.borderRadius.sm};
&:hover {
background: ${({theme}) => theme.colors.primary};
color: ${({theme}) => theme.colors.background};
}
`;

export const ThemeMenu: React.FC = () => {
const [currentTheme, setTheme] = useTheme();
const [isOpen, setIsOpen] = React.useState(false);

const handleThemeChange = (themeName: keyof typeof themes) => {
setTheme(themeName);
setIsOpen(false);
};

return (
<ThemeMenuContainer>
<ThemeButton onClick={() => setIsOpen(!isOpen)}>
Theme: {currentTheme}
</ThemeButton>
{isOpen && (
<ThemeList>
{Object.keys(themes).map((themeName) => (
<ThemeOption
key={themeName}
onClick={() => handleThemeChange(themeName as keyof typeof themes)}
>
{themeName}
</ThemeOption>
))}
</ThemeList>
)}
</ThemeMenuContainer>
);
};
1 change: 1 addition & 0 deletions webapp/chat-app/src/components/Menu/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {ThemeMenu} from './ThemeMenu';
6 changes: 3 additions & 3 deletions webapp/chat-app/src/components/MessageList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
import { useSelector } from 'react-redux';
import { RootState } from '../store';
import {useSelector} from 'react-redux';
import {RootState} from '../store';

const MessageListContainer = styled.div`
flex: 1;
Expand Down Expand Up @@ -36,7 +36,7 @@ const MessageList: React.FC = () => {
return (
<MessageListContainer>
{messages.map((message) => (
<MessageItem key={message.id} type={message.type}>
<MessageItem key={`${message.id}-${message.timestamp}`} type={message.type}>
{message.content}
</MessageItem>
))}
Expand Down
Loading

0 comments on commit 11914a0

Please sign in to comment.