Compare commits

..

No commits in common. "7f5bf35806550eed155dee7bcc25e1d726744ab8" and "b9a889ab697f68c544ddbef2f5fd31f6f3df8427" have entirely different histories.

5 changed files with 17 additions and 209 deletions

View file

@ -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}
/> />
))} ))}

View file

@ -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

View file

@ -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",

View file

@ -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
] ]

View file

@ -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