diff --git a/frontend/README.md b/frontend/README.md
index 7959ce4..10e1545 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -13,9 +13,9 @@ If you are developing a production application, we recommend updating the config
```js
export default tseslint.config([
- globalIgnores(['dist']),
+ globalIgnores(["dist"]),
{
- files: ['**/*.{ts,tsx}'],
+ files: ["**/*.{ts,tsx}"],
extends: [
// Other configs...
@@ -30,40 +30,40 @@ export default tseslint.config([
],
languageOptions: {
parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ project: ["./tsconfig.node.json", "./tsconfig.app.json"],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
-])
+]);
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
-import reactX from 'eslint-plugin-react-x'
-import reactDom from 'eslint-plugin-react-dom'
+import reactX from "eslint-plugin-react-x";
+import reactDom from "eslint-plugin-react-dom";
export default tseslint.config([
- globalIgnores(['dist']),
+ globalIgnores(["dist"]),
{
- files: ['**/*.{ts,tsx}'],
+ files: ["**/*.{ts,tsx}"],
extends: [
// Other configs...
// Enable lint rules for React
- reactX.configs['recommended-typescript'],
+ reactX.configs["recommended-typescript"],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
+ project: ["./tsconfig.node.json", "./tsconfig.app.json"],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
-])
+]);
```
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
index d94e7de..f461674 100644
--- a/frontend/eslint.config.js
+++ b/frontend/eslint.config.js
@@ -1,18 +1,18 @@
-import js from '@eslint/js'
-import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
-import tseslint from 'typescript-eslint'
-import { globalIgnores } from 'eslint/config'
+import js from "@eslint/js";
+import globals from "globals";
+import reactHooks from "eslint-plugin-react-hooks";
+import reactRefresh from "eslint-plugin-react-refresh";
+import tseslint from "typescript-eslint";
+import { globalIgnores } from "eslint/config";
export default tseslint.config([
- globalIgnores(['dist']),
+ globalIgnores(["dist"]),
{
- files: ['**/*.{ts,tsx}'],
+ files: ["**/*.{ts,tsx}"],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
- reactHooks.configs['recommended-latest'],
+ reactHooks.configs["recommended-latest"],
reactRefresh.configs.vite,
],
languageOptions: {
@@ -20,4 +20,4 @@ export default tseslint.config([
globals: globals.browser,
},
},
-])
+]);
diff --git a/frontend/src/App.module.css b/frontend/src/App.module.css
index 4b658f0..d7aae00 100644
--- a/frontend/src/App.module.css
+++ b/frontend/src/App.module.css
@@ -3,7 +3,7 @@
height: 100%;
max-width: 1200px;
max-height: 800px;
- background: #FFFFFD;
+ background: #fffffd;
display: flex;
border-radius: 12px;
overflow: hidden;
@@ -60,12 +60,14 @@ main {
margin-bottom: 0.75rem;
cursor: pointer;
border: 1px solid #e5e7eb;
- transition: background-color 0.2s, box-shadow 0.2s;
+ transition:
+ background-color 0.2s,
+ box-shadow 0.2s;
}
.historyItem:hover {
background-color: #f9fafb;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.historyItemContent {
@@ -94,4 +96,4 @@ main {
font-size: 0.875rem;
color: #6b7280;
margin-left: 1rem;
-}
\ No newline at end of file
+}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e2e1726..ad333e4 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,18 +1,27 @@
-import Chat from './components/Chat';
-import Sidebar from './components/Sidebar';
-import styles from './App.module.css';
-import { useState } from 'react';
+import Chat from "./components/Chat";
+import Sidebar from "./components/Sidebar";
+import styles from "./App.module.css";
+import { useState } from "react";
const saveSessionToHistory = () => {
- const history = JSON.parse(localStorage.getItem('apiconf_chat_history') || '[]');
- const sessionId = localStorage.getItem('apiconf_session_id');
-
+ const history = JSON.parse(
+ localStorage.getItem("apiconf_chat_history") || "[]",
+ );
+ const sessionId = localStorage.getItem("apiconf_session_id");
+
// Avoid adding duplicate or empty sessions
- if (sessionId && !history.some((item: { sessionId: string }) => item.sessionId === sessionId)) {
+ if (
+ sessionId &&
+ !history.some((item: { sessionId: string }) => item.sessionId === sessionId)
+ ) {
const timestamp = new Date().toLocaleString();
- const preview = localStorage.getItem(`session_preview_${sessionId}`) || 'New Chat';
+ const preview =
+ localStorage.getItem(`session_preview_${sessionId}`) || "New Chat";
history.unshift({ sessionId, timestamp, preview });
- localStorage.setItem('apiconf_chat_history', JSON.stringify(history.slice(0, 20))); // Increased limit
+ localStorage.setItem(
+ "apiconf_chat_history",
+ JSON.stringify(history.slice(0, 20)),
+ ); // Increased limit
}
};
@@ -20,7 +29,7 @@ const SettingsComponent = () =>
Settings view coming soon.
;
const App: React.FC = () => {
const [sidebarOpen, setSidebarOpen] = useState(false);
- const [view, setView] = useState<'chat' | 'settings'>('chat'); // Simplified view
+ const [view, setView] = useState<"chat" | "settings">("chat"); // Simplified view
const [resetSignal, setResetSignal] = useState(0);
const toggleSidebar = () => {
@@ -32,23 +41,24 @@ const App: React.FC = () => {
};
// Get active session ID from local storage to pass to sidebar
- const activeSessionId = localStorage.getItem('apiconf_session_id');
+ const activeSessionId = localStorage.getItem("apiconf_session_id");
const handleNewChat = () => {
saveSessionToHistory();
- const newSessionId = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
- localStorage.setItem('apiconf_session_id', newSessionId);
- setView('chat');
+ const newSessionId =
+ Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
+ localStorage.setItem("apiconf_session_id", newSessionId);
+ setView("chat");
if (window.innerWidth <= 768) setSidebarOpen(false); // Close sidebar on mobile
- setResetSignal(s => s + 1);
+ setResetSignal((s) => s + 1);
};
const handleRestoreSession = (sessionId: string) => {
saveSessionToHistory(); // Save the current session before restoring another
- localStorage.setItem('apiconf_session_id', sessionId);
- setView('chat');
+ localStorage.setItem("apiconf_session_id", sessionId);
+ setView("chat");
if (window.innerWidth <= 768) setSidebarOpen(false); // Close sidebar on mobile
- setResetSignal(s => s + 1);
+ setResetSignal((s) => s + 1);
};
return (
@@ -61,8 +71,10 @@ const App: React.FC = () => {
activeSessionId={activeSessionId}
/>
- {view === 'chat' && }
- {view === 'settings' && }
+ {view === "chat" && (
+
+ )}
+ {view === "settings" && }
);
diff --git a/frontend/src/components/Chat.module.css b/frontend/src/components/Chat.module.css
index daf55d5..e88da44 100644
--- a/frontend/src/components/Chat.module.css
+++ b/frontend/src/components/Chat.module.css
@@ -13,8 +13,12 @@
font-weight: 600;
color: #1e293b;
flex-shrink: 0;
-}
+
+h1 {
+ font-size: clamp(1rem, 4vw, 1.15rem);
+}
+}
.content {
flex: 1;
display: flex;
@@ -163,7 +167,9 @@
border-radius: 12px;
border: 1px solid #e2e8f0;
}
-
+.inputForm:focus-within{
+ outline:1px solid #0000ff;
+}
.inputField {
flex: 1;
padding: 12px;
@@ -204,6 +210,17 @@
cursor: pointer;
margin-right: 16px;
}
+.srOnly {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
@media (max-width: 768px) {
.chat {
@@ -213,8 +230,10 @@
}
.chatHeader {
- padding: 16px 12px;
- font-size: 16px;
+ display: flex;
+ align-items: center;
+
+
}
.content {
@@ -241,6 +260,4 @@
.menuButton {
display: block;
}
-
}
-
diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx
index 66c92b9..c040791 100644
--- a/frontend/src/components/Chat.tsx
+++ b/frontend/src/components/Chat.tsx
@@ -1,14 +1,14 @@
-import { FiMenu, FiSend } from 'react-icons/fi';
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { FiMenu, FiSend } from "react-icons/fi";
+import React, { useCallback, useEffect, useRef, useState } from "react";
-import ReactMarkdown from 'react-markdown';
-import TypingIndicator from './TypingIndicator';
-import remarkGfm from 'remark-gfm';
-import styles from './Chat.module.css';
+import ReactMarkdown from "react-markdown";
+import TypingIndicator from "./TypingIndicator";
+import remarkGfm from "remark-gfm";
+import styles from "./Chat.module.css";
interface Message {
text: string;
- sender: 'user' | 'bot';
+ sender: "user" | "bot";
timestamp: string;
}
@@ -19,107 +19,128 @@ interface ChatProps {
const Chat: React.FC = ({ onMenuClick, resetSignal }) => {
const [messages, setMessages] = useState([]);
- const [input, setInput] = useState('');
+ const [input, setInput] = useState("");
const [isTyping, setIsTyping] = useState(false);
- const [processedMessages, setProcessedMessages] = useState>(new Set());
+ const [processedMessages, setProcessedMessages] = useState>(
+ new Set(),
+ );
const [hasProcessedUrlMessage, setHasProcessedUrlMessage] = useState(false);
const [error, setError] = useState(null);
const getOrCreateId = (key: string) => {
let id = localStorage.getItem(key);
if (!id) {
- id = Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
+ id =
+ Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
localStorage.setItem(key, id);
}
return id;
};
- const userId = getOrCreateId('apiconf_user_id');
- const sessionId = getOrCreateId('apiconf_session_id');
+ const userId = getOrCreateId("apiconf_user_id");
+ const sessionId = getOrCreateId("apiconf_session_id");
- const handleSend = useCallback(async (messageToSend: string) => {
- if (!messageToSend.trim()) return;
+ const handleSend = useCallback(
+ async (messageToSend: string) => {
+ if (!messageToSend.trim()) return;
- // Save the first user message as the preview for the history
- const isFirstUserMessage = messages.filter(m => m.sender === 'user').length === 0;
- if (isFirstUserMessage) {
- localStorage.setItem(`session_preview_${sessionId}`, messageToSend);
- }
+ // Save the first user message as the preview for the history
+ const isFirstUserMessage =
+ messages.filter((m) => m.sender === "user").length === 0;
+ if (isFirstUserMessage) {
+ localStorage.setItem(`session_preview_${sessionId}`, messageToSend);
+ }
- if (processedMessages.has(messageToSend)) {
- console.log('Message already processed:', messageToSend);
- return;
- }
+ if (processedMessages.has(messageToSend)) {
+ console.log("Message already processed:", messageToSend);
+ return;
+ }
+
+ console.log("Sending message to /api/v1/agents/chat:", messageToSend);
- console.log('Sending message to /api/v1/agents/chat:', messageToSend);
-
- const userMessage: Message = {
- text: messageToSend,
- sender: 'user',
- timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
- };
-
- setMessages((prevMessages) => [...prevMessages, userMessage]);
- setIsTyping(true);
- setError(null);
-
- try {
- const response = await fetch('/api/v1/agents/chat', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- message: messageToSend,
- user_id: userId,
- session_id: sessionId,
+ const userMessage: Message = {
+ text: messageToSend,
+ sender: "user",
+ timestamp: new Date().toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
}),
- });
+ };
- if (!response.ok) {
- throw new Error(`Network response was not ok: ${response.status}`);
- }
+ setMessages((prevMessages) => [...prevMessages, userMessage]);
+ setIsTyping(true);
+ setError(null);
- const result = await response.json();
- console.log('API response:', result);
+ try {
+ const response = await fetch("/api/v1/agents/chat", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ message: messageToSend,
+ user_id: userId,
+ session_id: sessionId,
+ }),
+ });
- const botMessage: Message = {
- text: result.data.response,
- sender: 'bot',
- timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
- };
+ if (!response.ok) {
+ throw new Error(`Network response was not ok: ${response.status}`);
+ }
- setMessages((prevMessages) => [...prevMessages, botMessage]);
- setProcessedMessages((prev) => new Set(prev).add(messageToSend));
+ const result = await response.json();
+ console.log("API response:", result);
- // Clear URL parameters after processing
- if (window.location.search) {
- window.history.replaceState({}, document.title, window.location.pathname);
- }
+ const botMessage: Message = {
+ text: result.data.response,
+ sender: "bot",
+ timestamp: new Date().toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ }),
+ };
- } catch (error) {
- console.error('Error fetching chat response:', error);
- setError('Sorry, I seem to be having trouble connecting. Please try again later.');
- const errorMessage: Message = {
- text: 'Sorry, I seem to be having trouble connecting. Please try again later.',
- sender: 'bot',
- timestamp: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
- };
- setMessages((prevMessages) => [...prevMessages, errorMessage]);
- } finally {
- setIsTyping(false);
- }
- }, [processedMessages, userId, sessionId, messages]);
+ setMessages((prevMessages) => [...prevMessages, botMessage]);
+ setProcessedMessages((prev) => new Set(prev).add(messageToSend));
+
+ // Clear URL parameters after processing
+ if (window.location.search) {
+ window.history.replaceState(
+ {},
+ document.title,
+ window.location.pathname,
+ );
+ }
+ } catch (error) {
+ console.error("Error fetching chat response:", error);
+ setError(
+ "Sorry, I seem to be having trouble connecting. Please try again later.",
+ );
+ const errorMessage: Message = {
+ text: "Sorry, I seem to be having trouble connecting. Please try again later.",
+ sender: "bot",
+ timestamp: new Date().toLocaleTimeString([], {
+ hour: "2-digit",
+ minute: "2-digit",
+ }),
+ };
+ setMessages((prevMessages) => [...prevMessages, errorMessage]);
+ } finally {
+ setIsTyping(false);
+ }
+ },
+ [processedMessages, userId, sessionId, messages],
+ );
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
handleSend(input);
- setInput('');
+ setInput("");
};
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(() => {
@@ -130,31 +151,31 @@ const Chat: React.FC = ({ onMenuClick, resetSignal }) => {
useEffect(() => {
if (hasProcessedUrlMessage) return;
- console.log('=== URL DEBUGGING ===');
- console.log('Current URL:', window.location.href);
- console.log('Search params:', window.location.search);
- console.log('Pathname:', window.location.pathname);
+ console.log("=== URL DEBUGGING ===");
+ console.log("Current URL:", window.location.href);
+ console.log("Search params:", window.location.search);
+ console.log("Pathname:", window.location.pathname);
// Use native URLSearchParams to ensure we get the URL parameter
const urlParams = new URLSearchParams(window.location.search);
- const messageFromUrl = urlParams.get('message');
+ const messageFromUrl = urlParams.get("message");
- console.log('All URL params:', Object.fromEntries(urlParams));
- console.log('Message from URL:', messageFromUrl);
+ console.log("All URL params:", Object.fromEntries(urlParams));
+ console.log("Message from URL:", messageFromUrl);
if (messageFromUrl) {
const decodedMessage = decodeURIComponent(messageFromUrl);
- console.log('Decoded message:', decodedMessage);
- console.log('About to send message to API...');
-
+ console.log("Decoded message:", decodedMessage);
+ console.log("About to send message to API...");
+
setHasProcessedUrlMessage(true);
-
+
// Add a small delay to ensure the component is fully mounted
setTimeout(() => {
handleSend(decodedMessage);
}, 100);
} else {
- console.log('No message parameter found in URL');
+ console.log("No message parameter found in URL");
}
}, [handleSend, hasProcessedUrlMessage]);
@@ -168,27 +189,59 @@ const Chat: React.FC = ({ onMenuClick, resetSignal }) => {
}, [resetSignal]);
return (
-
+
-
-
+
+
- Chat with Ndu
+ Chat with Ndu
-
-
+
+
{messages.length === 0 ? (
-
Welcome!
-
Your friendly assistant for the API Conference 2025 in Lagos. Ask me about speakers, schedules, and more!
+
Welcome!
+
+ Your friendly assistant for the API Conference 2025 in Lagos.
+ Ask me about speakers, schedules, and more!
+
- handleSend('Who are the main speakers?')}>
+ handleSend("Who are the main speakers?")}
+ aria-label="Ask: Who are the main speakers?"
+ >
Who are the main speakers?
- handleSend('What is the conference schedule?')}>
+ handleSend("What is the conference schedule?")}
+ aria-label="Ask: What is the conference schedule?"
+ >
What is the conference schedule?
- handleSend('How do I get to the venue?')}>
+ handleSend("How do I get to the venue?")}
+ aria-label="Ask: How do I get to the venue?"
+ >
How do I get to the venue?
@@ -198,11 +251,13 @@ const Chat: React.FC
= ({ onMenuClick, resetSignal }) => {
- {msg.sender === 'bot' ? (
+ {msg.sender === "bot" ? (
{msg.text}
@@ -210,36 +265,61 @@ const Chat: React.FC = ({ onMenuClick, resetSignal }) => {
msg.text
)}
-
{msg.timestamp}
+
+ {msg.timestamp}
+
))
)}
{isTyping && (
-
-
+
+
Ndu is typing...
)}
- {error &&
{error}
}
-
+ {error && (
+
+ {error}
+
+ )}
+
-
-
+
+ Press Enter to send, or Ctrl+Enter for quick send
+
+
);
};
-export default Chat;
\ No newline at end of file
+export default Chat;
diff --git a/frontend/src/components/Sidebar.module.css b/frontend/src/components/Sidebar.module.css
index 62875cc..46b3c26 100644
--- a/frontend/src/components/Sidebar.module.css
+++ b/frontend/src/components/Sidebar.module.css
@@ -61,7 +61,9 @@
align-items: center;
gap: 12px;
font-size: 16px;
- transition: background-color 0.2s, color 0.2s;
+ transition:
+ background-color 0.2s,
+ color 0.2s;
}
.nav a:hover {
@@ -119,7 +121,9 @@
font-size: 0.75rem;
color: #6b7280;
}
-
+.closeButton {
+ display: none;
+}
@media (max-width: 768px) {
.sidebar {
position: absolute;
@@ -157,7 +161,7 @@
font-size: 24px;
cursor: pointer;
}
-
+
.logo {
position: relative;
}
@@ -166,7 +170,3 @@
.overlay {
display: none;
}
-
-.closeButton {
- display: none;
-}
\ No newline at end of file
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index 7feb23d..0f07fe8 100644
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -1,6 +1,6 @@
-import React, { useEffect, useState } from 'react';
-import styles from './Sidebar.module.css';
-import { FiPlus, FiX } from 'react-icons/fi';
+import React, { useEffect, useState } from "react";
+import styles from "./Sidebar.module.css";
+import { FiPlus, FiX } from "react-icons/fi";
interface HistoryItem {
sessionId: string;
@@ -16,53 +16,92 @@ interface SidebarProps {
activeSessionId: string | null;
}
-const Sidebar: React.FC = ({ isOpen, onClose, onNewChat, onRestoreSession, activeSessionId }) => {
+const Sidebar: React.FC = ({
+ isOpen,
+ onClose,
+ onNewChat,
+ onRestoreSession,
+ activeSessionId,
+}) => {
const [history, setHistory] = useState([]);
useEffect(() => {
// Refresh the history list whenever the sidebar is opened or the active session changes.
if (isOpen) {
- setHistory(JSON.parse(localStorage.getItem('apiconf_chat_history') || '[]'));
+ setHistory(
+ JSON.parse(localStorage.getItem("apiconf_chat_history") || "[]"),
+ );
}
}, [isOpen, activeSessionId]);
return (
<>
- {isOpen &&
}
+ {isOpen && (
+
+ )}
-
-
-
+
+
+
-
-
- New Chat
+
+
+ New Chat
-
Recent Chats
- {history.map((item) => (
-
onRestoreSession(item.sessionId)}
- >
-
{item.preview}
-
{new Date(item.timestamp).toLocaleDateString()}
-
- ))}
+
Recent Chats
+
+ {history.map((item) => (
+
onRestoreSession(item.sessionId)}
+ tabIndex={0}
+ role="listitem"
+ aria-label={`Chat from ${new Date(item.timestamp).toLocaleDateString()}: ${item.preview}`}
+ >
+
{item.preview}
+
+ {new Date(item.timestamp).toLocaleDateString()}
+
+
+ ))}
+
>
);
};
-export default Sidebar;
\ No newline at end of file
+export default Sidebar;
diff --git a/frontend/src/components/TypingIndicator.module.css b/frontend/src/components/TypingIndicator.module.css
index 749ba68..e4e74d0 100644
--- a/frontend/src/components/TypingIndicator.module.css
+++ b/frontend/src/components/TypingIndicator.module.css
@@ -22,10 +22,12 @@
}
@keyframes bounce {
- 0%, 80%, 100% {
+ 0%,
+ 80%,
+ 100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
-}
\ No newline at end of file
+}
diff --git a/frontend/src/components/TypingIndicator.tsx b/frontend/src/components/TypingIndicator.tsx
index 17b35ef..3f65f0e 100644
--- a/frontend/src/components/TypingIndicator.tsx
+++ b/frontend/src/components/TypingIndicator.tsx
@@ -1,14 +1,19 @@
-import React from 'react';
-import styles from './TypingIndicator.module.css';
+import React from "react";
+import styles from "./TypingIndicator.module.css";
const TypingIndicator: React.FC = () => {
return (
-
-
-
-
+
);
};
-export default TypingIndicator;
\ No newline at end of file
+export default TypingIndicator;
diff --git a/frontend/src/index.css b/frontend/src/index.css
index 0f04501..93b033f 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -1,9 +1,9 @@
/* frontend/src/index.css */
-@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
:root {
- font-family: 'Inter', sans-serif;
+ font-family: "Inter", sans-serif;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
@@ -17,7 +17,8 @@
padding: 0;
}
-html, body {
+html,
+body {
height: 100%;
overflow: hidden;
}
@@ -31,10 +32,13 @@ body {
background-color: #f0f0f0;
}
+button:focus{
+ outline:1px solid blue;
+}
#root {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
-}
\ No newline at end of file
+}
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 31cc4b6..69ccb3e 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -1,14 +1,14 @@
-import './index.css';
+import "./index.css";
-import App from './App.tsx';
-import { BrowserRouter } from 'react-router-dom';
-import { StrictMode } from 'react';
-import { createRoot } from 'react-dom/client';
+import App from "./App.tsx";
+import { BrowserRouter } from "react-router-dom";
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
-createRoot(document.getElementById('root')!).render(
+createRoot(document.getElementById("root")!).render(
-
+ ,
);
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 8377546..8d49c65 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,15 +1,15 @@
-import { defineConfig } from 'vite'
-import react from '@vitejs/plugin-react'
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
- '/api': {
- target: 'http://127.0.0.1:8000',
+ "/api": {
+ target: "http://127.0.0.1:8000",
changeOrigin: true,
},
},
},
-})
+});