diff --git a/src/interface/web/app/automations/page.tsx b/src/interface/web/app/automations/page.tsx index c933f8fd..251c3500 100644 --- a/src/interface/web/app/automations/page.tsx +++ b/src/interface/web/app/automations/page.tsx @@ -447,7 +447,7 @@ function SharedAutomationCard(props: SharedAutomationCardProps) { - + Create Automation @@ -946,8 +946,8 @@ export default function Automations() {
-

Automations

-
+

Automations

+
{ authenticatedData ? ( {authenticatedData.email} @@ -1035,7 +1035,7 @@ export default function Automations() { { ((!personalAutomations || personalAutomations.length === 0) && (allNewAutomations.length == 0) && !isLoading) && ( -
+
So empty! Create your own automation to get started.
{ diff --git a/src/interface/web/app/common/auth.ts b/src/interface/web/app/common/auth.ts index a5bc7f13..15dbd9dd 100644 --- a/src/interface/web/app/common/auth.ts +++ b/src/interface/web/app/common/auth.ts @@ -71,9 +71,9 @@ export interface UserConfig { export function useUserConfig(detailed: boolean = false) { const url = `/api/settings?detailed=${detailed}`; - const { data, error } = useSWR(url, fetcher, { revalidateOnFocus: false }); + const { data: userConfig, error, isLoading: isLoadingUserConfig } = useSWR(url, fetcher, { revalidateOnFocus: false }); - if (error || !data || data.detail === 'Forbidden') return null; + if (error || !userConfig || userConfig?.detail === 'Forbidden') return {userConfig: null, isLoadingUserConfig}; - return data; + return {userConfig, isLoadingUserConfig}; } diff --git a/src/interface/web/app/components/logo/khogLogo.tsx b/src/interface/web/app/components/logo/khogLogo.tsx index c2cdc8c1..fcdeb60c 100644 --- a/src/interface/web/app/components/logo/khogLogo.tsx +++ b/src/interface/web/app/components/logo/khogLogo.tsx @@ -1,6 +1,6 @@ export function KhojLogoType() { return ( - + diff --git a/src/interface/web/app/components/navMenu/navMenu.tsx b/src/interface/web/app/components/navMenu/navMenu.tsx index 859b01a8..c119596a 100644 --- a/src/interface/web/app/components/navMenu/navMenu.tsx +++ b/src/interface/web/app/components/navMenu/navMenu.tsx @@ -81,30 +81,26 @@ export default function NavMenu() { { userData ? - + - - {userData?.username[0]} + + {userData?.username[0].toUpperCase()} : - + } - + setDarkMode(!darkMode)} className='w-full cursor-pointer'>
{ - setDarkMode(!darkMode) - } - } className="flex flex-rows"> {darkMode ? : }

{darkMode ? 'Light Mode' : 'Dark Mode'}

- +

Agents

@@ -112,7 +108,7 @@ export default function NavMenu() { - +

Automations

@@ -120,7 +116,7 @@ export default function NavMenu() { - +

Search

@@ -131,7 +127,7 @@ export default function NavMenu() { {userData && - +

Settings

@@ -140,7 +136,7 @@ export default function NavMenu() { } - +

Help

@@ -150,7 +146,7 @@ export default function NavMenu() { { userData ? - +

Logout

@@ -159,7 +155,7 @@ export default function NavMenu() { : - +

Login

@@ -171,15 +167,15 @@ export default function NavMenu() { : - + { userData ? - + - - {userData?.username[0]} + + {userData?.username[0].toUpperCase()} : @@ -187,19 +183,15 @@ export default function NavMenu() { } - + setDarkMode(!darkMode)} className="w-full hover:cursor-pointer">
{ - setDarkMode(!darkMode) - } - } className="flex flex-rows"> {darkMode ? : }

{darkMode ? 'Light Mode' : 'Dark Mode'}

- +

Agents

@@ -207,7 +199,7 @@ export default function NavMenu() { - +

Automations

@@ -215,7 +207,7 @@ export default function NavMenu() { - +

Search

@@ -225,7 +217,7 @@ export default function NavMenu() { <> - +

Help

@@ -235,7 +227,7 @@ export default function NavMenu() { { userData && - +

Settings

@@ -246,7 +238,7 @@ export default function NavMenu() { { userData ? - +

Logout

@@ -255,7 +247,7 @@ export default function NavMenu() { : - +

Login

diff --git a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx index 7322c12e..f21c6056 100644 --- a/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx +++ b/src/interface/web/app/components/sidePanel/chatHistorySidePanel.tsx @@ -696,19 +696,19 @@ export default function SidePanel(props: SidePanelProps) { : -
+
-
- +
+ {enabled ? : }
-
+
diff --git a/src/interface/web/app/components/sidePanel/sidePanel.module.css b/src/interface/web/app/components/sidePanel/sidePanel.module.css index e984eb6a..74d5358e 100644 --- a/src/interface/web/app/components/sidePanel/sidePanel.module.css +++ b/src/interface/web/app/components/sidePanel/sidePanel.module.css @@ -143,7 +143,8 @@ div.modalSessionsList div.session { div.session.compressed { max-width: 100%; - grid-template-columns: minmax(auto, 300px) 1fr; + display: flex; + justify-content: space-between; } div.session { diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index b2b90ac3..8d58c803 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -38,7 +38,7 @@ interface SuggestionCardProps { export default function SuggestionCard(data: SuggestionCardProps) { const bgColors = converColorToBgGradient(data.color); - const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full sm:h-fit lg:w-[200px] lg:h-[200px]`; + const cardClassName = `${styles.card} ${bgColors} md:w-full md:h-fit sm:w-full sm:h-fit lg:w-[200px] lg:h-[200px] cursor-pointer`; const titleClassName = `${styles.title} pt-2 dark:text-white dark:font-bold`; const descriptionClassName = `${styles.text} dark:text-white`; diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index 91c5978d..50701efd 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -1,39 +1,25 @@ 'use client' import './globals.css'; - import styles from './page.module.css'; -import React, { useEffect, useState } from 'react'; - -import SuggestionCard from './components/suggestions/suggestionCard'; -import SidePanel from './components/sidePanel/chatHistorySidePanel'; -import Loading from './components/loading/loading'; -import useSWR from 'swr'; -import Image from 'next/image'; - import 'katex/dist/katex.min.css'; -import ChatInputArea, { ChatOptions } from './components/chatInputArea/chatInputArea'; -import { useAuthenticatedData } from './common/auth'; -import { Card, CardTitle } from '@/components/ui/card'; -import { convertColorToBorderClass } from './common/colorUtils'; -import { getIconFromIconName } from './common/iconUtils'; +import React, { useEffect, useState } from 'react'; +import useSWR from 'swr'; +import Image from 'next/image'; import { ClockCounterClockwise } from '@phosphor-icons/react'; -import { AgentData } from './agents/page'; -import { Suggestion, suggestionsData } from './components/suggestions/suggestionsData'; -import LoginPrompt from './components/loginPrompt/loginPrompt'; +import { Card, CardTitle } from '@/components/ui/card'; +import SuggestionCard from '@/app/components/suggestions/suggestionCard'; +import SidePanel from '@/app/components/sidePanel/chatHistorySidePanel'; +import Loading from '@/app/components/loading/loading'; +import ChatInputArea, { ChatOptions } from '@/app/components/chatInputArea/chatInputArea'; +import { Suggestion, suggestionsData } from '@/app/components/suggestions/suggestionsData'; +import LoginPrompt from '@/app/components/loginPrompt/loginPrompt'; -//get today's day -const today = new Date(); -const day = today.getDay(); -const greetings = [ - `Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What would you like to do today?`, - 'How can I help you today?', - `Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What's on your mind?`, - `Ready to breeze through your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]}?`, - `Want to navigate your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?` -]; -const greeting = greetings[~~(Math.random() * greetings.length)]; +import { useAuthenticatedData, UserConfig, useUserConfig } from '@/app/common/auth'; +import { convertColorToBorderClass } from '@/app/common/colorUtils'; +import { getIconFromIconName } from '@/app/common/iconUtils'; +import { AgentData } from '@/app/agents/page'; interface ChatBodyDataProps { @@ -42,19 +28,17 @@ interface ChatBodyDataProps { setUploadedFiles: (files: string[]) => void; isMobileWidth?: boolean; isLoggedIn: boolean; + userConfig: UserConfig | null; + isLoadingUserConfig: boolean; } async function createNewConvo(slug: string) { try { const response = await fetch(`/api/chat/sessions?client=web&agent_slug=${slug}`, { method: "POST" }); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } + if (!response.ok) throw new Error(`Failed to fetch chat sessions with status: ${response.status}`); const data = await response.json(); const conversationID = data.conversation_id; - if (!conversationID) { - throw new Error("Conversation ID not found in response"); - } + if (!conversationID) throw new Error("Conversation ID not found in response"); return conversationID; } catch (error) { console.error("Error creating new conversation:", error); @@ -65,6 +49,7 @@ async function createNewConvo(slug: string) { function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = useState(''); const [processingMessage, setProcessingMessage] = useState(false); + const [greeting, setGreeting] = useState(''); const [shuffledOptions, setShuffledOptions] = useState([]); const [selectedAgent, setSelectedAgent] = useState("khoj"); const [agentIcons, setAgentIcons] = useState([]); @@ -79,6 +64,29 @@ function ChatBodyData(props: ChatBodyDataProps) { setShuffledOptions(shuffled.slice(0, 3)); } + useEffect(() => { + console.log(`Loading user config: ${props.isLoadingUserConfig}`); + if (props.isLoadingUserConfig) return; + + // Set user config + console.log(`Logged In: ${props.isLoggedIn}\nUserConfig: ${props.userConfig}`); + + // Get today's day + const today = new Date(); + const day = today.getDay(); + const timeOfDay = today.getHours() > 4 && today.getHours() < 12 ? 'morning' : today.getHours() < 17 ? 'afternoon' : 'evening'; + const nameSuffix = props.userConfig?.given_name ? `, ${props.userConfig?.given_name}` : ""; + const greetings = [ + `What would you like to get done${nameSuffix}?`, + `Hey${nameSuffix}! How can I help?`, + `Good ${timeOfDay}${nameSuffix}! What's on your mind?`, + `Ready to breeze through your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]}?`, + `Want help navigating your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?` + ]; + const greeting = greetings[Math.floor(Math.random() * greetings.length)]; + setGreeting(greeting); + }, [props.isLoadingUserConfig, props.userConfig]); + useEffect(() => { if (props.chatOptionsData) { shuffleAndSetOptions(); @@ -87,9 +95,7 @@ function ChatBodyData(props: ChatBodyDataProps) { useEffect(() => { const nSlice = props.isMobileWidth ? 3 : 4; - const shuffledAgents = agentsData ? [...agentsData].sort(() => 0.5 - Math.random()) : []; - const agents = agentsData ? [agentsData[0]] : []; // Always add the first/default agent. shuffledAgents.slice(0, nSlice - 1).forEach(agent => { @@ -168,7 +174,7 @@ function ChatBodyData(props: ChatBodyDataProps) { }
-

{greeting}

+

{greeting}

{ !props.isMobileWidth && @@ -280,12 +286,19 @@ export default function Home() { const [uploadedFiles, setUploadedFiles] = useState([]); const [isMobileWidth, setIsMobileWidth] = useState(false); + const {userConfig: initialUserConfig, isLoadingUserConfig} = useUserConfig(true); + const [userConfig, setUserConfig] = useState(null); + const authenticatedData = useAuthenticatedData(); const handleConversationIdChange = (newConversationId: string) => { setConversationID(newConversationId); }; + useEffect(() => { + setUserConfig(initialUserConfig); + }, [initialUserConfig]); + useEffect(() => { fetch('/api/chat/options') .then(response => response.json()) @@ -332,6 +345,8 @@ export default function Home() { setUploadedFiles={setUploadedFiles} isMobileWidth={isMobileWidth} onConversationIdChange={handleConversationIdChange} + userConfig={userConfig} + isLoadingUserConfig={isLoadingUserConfig} />
diff --git a/src/interface/web/app/search/page.tsx b/src/interface/web/app/search/page.tsx index 61e726ac..33b613d8 100644 --- a/src/interface/web/app/search/page.tsx +++ b/src/interface/web/app/search/page.tsx @@ -159,13 +159,7 @@ export default function Search() { }, [isMobileWidth]); function search() { - if (searchResultsLoading) { - return; - } - - if (!searchQuery.trim()) { - return; - } + if (searchResultsLoading || !searchQuery.trim()) return; const apiUrl = `/api/search?q=${encodeURIComponent(searchQuery)}&client=web`; fetch(apiUrl, { @@ -246,7 +240,7 @@ export default function Search() { } { !focusSearchResult && searchResults && searchResults.length > 0 && -
+
{ searchResults.map((result, index) => { diff --git a/src/interface/web/app/settings/page.tsx b/src/interface/web/app/settings/page.tsx index 9947c06c..ba04c85a 100644 --- a/src/interface/web/app/settings/page.tsx +++ b/src/interface/web/app/settings/page.tsx @@ -348,7 +348,7 @@ export default function SettingsView() { const [title, setTitle] = useState("Settings"); const [isMobileWidth, setIsMobileWidth] = useState(false); const { apiKeys, generateAPIKey, copyAPIKey, deleteAPIKey } = useApiKeys(); - const initialUserConfig = useUserConfig(true); + const {userConfig: initialUserConfig} = useUserConfig(true); const [userConfig, setUserConfig] = useState(null); const [name, setName] = useState(undefined); const [notionToken, setNotionToken] = useState(null); diff --git a/src/interface/web/app/share/chat/page.tsx b/src/interface/web/app/share/chat/page.tsx index c763248b..cd3c4eb7 100644 --- a/src/interface/web/app/share/chat/page.tsx +++ b/src/interface/web/app/share/chat/page.tsx @@ -280,7 +280,6 @@ export default function SharedChat() {
-
}>