mirror of
https://github.com/harivansh-afk/RAG-ui.git
synced 2026-04-15 04:03:30 +00:00
UI updates
This commit is contained in:
parent
171fe6e04f
commit
7dda2e20d0
7 changed files with 195 additions and 122 deletions
2
.env
2
.env
|
|
@ -2,7 +2,7 @@ VITE_SUPABASE_URL=https://nvatjthzedykhikmttot.supabase.co
|
|||
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im52YXRqdGh6ZWR5a2hpa210dG90Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzM1OTYxOTMsImV4cCI6MjA0OTE3MjE5M30.u4euR8U-XxxvOdLFmWJD2yrd4E_MPMt_X1yqRrDTF2I
|
||||
|
||||
# N8N Configuration
|
||||
VITE_N8N_WEBHOOK_URL=https://harivansh.app.n8n.cloud/webhook/chat-webhook
|
||||
VITE_N8N_WEBHOOK_URL=https://harivansh.app.n8n.cloud/webhook/chat-webhoo
|
||||
VITE_N8N_UPLOAD_WEBHOOK_URL=https://harivansh.app.n8n.cloud/webhook/upload-webhook
|
||||
|
||||
# Google Drive Configuration
|
||||
|
|
|
|||
17
src/App.tsx
17
src/App.tsx
|
|
@ -2,17 +2,20 @@ import React from 'react';
|
|||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { AppRouter } from './routes';
|
||||
import { AuthProvider } from './contexts/AuthContext';
|
||||
import { ThemeProvider } from './contexts/ThemeContext';
|
||||
import './index.css';
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<BrowserRouter>
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<AppRouter />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</AuthProvider>
|
||||
<ThemeProvider>
|
||||
<AuthProvider>
|
||||
<BrowserRouter>
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<AppRouter />
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</AuthProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ const DashboardLayout = () => {
|
|||
<Header />
|
||||
<div className="flex flex-1 overflow-hidden gap-6 p-6 pt-24">
|
||||
<Sidebar />
|
||||
<main className="flex-1 overflow-y-auto rounded-2xl bg-background shadow-sm border">
|
||||
<div className="p-6">
|
||||
<main className="flex-1 rounded-2xl bg-background shadow-sm border overflow-hidden">
|
||||
<div className="h-full">
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
|||
72
src/contexts/ThemeContext.tsx
Normal file
72
src/contexts/ThemeContext.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
interface ThemeContextType {
|
||||
isDarkMode: boolean;
|
||||
toggleDarkMode: () => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
function setThemeClass(isDark: boolean) {
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||
const [isDarkMode, setIsDarkMode] = useState(() => {
|
||||
if (typeof window === 'undefined') return false;
|
||||
|
||||
// Check localStorage first
|
||||
const stored = localStorage.getItem('darkMode');
|
||||
if (stored !== null) {
|
||||
const isDark = stored === 'true';
|
||||
setThemeClass(isDark);
|
||||
return isDark;
|
||||
}
|
||||
|
||||
// Then check system preference
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
setThemeClass(systemPrefersDark);
|
||||
return systemPrefersDark;
|
||||
});
|
||||
|
||||
// Update theme when isDarkMode changes
|
||||
useEffect(() => {
|
||||
setThemeClass(isDarkMode);
|
||||
localStorage.setItem('darkMode', String(isDarkMode));
|
||||
}, [isDarkMode]);
|
||||
|
||||
// Listen for system theme changes
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
const handleChange = (e: MediaQueryListEvent) => {
|
||||
if (localStorage.getItem('darkMode') === null) {
|
||||
setIsDarkMode(e.matches);
|
||||
}
|
||||
};
|
||||
|
||||
mediaQuery.addEventListener('change', handleChange);
|
||||
return () => mediaQuery.removeEventListener('change', handleChange);
|
||||
}, []);
|
||||
|
||||
const toggleDarkMode = () => {
|
||||
setIsDarkMode(prev => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ isDarkMode, toggleDarkMode }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const context = useContext(ThemeContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useTheme must be used within a ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Button } from '../../components/ui/Button';
|
||||
import { Card } from '../../components/ui/card';
|
||||
import { Label } from '../../components/ui/label';
|
||||
import { Switch } from '../../components/ui/switch';
|
||||
import { Slider } from '../../components/ui/slider';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/ui/tabs';
|
||||
import { useTheme } from '../../contexts/ThemeContext';
|
||||
|
||||
function Settings() {
|
||||
// Chat & RAG Settings
|
||||
|
|
@ -14,8 +14,7 @@ function Settings() {
|
|||
const [relevanceThreshold, setRelevanceThreshold] = useState(0.8);
|
||||
|
||||
// UI Settings
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
const [compactView, setCompactView] = useState(false);
|
||||
const { isDarkMode, toggleDarkMode } = useTheme();
|
||||
const [messageGrouping, setMessageGrouping] = useState(true);
|
||||
|
||||
// Privacy Settings
|
||||
|
|
@ -32,7 +31,7 @@ function Settings() {
|
|||
<h1 className="text-3xl font-bold">Settings</h1>
|
||||
|
||||
<Tabs defaultValue="chat" className="w-full">
|
||||
<TabsList className="mb-4 bg-purple-50">
|
||||
<TabsList className="mb-4 bg-muted">
|
||||
<TabsTrigger
|
||||
value="chat"
|
||||
className="data-[state=active]:bg-gradient-to-r data-[state=active]:from-purple-400 data-[state=active]:to-purple-500 data-[state=active]:text-white"
|
||||
|
|
@ -115,20 +114,8 @@ function Settings() {
|
|||
<p className="text-sm text-muted-foreground">Enable dark color theme</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={darkMode}
|
||||
onCheckedChange={setDarkMode}
|
||||
className="data-[state=checked]:bg-gradient-to-r data-[state=checked]:from-purple-400 data-[state=checked]:to-purple-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Compact View</Label>
|
||||
<p className="text-sm text-muted-foreground">Reduce spacing in chat interface</p>
|
||||
</div>
|
||||
<Switch
|
||||
checked={compactView}
|
||||
onCheckedChange={setCompactView}
|
||||
checked={isDarkMode}
|
||||
onCheckedChange={toggleDarkMode}
|
||||
className="data-[state=checked]:bg-gradient-to-r data-[state=checked]:from-purple-400 data-[state=checked]:to-purple-500"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -187,11 +174,6 @@ function Settings() {
|
|||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="flex justify-end space-x-4">
|
||||
<Button variant="outline" className="hover:bg-purple-50">Reset to Defaults</Button>
|
||||
<Button className="bg-gradient-to-r from-purple-400 to-purple-500 hover:from-purple-500 hover:to-purple-600 text-white">Save Changes</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,8 +108,8 @@ export default function AskQuestion() {
|
|||
try {
|
||||
let currentChatId: string;
|
||||
|
||||
// If this is a new chat, create it first with the title from the first message
|
||||
if (isNewChat) {
|
||||
// If no chat exists or we're on the base ask page, create a new one
|
||||
if (!chatId || chatId === undefined) {
|
||||
const title = generateChatTitle(question.trim());
|
||||
const newChat = await chatService.createChatInstance(user.id, title);
|
||||
if (!newChat) {
|
||||
|
|
@ -120,8 +120,6 @@ export default function AskQuestion() {
|
|||
setIsNewChat(false);
|
||||
// Update URL with the real chat ID
|
||||
navigate(`/dashboard/ask/${newChat.id}`, { replace: true });
|
||||
} else if (!chatId) {
|
||||
throw new Error('No chat ID available');
|
||||
} else {
|
||||
currentChatId = chatId;
|
||||
}
|
||||
|
|
@ -182,75 +180,73 @@ export default function AskQuestion() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="h-[calc(100vh-4rem)] flex flex-col">
|
||||
{/* Main Chat Container */}
|
||||
<div className="flex-1 flex flex-col relative">
|
||||
{/* Sticky Subheader */}
|
||||
<div className="sticky top-0 z-10 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 border-b">
|
||||
<div className="flex h-14 items-center justify-between px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10">
|
||||
<MessageSquarePlus className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-sm font-medium">Ask a Question</h1>
|
||||
{chat && (
|
||||
<p className="text-xs text-muted-foreground line-clamp-1">
|
||||
{chat.title}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Header Container */}
|
||||
<div className="flex-none bg-background rounded-lg m-4">
|
||||
<div className="flex h-full items-center justify-between px-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/10">
|
||||
<MessageSquarePlus className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div>
|
||||
<h1 className="text-sm font-medium">Ask a Question</h1>
|
||||
{chat && (
|
||||
<p className="text-xs text-muted-foreground line-clamp-1">
|
||||
{chat.title}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleNewChat}
|
||||
className="text-muted-foreground hover:text-primary hover:bg-primary/10"
|
||||
>
|
||||
<MessageSquarePlus className="mr-2 h-4 w-4" />
|
||||
New Chat
|
||||
</Button>
|
||||
{chatId && !isNewChat && messages.length > 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleNewChat}
|
||||
onClick={clearChat}
|
||||
className="text-muted-foreground hover:text-primary hover:bg-primary/10"
|
||||
>
|
||||
<MessageSquarePlus className="mr-2 h-4 w-4" />
|
||||
New Chat
|
||||
<X className="mr-2 h-4 w-4" />
|
||||
Clear chat
|
||||
</Button>
|
||||
{chatId && !isNewChat && messages.length > 0 && (
|
||||
)}
|
||||
{hasExistingChats && (
|
||||
<Link to="/dashboard/history">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={clearChat}
|
||||
className="text-muted-foreground hover:text-primary hover:bg-primary/10"
|
||||
>
|
||||
<X className="mr-2 h-4 w-4" />
|
||||
Clear chat
|
||||
<History className="mr-2 h-4 w-4" />
|
||||
History
|
||||
</Button>
|
||||
)}
|
||||
{hasExistingChats && (
|
||||
<Link to="/dashboard/history">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-muted-foreground hover:text-primary hover:bg-primary/10"
|
||||
>
|
||||
<History className="mr-2 h-4 w-4" />
|
||||
History
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Error Message */}
|
||||
{error && (
|
||||
<div className="flex-none mx-4 mt-4 flex items-center gap-2 rounded-lg bg-destructive/10 p-3 text-sm text-destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Main Chat Container - Added pb-20 to create space at bottom */}
|
||||
<div className="flex-1 bg-background rounded-lg mx-4 flex flex-col min-h-0 pb-20">
|
||||
{/* Messages Container */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{error && (
|
||||
<div className="flex-none mx-4 mt-4 flex items-center gap-2 rounded-lg bg-destructive/10 p-3 text-sm text-destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col space-y-6 p-4">
|
||||
{!isNewChat && !chatId ? (
|
||||
<div className="h-full flex items-center justify-center min-h-[calc(100vh-12rem)]">
|
||||
<div className="h-full flex items-center justify-center min-h-[calc(100vh-50rem)]">
|
||||
<div className="max-w-md w-full space-y-8">
|
||||
<div className="flex flex-col items-center text-center">
|
||||
<div className="rounded-full bg-purple-500/20 p-6 mb-8">
|
||||
|
|
@ -371,43 +367,43 @@ export default function AskQuestion() {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input Box - Fixed at Bottom */}
|
||||
<div className="sticky bottom-0 left-0 right-0 p-3 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<form onSubmit={handleSubmit} className="mx-auto max-w-3xl">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
value={question}
|
||||
onChange={(e) => setQuestion(e.target.value)}
|
||||
placeholder="Ask me anything..."
|
||||
className={cn(
|
||||
'flex-1 rounded-full px-4 py-2 text-sm',
|
||||
'bg-primary/5 border-primary/10',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20',
|
||||
'placeholder:text-muted-foreground'
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || !question.trim()}
|
||||
className={cn(
|
||||
'rounded-full bg-gradient-to-r from-purple-400 to-purple-500 text-white h-8 w-8 p-0',
|
||||
'hover:from-purple-500 hover:to-purple-600',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20',
|
||||
'disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Send className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{/* Input Box - Keep original styling */}
|
||||
<div className="sticky bottom-0 left-0 right-0 p-4 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
|
||||
<form onSubmit={handleSubmit} className="mx-auto max-w-3xl">
|
||||
<div className="flex items-center gap-2 h-full">
|
||||
<input
|
||||
type="text"
|
||||
value={question}
|
||||
onChange={(e) => setQuestion(e.target.value)}
|
||||
placeholder="Ask me anything..."
|
||||
className={cn(
|
||||
'flex-1 rounded-full px-4 py-2 text-sm',
|
||||
'bg-primary/5 border-primary/10',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20',
|
||||
'placeholder:text-muted-foreground'
|
||||
)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading || !question.trim()}
|
||||
className={cn(
|
||||
'rounded-full bg-gradient-to-r from-purple-400 to-purple-500 text-white h-8 w-8 p-0',
|
||||
'hover:from-purple-500 hover:to-purple-600',
|
||||
'focus:outline-none focus:ring-2 focus:ring-primary/20',
|
||||
'disabled:opacity-50'
|
||||
)}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Send className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
20
src/types.d.ts
vendored
Normal file
20
src/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/// <reference types="@radix-ui/react-label" />
|
||||
/// <reference types="@radix-ui/react-switch" />
|
||||
/// <reference types="@radix-ui/react-slider" />
|
||||
/// <reference types="@radix-ui/react-tabs" />
|
||||
|
||||
declare module '../../components/ui/label' {
|
||||
export { Label } from '@radix-ui/react-label'
|
||||
}
|
||||
|
||||
declare module '../../components/ui/switch' {
|
||||
export { Switch } from '@radix-ui/react-switch'
|
||||
}
|
||||
|
||||
declare module '../../components/ui/slider' {
|
||||
export { Slider } from '@radix-ui/react-slider'
|
||||
}
|
||||
|
||||
declare module '../../components/ui/tabs' {
|
||||
export { Tabs, TabsContent, TabsList, TabsTrigger } from '@radix-ui/react-tabs'
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue