Ux improvements (#3947)

* black history sidebar

* misc improvements

* minor misc ux improvemnts

* quick nit

* add nits

* quick nit
This commit is contained in:
pablonyx
2025-02-10 12:18:41 -08:00
committed by GitHub
parent baee4c5f22
commit 3b25a2dd84
14 changed files with 59 additions and 323 deletions

View File

@@ -228,6 +228,7 @@ def create_update_persona(
num_chunks=create_persona_request.num_chunks,
llm_relevance_filter=create_persona_request.llm_relevance_filter,
llm_filter_extraction=create_persona_request.llm_filter_extraction,
is_default_persona=create_persona_request.is_default_persona,
)
versioned_make_persona_private = fetch_versioned_implementation(

View File

@@ -15,8 +15,6 @@ import {
PopoverContent,
} from "@/components/ui/popover";
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
import { AssistantVisibilityPopover } from "./AssistantVisibilityPopover";
import { DeleteAssistantPopover } from "./DeleteAssistantPopover";
import { Persona } from "@/app/admin/assistants/interfaces";
import { useUser } from "@/components/user/UserProvider";
import { useAssistants } from "@/components/context/AssistantsContext";
@@ -149,14 +147,9 @@ const AssistantCard: React.FC<{
)}
</div>
{isOwnedByUser && (
<div className="flex ml-2 items-center gap-x-2">
<Popover
open={activePopover !== undefined}
onOpenChange={(open) =>
open ? setActivePopover(null) : setActivePopover(undefined)
}
>
<PopoverTrigger asChild>
<div className="flex ml-2 relative items-center gap-x-2">
<Popover modal>
<PopoverTrigger>
<button
type="button"
className="hover:bg-neutral-200 dark:hover:bg-neutral-700 p-1 -my-1 rounded-full"
@@ -165,86 +158,56 @@ const AssistantCard: React.FC<{
</button>
</PopoverTrigger>
<PopoverContent
className={`z-[1000000] ${
activePopover === null ? "w-32" : "w-80"
} p-2`}
className={`w-32 z-[10000] p-2 hover:bg-red-400`}
>
{activePopover === null && (
<div className="flex flex-col text-sm space-y-1">
<div className="flex flex-col text-sm space-y-1">
<button
onClick={isOwnedByUser ? handleEdit : undefined}
className={`w-full flex items-center text-left px-2 py-1 rounded ${
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral-700"
: "opacity-50 cursor-not-allowed"
}`}
disabled={!isOwnedByUser}
>
<FiEdit size={12} className="inline mr-2" />
Edit
</button>
{isPaidEnterpriseFeaturesEnabled && isOwnedByUser && (
<button
onClick={isOwnedByUser ? handleEdit : undefined}
className={`w-full flex items-center text-left px-2 py-1 rounded ${
onClick={
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral-700"
? () => {
router.push(
`/assistants/stats/${persona.id}`
);
closePopover();
}
: undefined
}
className={`w-full text-left items-center px-2 py-1 rounded ${
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral-800"
: "opacity-50 cursor-not-allowed"
}`}
disabled={!isOwnedByUser}
>
<FiEdit size={12} className="inline mr-2" />
Edit
<FiBarChart size={12} className="inline mr-2" />
Stats
</button>
{isPaidEnterpriseFeaturesEnabled && isOwnedByUser && (
<button
onClick={
isOwnedByUser
? () => {
router.push(
`/assistants/stats/${persona.id}`
);
closePopover();
}
: undefined
}
className={`w-full text-left items-center px-2 py-1 rounded ${
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral-800"
: "opacity-50 cursor-not-allowed"
}`}
disabled={!isOwnedByUser}
>
<FiBarChart size={12} className="inline mr-2" />
Stats
</button>
)}
<button
onClick={isOwnedByUser ? handleDelete : undefined}
className={`w-full text-left items-center px-2 py-1 rounded ${
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral- text-red-600 dark:text-red-400"
: "opacity-50 cursor-not-allowed text-red-300 dark:text-red-500"
}`}
disabled={!isOwnedByUser}
>
<FiTrash size={12} className="inline mr-2" />
Delete
</button>
</div>
)}
{activePopover === "visibility" && (
<AssistantVisibilityPopover
assistant={persona}
user={user}
allUsers={[]}
onClose={closePopover}
onTogglePublic={async (isPublic: boolean) => {
await togglePersonaPublicStatus(persona.id, isPublic);
await refreshAssistants();
}}
/>
)}
{activePopover === "delete" && (
<DeleteAssistantPopover
entityName={persona.name}
onClose={closePopover}
onSubmit={async () => {
const success = await deletePersona(persona.id);
if (success) {
await refreshAssistants();
}
closePopover();
}}
/>
)}
)}
<button
onClick={isOwnedByUser ? handleDelete : undefined}
className={`w-full text-left items-center px-2 py-1 rounded ${
isOwnedByUser
? "hover:bg-neutral-200 dark:hover:bg-neutral- text-red-600 dark:text-red-400"
: "opacity-50 cursor-not-allowed text-red-300 dark:text-red-500"
}`}
disabled={!isOwnedByUser}
>
<FiTrash size={12} className="inline mr-2" />
Delete
</button>
</div>
</PopoverContent>
</Popover>
</div>

View File

@@ -118,7 +118,7 @@ export function AssistantModal({
return (
<Dialog open={true} onOpenChange={(open) => !open && hideModal()}>
<DialogContent
className="p-0 max-h-[80vh] max-w-none w-[95%] bg-background rounded-sm shadow-2xl transform transition-all duration-300 ease-in-out relative w-11/12 max-w-4xl pt-10 pb-10 px-10 overflow-hidden flex flex-col max-w-4xl"
className="p-0 max-h-[80vh] max-w-none w-[95%] bg-background rounded-sm shadow-2xl transform transition-all duration-300 ease-in-out relative w-11/12 max-w-4xl pt-10 pb-10 px-10 flex flex-col max-w-4xl"
style={{
position: "fixed",
top: "10vh",
@@ -127,7 +127,7 @@ export function AssistantModal({
margin: 0,
}}
>
<div className="flex den flex-col h-full">
<div className="flex overflow-hidden flex-col h-full">
<div className="flex flex-col sticky top-0 z-10">
<div className="flex px-2 justify-between items-center gap-x-2 mb-0">
<div className="h-12 w-full rounded-lg flex-col justify-center items-start gap-2.5 inline-flex">
@@ -205,7 +205,7 @@ export function AssistantModal({
Featured Assistants
</h2>
<div className="w-full px-2 pb-2 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
<div className="w-full px-2 pb-10 grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-6">
{featuredAssistants.length > 0 ? (
featuredAssistants.map((assistant, index) => (
<div key={index}>

View File

@@ -1,198 +0,0 @@
import React, { useState } from "react";
import { MinimalUserSnapshot, User } from "@/lib/types";
import { Button } from "@/components/ui/button";
import { FiPlus, FiX, FiEye, FiEyeOff } from "react-icons/fi";
import { SearchMultiSelectDropdown } from "@/components/Dropdown";
import { UsersIcon } from "@/components/icons/icons";
import { AssistantSharedStatusDisplay } from "../AssistantSharedStatus";
import {
addUsersToAssistantSharedList,
removeUsersFromAssistantSharedList,
} from "@/lib/assistants/shareAssistant";
import { usePopup } from "@/components/admin/connectors/Popup";
import { Bubble } from "@/components/Bubble";
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
import { Spinner } from "@/components/Spinner";
import { useAssistants } from "@/components/context/AssistantsContext";
import { Separator } from "@/components/ui/separator";
import { Persona } from "@/app/admin/assistants/interfaces";
import { ThreeDotsLoader } from "@/components/Loading";
interface AssistantVisibilityPopoverProps {
assistant: Persona;
user: User | null;
allUsers: MinimalUserSnapshot[];
onClose: () => void;
onTogglePublic: (isPublic: boolean) => Promise<void>;
}
export function AssistantVisibilityPopover({
assistant,
user,
allUsers,
onClose,
onTogglePublic,
}: AssistantVisibilityPopoverProps) {
const { refreshAssistants } = useAssistants();
const { popup, setPopup } = usePopup();
const [isUpdating, setIsUpdating] = useState(false);
const [selectedUsers, setSelectedUsers] = useState<MinimalUserSnapshot[]>([]);
const assistantName = assistant.name;
const sharedUsersWithoutOwner = (assistant.users || [])?.filter(
(u: MinimalUserSnapshot) => u.id !== assistant.owner?.id
);
const handleShare = async () => {
setIsUpdating(true);
const startTime = Date.now();
const error = await addUsersToAssistantSharedList(
assistant,
selectedUsers.map((user) => user.id)
);
await refreshAssistants();
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(0, 1000 - elapsedTime);
setTimeout(() => {
setIsUpdating(false);
if (error) {
setPopup({
message: `Failed to share assistant - ${error}`,
type: "error",
});
}
}, remainingTime);
};
const handleTogglePublic = async () => {
setIsUpdating(true);
await onTogglePublic(!assistant.is_public);
setIsUpdating(false);
};
return (
<>
{popup}
<div className="space-y-4">
<div>
<h3 className="text-sm font-semibold mb-2">Visibility</h3>
<Button
onClick={handleTogglePublic}
variant="outline"
size="sm"
className="w-full justify-start"
>
{assistant.is_public ? (
<>
<FiEyeOff className="mr-2" />
Make Private
</>
) : (
<>
<FiEye className="mr-2" />
Make Public
</>
)}
{isUpdating && (
<div className="ml-2 inline-flex items-center">
<ThreeDotsLoader />
<span className="ml-2 text-sm text-text-600">Updating...</span>
</div>
)}
</Button>
</div>
<Separator />
<div>
<h3 className="text-sm font-semibold mb-2">Share</h3>
<SearchMultiSelectDropdown
options={allUsers
.filter(
(u1) =>
!selectedUsers.map((u2) => u2.id).includes(u1.id) &&
!sharedUsersWithoutOwner
.map((u2: MinimalUserSnapshot) => u2.id)
.includes(u1.id) &&
u1.id !== user?.id
)
.map((user) => ({
name: user.email,
value: user.id,
}))}
onSelect={(option) => {
setSelectedUsers([
...Array.from(
new Set([
...selectedUsers,
{ id: option.value as string, email: option.name },
])
),
]);
}}
itemComponent={({ option }) => (
<div className="flex items-center px-4 py-2.5 cursor-pointer hover:bg-background-100">
<UsersIcon className="mr-3 text-text-500" />
<span className="flex-grow">{option.name}</span>
<FiPlus className="text-blue-500" />
</div>
)}
/>
</div>
{selectedUsers.length > 0 && (
<div>
<h4 className="text-xs font-medium text-text-700 mb-2">
Selected Users:
</h4>
<div className="flex flex-wrap gap-2">
{selectedUsers.map((selectedUser) => (
<div
key={selectedUser.id}
onClick={() => {
setSelectedUsers(
selectedUsers.filter(
(user) => user.id !== selectedUser.id
)
);
}}
className="flex items-center bg-blue-50 text-blue-700 rounded-full px-3 py-1 text-xs hover:bg-blue-100 transition-colors duration-200 cursor-pointer"
>
{selectedUser.email}
<FiX className="ml-2 text-blue-500" />
</div>
))}
</div>
</div>
)}
{selectedUsers.length > 0 && (
<Button
onClick={() => {
handleShare();
setSelectedUsers([]);
}}
size="sm"
variant="secondary"
>
Share with Selected Users
</Button>
)}
<div>
<h3 className="text-sm font-semibold mb-2">Currently Shared With</h3>
<div className="bg-background-50 rounded-lg p-2">
<AssistantSharedStatusDisplay
size="md"
assistant={assistant}
user={user}
/>
</div>
</div>
</div>
</>
);
}

View File

@@ -1,31 +0,0 @@
import React from "react";
import { FiTrash } from "react-icons/fi";
import { Button } from "@/components/ui/button";
interface DeleteAssistantPopoverProps {
entityName: string;
onClose: () => void;
onSubmit: () => void;
}
export function DeleteAssistantPopover({
entityName,
onClose,
onSubmit,
}: DeleteAssistantPopoverProps) {
return (
<div className="w-full">
<p className="text-sm mb-3">
Are you sure you want to delete assistant <b>{entityName}</b>?
</p>
<div className="flex justify-center gap-2">
<Button variant="secondary" size="sm" onClick={onClose}>
Cancel
</Button>
<Button variant="destructive" size="sm" onClick={onSubmit}>
Delete
</Button>
</div>
</div>
);
}

View File

@@ -162,12 +162,12 @@ export const FolderDropdown = forwardRef<HTMLDivElement, FolderDropdownProps>(
onDrop={handleDrop}
>
<div
className="sticky top-0 bg-background-sidebar z-10"
className="sticky top-0 bg-background-sidebar dark:bg-transparent z-10"
style={{ zIndex: 1000 - index }}
>
<div
ref={ref}
className="flex overflow-visible items-center w-full text-text-darker rounded-md p-1 relative bg-background-sidebar sticky top-0"
className="flex overflow-visible items-center w-full text-text-darker rounded-md p-1 relative sticky top-0"
style={{ zIndex: 10 - index }}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}

View File

@@ -161,7 +161,6 @@ export default function LLMPopover({
size: 16,
className: "flex-none my-auto text-black",
})}
asdfasdf
<span className="line-clamp-1 ">
{getDisplayNameForModel(name)}
</span>

View File

@@ -273,6 +273,7 @@ export const HistorySidebar = forwardRef<HTMLDivElement, HistorySidebarProps>(
border-r
dark:border-none
dark:text-[#D4D4D4]
dark:bg-[#000]
border-sidebar-border
flex
flex-col relative

View File

@@ -317,7 +317,7 @@ export function PagesTab({
return (
<div className="flex flex-col gap-y-2 flex-grow">
{popup}
<div className="px-4 mt-2 group mr-2 bg-background-sidebar z-20">
<div className="px-4 mt-2 group mr-2 bg-background-sidebar dark:bg-transparent z-20">
<div className="flex justify-between text-sm gap-x-2 text-text-300/80 items-center font-normal leading-normal">
<p>Chats</p>
<button

View File

@@ -116,7 +116,7 @@ export function Modal({
{icon && icon({ size: 30 })}
</h2>
</div>
{!hideDividerForTitle && <Separator />}
{!hideDividerForTitle ? <Separator /> : <div className="my-4" />}
</>
)}
</div>

View File

@@ -2,7 +2,7 @@ import "./spinner.css";
export const Spinner = () => {
return (
<div className="fixed top-0 left-0 z-50 w-screen h-screen bg-black bg-opacity-50 flex items-center justify-center">
<div className="fixed top-0 left-0 z-50 w-screen h-screen bg-[#000] bg-opacity-50 flex items-center justify-center">
<div className="loader ease-linear rounded-full border-8 border-t-8 border-background-200 h-8 w-8"></div>
</div>
);

View File

@@ -75,7 +75,7 @@ export function ClientLayout({
/>
)}
<div className="default-scrollbar flex-none text-text-settings-sidebar bg-background-sidebar w-[250px] overflow-x-hidden z-20 pt-2 pb-8 h-full border-r border-border dark:border-none miniscroll overflow-auto">
<div className="default-scrollbar flex-none text-text-settings-sidebar bg-background-sidebar dark:bg-[#000] w-[250px] overflow-x-hidden z-20 pt-2 pb-8 h-full border-r border-border dark:border-none miniscroll overflow-auto">
<AdminSidebar
collections={[
{

View File

@@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
"z-[1000000] w-72 rounded-md border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
" z-50 w-72 rounded-md border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
className
)}
{...props}

View File

@@ -647,6 +647,7 @@ export const useUserGroups = (): {
const MODEL_DISPLAY_NAMES: { [key: string]: string } = {
// OpenAI models
"o1-2025-12-17": "O1 (December 2025)",
"o3-mini": "O3 Mini",
"o1-mini": "O1 Mini",
"o1-preview": "O1 Preview",