mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-04 05:39:06 +00:00
Nav Menu Upgrades and Minor UX Improvements (#869)
* Converted navigation menu into a dropdown menu * Moved collapsed side panel menu icons into top row * Auto refresh when conversation is deleted to update side panel and route back to main page if deletion is on current conversation * Highlight the current conversation in the side panel * Dynamic homepage messages with current day and time of day. * `colorutils` upgraded to have more expansive tailwind color options and dynamic class name generation. * Converted create agent button alert into shadcn `ToolTip` * Colored lines and icons for agents in chat window * Cleaned up border styling in dark mode * fixed three dot menu in side panel to be more easier to click * Add the KhojLogo import in the nav menu and use a default user profile icon when not authenticated * Get rid of custom --box-shadow CSS variable * Pass the agent metadat through the chat body data in order to style the send button * Add login to the unauthenticated login view, redirecto to home if conversation history not loaded * Set a max height for the input text area * Simplify tailwind class names --------- Co-authored-by: sabaimran <narmiabas@gmail.com>
This commit is contained in:
@@ -9,8 +9,23 @@ div.automationCard {
|
||||
grid-template-rows: auto 1fr auto;
|
||||
}
|
||||
|
||||
div.pageLayout {
|
||||
max-width: 60vw;
|
||||
margin: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
div.sidePanel {
|
||||
position: fixed;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
div.automationsLayout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
div.pageLayout {
|
||||
max-width: 90vw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
.automationsLayout {
|
||||
max-width: 70vw;
|
||||
margin: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px) {
|
||||
.automationsLayout {
|
||||
max-width: 90vw;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import type { Metadata } from "next";
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
import styles from './automationsLayout.module.css';
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
|
||||
import "../globals.css";
|
||||
@@ -20,8 +18,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<div className={`${styles.automationsLayout}`}>
|
||||
<NavMenu selected="Automations" showLogo={true} />
|
||||
<div>
|
||||
{children}
|
||||
<Toaster />
|
||||
</div>
|
||||
|
||||
@@ -53,6 +53,8 @@ import LoginPrompt from '../components/loginPrompt/loginPrompt';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { ToastAction } from '@/components/ui/toast';
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import SidePanel from '../components/sidePanel/chatHistorySidePanel';
|
||||
import NavMenu from '../components/navMenu/navMenu';
|
||||
|
||||
const automationsFetcher = () => window.fetch('/api/automations').then(res => res.json()).catch(err => console.log(err));
|
||||
|
||||
@@ -899,9 +901,20 @@ export default function Automations() {
|
||||
const [allNewAutomations, setAllNewAutomations] = useState<AutomationsData[]>([]);
|
||||
const [suggestedAutomations, setSuggestedAutomations] = useState<AutomationsData[]>([]);
|
||||
const [showLoginPrompt, setShowLoginPrompt] = useState(false);
|
||||
const [isMobileWidth, setIsMobileWidth] = useState(false);
|
||||
|
||||
const ipLocationData = useIPLocationData();
|
||||
|
||||
useEffect(() => {
|
||||
if (window.innerWidth < 768) {
|
||||
setIsMobileWidth(true);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
setIsMobileWidth(window.innerWidth < 768);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (newAutomationData) {
|
||||
setAllNewAutomations([...allNewAutomations, newAutomationData]);
|
||||
@@ -923,186 +936,201 @@ export default function Automations() {
|
||||
|
||||
if (error) return <div>Failed to load</div>;
|
||||
|
||||
if (isLoading) return <Loading />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='py-4 flex justify-between'>
|
||||
<h3
|
||||
className='text-xl font-bold'>
|
||||
Automations
|
||||
</h3>
|
||||
<div className='flex flex-wrap gap-2 items-center md:justify-start justify-end'>
|
||||
{
|
||||
authenticatedData ? (
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><Envelope className='h-4 w-4 mr-2 inline text-orange-500' shadow-s />{authenticatedData.email}</span>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{
|
||||
ipLocationData && (
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><MapPinSimple className='h-4 w-4 mr-2 inline text-purple-500' />{ipLocationData ? `${ipLocationData.city}, ${ipLocationData.country}` : 'Unknown'}</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
ipLocationData && (
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><Clock className='h-4 w-4 mr-2 inline text-green-500' />{ipLocationData ? `${ipLocationData.timezone}` : 'Unknown'}</span>
|
||||
|
||||
)
|
||||
}
|
||||
<main className={`${styles.main} w-full ml-auto mr-auto`}>
|
||||
<div className="float-right w-fit h-fit">
|
||||
<NavMenu selected="Automations" />
|
||||
</div>
|
||||
<div className={`grid w-full ml-auto mr-auto`}>
|
||||
<div className={`${styles.sidePanel} top-0`}>
|
||||
<SidePanel
|
||||
webSocketConnected={true}
|
||||
conversationId={null}
|
||||
uploadedFiles={[]}
|
||||
isMobileWidth={isMobileWidth}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
showLoginPrompt && (
|
||||
<LoginPrompt
|
||||
onOpenChange={setShowLoginPrompt}
|
||||
loginRedirectMessage={"Create an account to make your own automation"} />
|
||||
)
|
||||
}
|
||||
<Alert className='bg-secondary border-none'>
|
||||
<AlertDescription>
|
||||
<Lightning weight={'fill'} className='h-4 w-4 text-purple-400 inline' />
|
||||
<span className='font-bold'>How it works</span> Automations help you structure your time by automating tasks you do regularly. Build your own, or try out our presets. Get results straight to your inbox.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className='flex justify-between py-4'>
|
||||
<h3
|
||||
className="text-xl">
|
||||
Your Creations
|
||||
</h3>
|
||||
{
|
||||
authenticatedData ? (
|
||||
<Dialog
|
||||
open={isCreating}
|
||||
onOpenChange={(open) => {
|
||||
setIsCreating(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
className='shadow-sm'
|
||||
variant="outline">
|
||||
<Plus className='h-4 w-4 mr-2' />
|
||||
Create Automation
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create Automation</DialogTitle>
|
||||
<EditCard
|
||||
createNew={true}
|
||||
setIsEditing={setIsCreating}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
authenticatedData={authenticatedData}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
setUpdatedAutomationData={setNewAutomationData}
|
||||
locationData={ipLocationData} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
className='shadow-sm'
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
variant={'outline'}>
|
||||
<Plus className='h-4 w-4 mr-2' />
|
||||
Create Automation
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Suspense>
|
||||
<SharedAutomationCard
|
||||
authenticatedData={authenticatedData}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
setNewAutomationData={setNewAutomationData} />
|
||||
</Suspense>
|
||||
{
|
||||
((!personalAutomations || personalAutomations.length === 0) && (allNewAutomations.length == 0)) && (
|
||||
<div>
|
||||
So empty! Create your own automation to get started.
|
||||
<div className='mt-4'>
|
||||
<div className={`${styles.pageLayout} w-full`}>
|
||||
<div className='py-4 sm:flex sm:justify-between grid gap-1'>
|
||||
<h1 className="text-3xl">Automations</h1>
|
||||
<div className='flex flex-wrap gap-2 items-center md:justify-start justify-end'>
|
||||
{
|
||||
authenticatedData ? (
|
||||
<Dialog
|
||||
open={isCreating}
|
||||
onOpenChange={(open) => {
|
||||
setIsCreating(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="default">Design</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create Automation</DialogTitle>
|
||||
<EditCard
|
||||
authenticatedData={authenticatedData}
|
||||
createNew={true}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
setIsEditing={setIsCreating}
|
||||
setUpdatedAutomationData={setNewAutomationData}
|
||||
locationData={ipLocationData} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><Envelope className='h-4 w-4 mr-2 inline text-orange-500' shadow-s />{authenticatedData.email}</span>
|
||||
)
|
||||
: null
|
||||
}
|
||||
{
|
||||
ipLocationData && (
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><MapPinSimple className='h-4 w-4 mr-2 inline text-purple-500' />{ipLocationData ? `${ipLocationData.city}, ${ipLocationData.country}` : 'Unknown'}</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
ipLocationData && (
|
||||
<span className='rounded-lg text-sm border-secondary border p-1 flex items-center shadow-sm' ><Clock className='h-4 w-4 mr-2 inline text-green-500' />{ipLocationData ? `${ipLocationData.timezone}` : 'Unknown'}</span>
|
||||
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
variant={'default'}>
|
||||
Design
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div
|
||||
className={`${styles.automationsLayout}`}>
|
||||
{
|
||||
personalAutomations && personalAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
key={automation.id}
|
||||
{
|
||||
showLoginPrompt && (
|
||||
<LoginPrompt
|
||||
onOpenChange={setShowLoginPrompt}
|
||||
loginRedirectMessage={"Create an account to make your own automation"} />
|
||||
)
|
||||
}
|
||||
<Alert className='bg-secondary border-none'>
|
||||
<AlertDescription>
|
||||
<Lightning weight={'fill'} className='h-4 w-4 text-purple-400 inline' />
|
||||
<span className='font-bold'>How it works</span> Automations help you structure your time by automating tasks you do regularly. Build your own, or try out our presets. Get results straight to your inbox.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
<div className='flex justify-between py-4'>
|
||||
<h3
|
||||
className="text-xl">
|
||||
Your Creations
|
||||
</h3>
|
||||
{
|
||||
authenticatedData ? (
|
||||
<Dialog
|
||||
open={isCreating}
|
||||
onOpenChange={(open) => {
|
||||
setIsCreating(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
className='shadow-sm'
|
||||
variant="outline">
|
||||
<Plus className='h-4 w-4 mr-2' />
|
||||
Create Automation
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create Automation</DialogTitle>
|
||||
<EditCard
|
||||
createNew={true}
|
||||
setIsEditing={setIsCreating}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
authenticatedData={authenticatedData}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
setUpdatedAutomationData={setNewAutomationData}
|
||||
locationData={ipLocationData} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
className='shadow-sm'
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
variant={'outline'}>
|
||||
<Plus className='h-4 w-4 mr-2' />
|
||||
Create Automation
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Suspense>
|
||||
<SharedAutomationCard
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt} />
|
||||
))}
|
||||
{
|
||||
allNewAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
key={automation.id}
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<h3
|
||||
className="text-xl py-4">
|
||||
Try these out
|
||||
</h3>
|
||||
<div
|
||||
className={`${styles.automationsLayout}`}>
|
||||
{
|
||||
suggestedAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
setNewAutomationData={setNewAutomationData}
|
||||
key={automation.id}
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
suggestedCard={true} />
|
||||
))
|
||||
}
|
||||
setNewAutomationData={setNewAutomationData} />
|
||||
</Suspense>
|
||||
{
|
||||
((!personalAutomations || personalAutomations.length === 0) && (allNewAutomations.length == 0) && !isLoading) && (
|
||||
<div>
|
||||
So empty! Create your own automation to get started.
|
||||
<div className='mt-4'>
|
||||
{
|
||||
authenticatedData ? (
|
||||
<Dialog
|
||||
open={isCreating}
|
||||
onOpenChange={(open) => {
|
||||
setIsCreating(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="default">Design</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogTitle>Create Automation</DialogTitle>
|
||||
<EditCard
|
||||
authenticatedData={authenticatedData}
|
||||
createNew={true}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
setIsEditing={setIsCreating}
|
||||
setUpdatedAutomationData={setNewAutomationData}
|
||||
locationData={ipLocationData} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
onClick={() => setShowLoginPrompt(true)}
|
||||
variant={'default'}>
|
||||
Design
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
isLoading && (
|
||||
<InlineLoading message='booting up your automations' />
|
||||
)
|
||||
}
|
||||
<div
|
||||
className={`${styles.automationsLayout}`}>
|
||||
{
|
||||
personalAutomations && personalAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
key={automation.id}
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt} />
|
||||
))}
|
||||
{
|
||||
allNewAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
key={automation.id}
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<h3
|
||||
className="text-xl py-4">
|
||||
Try these out
|
||||
</h3>
|
||||
<div
|
||||
className={`${styles.automationsLayout}`}>
|
||||
{
|
||||
suggestedAutomations.map((automation) => (
|
||||
<AutomationsCard
|
||||
setNewAutomationData={setNewAutomationData}
|
||||
key={automation.id}
|
||||
authenticatedData={authenticatedData}
|
||||
automation={automation}
|
||||
locationData={ipLocationData}
|
||||
isLoggedIn={authenticatedData ? true : false}
|
||||
setShowLoginPrompt={setShowLoginPrompt}
|
||||
suggestedCard={true} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user