mirror of
https://github.com/khoj-ai/khoj.git
synced 2025-02-17 08:04:21 +00:00
Compare commits
No commits in common. "7f5bf35806550eed155dee7bcc25e1d726744ab8" and "b9a889ab697f68c544ddbef2f5fd31f6f3df8427" have entirely different histories.
7f5bf35806
...
b9a889ab69
5 changed files with 17 additions and 209 deletions
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
import styles from "./sidePanel.module.css";
|
import styles from "./sidePanel.module.css";
|
||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRef } from "react";
|
|
||||||
|
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
|
|
||||||
|
@ -58,16 +57,12 @@ import {
|
||||||
UserCirclePlus,
|
UserCirclePlus,
|
||||||
Sidebar,
|
Sidebar,
|
||||||
NotePencil,
|
NotePencil,
|
||||||
FunnelSimple,
|
|
||||||
MagnifyingGlass,
|
|
||||||
} from "@phosphor-icons/react";
|
} from "@phosphor-icons/react";
|
||||||
|
|
||||||
interface ChatHistory {
|
interface ChatHistory {
|
||||||
conversation_id: string;
|
conversation_id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
agent_name: string;
|
agent_name: string;
|
||||||
agent_icon: string;
|
|
||||||
agent_color: string;
|
|
||||||
compressed: boolean;
|
compressed: boolean;
|
||||||
created: string;
|
created: string;
|
||||||
updated: string;
|
updated: string;
|
||||||
|
@ -76,11 +71,8 @@ interface ChatHistory {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
|
||||||
|
@ -104,8 +96,6 @@ import { modifyFileFilterForConversation } from "@/app/common/chatFunctions";
|
||||||
import { ScrollAreaScrollbar } from "@radix-ui/react-scroll-area";
|
import { ScrollAreaScrollbar } from "@radix-ui/react-scroll-area";
|
||||||
import { KhojLogoType } from "@/app/components/logo/khojLogo";
|
import { KhojLogoType } from "@/app/components/logo/khojLogo";
|
||||||
import NavMenu from "@/app/components/navMenu/navMenu";
|
import NavMenu from "@/app/components/navMenu/navMenu";
|
||||||
import { getIconFromIconName } from "@/app/common/iconUtils";
|
|
||||||
import AgentProfileCard from "../profileCard/profileCard";
|
|
||||||
|
|
||||||
// Define a fetcher function
|
// Define a fetcher function
|
||||||
const fetcher = (url: string) =>
|
const fetcher = (url: string) =>
|
||||||
|
@ -417,12 +407,6 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div>
|
<div>
|
||||||
{props.data && props.data.length > 5 && (
|
|
||||||
<ChatSessionsModal
|
|
||||||
data={props.organizedData}
|
|
||||||
showSidePanel={props.setEnabled}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<ScrollAreaScrollbar
|
<ScrollAreaScrollbar
|
||||||
orientation="vertical"
|
orientation="vertical"
|
||||||
|
@ -452,8 +436,6 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
|
||||||
}
|
}
|
||||||
slug={chatHistory.slug}
|
slug={chatHistory.slug}
|
||||||
agent_name={chatHistory.agent_name}
|
agent_name={chatHistory.agent_name}
|
||||||
agent_color={chatHistory.agent_color}
|
|
||||||
agent_icon={chatHistory.agent_icon}
|
|
||||||
showSidePanel={props.setEnabled}
|
showSidePanel={props.setEnabled}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
@ -462,6 +444,12 @@ function SessionsAndFiles(props: SessionsAndFilesProps) {
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
{props.data && props.data.length > 5 && (
|
||||||
|
<ChatSessionsModal
|
||||||
|
data={props.organizedData}
|
||||||
|
showSidePanel={props.setEnabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<FilesMenu
|
<FilesMenu
|
||||||
conversationId={props.conversationId}
|
conversationId={props.conversationId}
|
||||||
|
@ -695,150 +683,28 @@ interface ChatSessionsModalProps {
|
||||||
showSidePanel: (isEnabled: boolean) => void;
|
showSidePanel: (isEnabled: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AgentStyle {
|
|
||||||
color: string;
|
|
||||||
icon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
||||||
const [agentsFilter, setAgentsFilter] = useState<string[]>([]);
|
|
||||||
const [agentOptions, setAgentOptions] = useState<string[]>([]);
|
|
||||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
|
||||||
|
|
||||||
const [agentNameToStyleMap, setAgentNameToStyleMap] = useState<Record<string, AgentStyle>>({});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
const agents: string[] = [];
|
|
||||||
let agentNameToStyleMapLocal: Record<string, AgentStyle> = {};
|
|
||||||
Object.keys(data).forEach((timeGrouping) => {
|
|
||||||
data[timeGrouping].forEach((chatHistory) => {
|
|
||||||
if (!agents.includes(chatHistory.agent_name) && chatHistory.agent_name) {
|
|
||||||
agents.push(chatHistory.agent_name);
|
|
||||||
|
|
||||||
agentNameToStyleMapLocal = {
|
|
||||||
...agentNameToStyleMapLocal,
|
|
||||||
[chatHistory.agent_name]: {
|
|
||||||
color: chatHistory.agent_color,
|
|
||||||
icon: chatHistory.agent_icon,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
console.log(agentNameToStyleMapLocal);
|
|
||||||
setAgentNameToStyleMap(agentNameToStyleMapLocal);
|
|
||||||
setAgentOptions(agents);
|
|
||||||
}
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
// Memoize the filtered results
|
|
||||||
const filteredData = useMemo(() => {
|
|
||||||
if (!data) return null;
|
|
||||||
|
|
||||||
// Early return if no filters active
|
|
||||||
if (agentsFilter.length === 0 && searchQuery.length === 0) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filtered: GroupedChatHistory = {};
|
|
||||||
const agentSet = new Set(agentsFilter);
|
|
||||||
const searchLower = searchQuery.toLowerCase();
|
|
||||||
|
|
||||||
for (const timeGrouping in data) {
|
|
||||||
const matches = data[timeGrouping].filter((chatHistory) => {
|
|
||||||
// Early return for agent filter
|
|
||||||
if (agentsFilter.length > 0 && !agentSet.has(chatHistory.agent_name)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early return for search query
|
|
||||||
if (searchQuery && !chatHistory.slug?.toLowerCase().includes(searchLower)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matches.length > 0) {
|
|
||||||
filtered[timeGrouping] = matches;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered;
|
|
||||||
}, [data, agentsFilter, searchQuery]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog>
|
<Dialog>
|
||||||
<DialogTrigger className="flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-1 text-sm p-[0.1rem]">
|
<DialogTrigger className="flex text-left text-medium text-gray-500 hover:text-gray-300 cursor-pointer my-4 text-sm p-[0.5rem]">
|
||||||
<span className="flex items-center gap-1">
|
<span className="mr-2">
|
||||||
<MagnifyingGlass className="inline h-4 w-4 mr-1" weight="bold" /> Find
|
See All <ArrowRight className="inline h-4 w-4" weight="bold" />
|
||||||
Conversation
|
|
||||||
</span>
|
</span>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>All Conversations</DialogTitle>
|
<DialogTitle>All Conversations</DialogTitle>
|
||||||
<DialogDescription className="p-0">
|
<DialogDescription className="p-0">
|
||||||
<div className="flex flex-row justify-between mt-2 gap-2">
|
|
||||||
<Input
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSearchQuery(e.target.value);
|
|
||||||
}}
|
|
||||||
placeholder="Search conversations"
|
|
||||||
/>
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className={`p-0 px-1 ${agentsFilter.length > 0 ? "bg-muted text-muted-foreground" : "bg-inherit"} `}
|
|
||||||
>
|
|
||||||
<FunnelSimple />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent>
|
|
||||||
{/* <ScrollArea className="h-[200px]"> */}
|
|
||||||
<DropdownMenuLabel>Agents</DropdownMenuLabel>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
{agentOptions.map((agent) => (
|
|
||||||
<DropdownMenuCheckboxItem
|
|
||||||
key={agent}
|
|
||||||
onSelect={(e) => e.preventDefault()}
|
|
||||||
checked={agentsFilter.includes(agent)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (checked) {
|
|
||||||
setAgentsFilter([...agentsFilter, agent]);
|
|
||||||
} else {
|
|
||||||
setAgentsFilter(
|
|
||||||
agentsFilter.filter((a) => a !== agent),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-center px-1">
|
|
||||||
{getIconFromIconName(
|
|
||||||
agentNameToStyleMap[agent]?.icon,
|
|
||||||
agentNameToStyleMap[agent]?.color,
|
|
||||||
)}
|
|
||||||
<div className="break-words">{agent}</div>
|
|
||||||
</div>
|
|
||||||
</DropdownMenuCheckboxItem>
|
|
||||||
))}
|
|
||||||
{/* </ScrollArea> */}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</div>
|
|
||||||
<ScrollArea className="h-[500px] py-4">
|
<ScrollArea className="h-[500px] py-4">
|
||||||
{filteredData &&
|
{data &&
|
||||||
Object.keys(filteredData).map((timeGrouping) => (
|
Object.keys(data).map((timeGrouping) => (
|
||||||
<div key={timeGrouping}>
|
<div key={timeGrouping}>
|
||||||
<div
|
<div
|
||||||
className={`text-muted-foreground text-sm font-bold p-[0.5rem] `}
|
className={`text-muted-foreground text-sm font-bold p-[0.5rem] `}
|
||||||
>
|
>
|
||||||
{timeGrouping}
|
{timeGrouping}
|
||||||
</div>
|
</div>
|
||||||
{filteredData[timeGrouping].map((chatHistory) => (
|
{data[timeGrouping].map((chatHistory) => (
|
||||||
<ChatSession
|
<ChatSession
|
||||||
updated={chatHistory.updated}
|
updated={chatHistory.updated}
|
||||||
created={chatHistory.created}
|
created={chatHistory.created}
|
||||||
|
@ -847,8 +713,6 @@ function ChatSessionsModal({ data, showSidePanel }: ChatSessionsModalProps) {
|
||||||
conversation_id={chatHistory.conversation_id}
|
conversation_id={chatHistory.conversation_id}
|
||||||
slug={chatHistory.slug}
|
slug={chatHistory.slug}
|
||||||
agent_name={chatHistory.agent_name}
|
agent_name={chatHistory.agent_name}
|
||||||
agent_color={chatHistory.agent_color}
|
|
||||||
agent_icon={chatHistory.agent_icon}
|
|
||||||
showSidePanel={showSidePanel}
|
showSidePanel={showSidePanel}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -345,7 +345,7 @@ async def set_user_subscription(
|
||||||
user_subscription.type = type
|
user_subscription.type = type
|
||||||
if is_recurring is not None:
|
if is_recurring is not None:
|
||||||
user_subscription.is_recurring = is_recurring
|
user_subscription.is_recurring = is_recurring
|
||||||
if renewal_date is None:
|
if renewal_date is False:
|
||||||
user_subscription.renewal_date = None
|
user_subscription.renewal_date = None
|
||||||
elif renewal_date is not None:
|
elif renewal_date is not None:
|
||||||
user_subscription.renewal_date = renewal_date
|
user_subscription.renewal_date = renewal_date
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import csv
|
import csv
|
||||||
import json
|
import json
|
||||||
from datetime import date, datetime, timedelta, timezone
|
|
||||||
|
|
||||||
from apscheduler.job import Job
|
from apscheduler.job import Job
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
|
@ -66,38 +65,6 @@ admin.site.register(DjangoJob, KhojDjangoJobAdmin)
|
||||||
|
|
||||||
|
|
||||||
class KhojUserAdmin(UserAdmin):
|
class KhojUserAdmin(UserAdmin):
|
||||||
class DateJoinedAfterFilter(admin.SimpleListFilter):
|
|
||||||
title = "Joined after"
|
|
||||||
parameter_name = "joined_after"
|
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
|
||||||
return (
|
|
||||||
("1d", "Last 24 hours"),
|
|
||||||
("7d", "Last 7 days"),
|
|
||||||
("30d", "Last 30 days"),
|
|
||||||
("90d", "Last 90 days"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
|
||||||
if self.value():
|
|
||||||
days = int(self.value().rstrip("d"))
|
|
||||||
date_threshold = datetime.now() - timedelta(days=days)
|
|
||||||
return queryset.filter(date_joined__gte=date_threshold)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
class HasGoogleAuthFilter(admin.SimpleListFilter):
|
|
||||||
title = "Has Google Auth"
|
|
||||||
parameter_name = "has_google_auth"
|
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
|
||||||
return (("True", "True"), ("False", "False"))
|
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
|
||||||
if self.value() == "True":
|
|
||||||
return queryset.filter(googleuser__isnull=False)
|
|
||||||
if self.value() == "False":
|
|
||||||
return queryset.filter(googleuser__isnull=True)
|
|
||||||
|
|
||||||
list_display = (
|
list_display = (
|
||||||
"id",
|
"id",
|
||||||
"email",
|
"email",
|
||||||
|
@ -111,12 +78,6 @@ class KhojUserAdmin(UserAdmin):
|
||||||
search_fields = ("email", "username", "phone_number", "uuid")
|
search_fields = ("email", "username", "phone_number", "uuid")
|
||||||
filter_horizontal = ("groups", "user_permissions")
|
filter_horizontal = ("groups", "user_permissions")
|
||||||
|
|
||||||
list_filter = (
|
|
||||||
HasGoogleAuthFilter,
|
|
||||||
DateJoinedAfterFilter,
|
|
||||||
"verified_email",
|
|
||||||
) + UserAdmin.list_filter
|
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
"Personal info",
|
"Personal info",
|
||||||
|
|
|
@ -432,15 +432,7 @@ def chat_sessions(
|
||||||
conversations = conversations[:8]
|
conversations = conversations[:8]
|
||||||
|
|
||||||
sessions = conversations.values_list(
|
sessions = conversations.values_list(
|
||||||
"id",
|
"id", "slug", "title", "agent__slug", "agent__name", "created_at", "updated_at"
|
||||||
"slug",
|
|
||||||
"title",
|
|
||||||
"agent__slug",
|
|
||||||
"agent__name",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
"agent__style_icon",
|
|
||||||
"agent__style_color",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
session_values = [
|
session_values = [
|
||||||
|
@ -450,8 +442,6 @@ def chat_sessions(
|
||||||
"agent_name": session[4],
|
"agent_name": session[4],
|
||||||
"created": session[5].strftime("%Y-%m-%d %H:%M:%S"),
|
"created": session[5].strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
"updated": session[6].strftime("%Y-%m-%d %H:%M:%S"),
|
"updated": session[6].strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
"agent_icon": session[7],
|
|
||||||
"agent_color": session[8],
|
|
||||||
}
|
}
|
||||||
for session in sessions
|
for session in sessions
|
||||||
]
|
]
|
||||||
|
|
|
@ -66,23 +66,16 @@ async def subscribe(request: Request):
|
||||||
success = user is not None
|
success = user is not None
|
||||||
elif event_type in {"customer.subscription.updated"}:
|
elif event_type in {"customer.subscription.updated"}:
|
||||||
user_subscription = await sync_to_async(adapters.get_user_subscription)(customer_email)
|
user_subscription = await sync_to_async(adapters.get_user_subscription)(customer_email)
|
||||||
|
|
||||||
renewal_date = None
|
|
||||||
if subscription["current_period_end"]:
|
|
||||||
renewal_date = datetime.fromtimestamp(subscription["current_period_end"], tz=timezone.utc)
|
|
||||||
|
|
||||||
# Allow updating subscription status if paid user
|
# Allow updating subscription status if paid user
|
||||||
if user_subscription and user_subscription.renewal_date:
|
if user_subscription and user_subscription.renewal_date:
|
||||||
# Mark user as unsubscribed or resubscribed
|
# Mark user as unsubscribed or resubscribed
|
||||||
is_recurring = not subscription["cancel_at_period_end"]
|
is_recurring = not subscription["cancel_at_period_end"]
|
||||||
user, is_new = await adapters.set_user_subscription(
|
user, is_new = await adapters.set_user_subscription(customer_email, is_recurring=is_recurring)
|
||||||
customer_email, is_recurring=is_recurring, renewal_date=renewal_date
|
|
||||||
)
|
|
||||||
success = user is not None
|
success = user is not None
|
||||||
elif event_type in {"customer.subscription.deleted"}:
|
elif event_type in {"customer.subscription.deleted"}:
|
||||||
# Reset the user to trial state
|
# Reset the user to trial state
|
||||||
user, is_new = await adapters.set_user_subscription(
|
user, is_new = await adapters.set_user_subscription(
|
||||||
customer_email, is_recurring=False, renewal_date=None, type=Subscription.Type.TRIAL
|
customer_email, is_recurring=False, renewal_date=False, type=Subscription.Type.TRIAL
|
||||||
)
|
)
|
||||||
success = user is not None
|
success = user is not None
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue