mirror of https://github.com/kortix-ai/suna.git
wip
This commit is contained in:
parent
eedb4cfdaf
commit
3fd6d25826
|
@ -2510,7 +2510,6 @@
|
|||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.4.tgz",
|
||||
"integrity": "sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
|
|
|
@ -7,19 +7,10 @@ export default async function PersonalAccountBillingPage() {
|
|||
const supabaseClient = await createClient();
|
||||
const {data: personalAccount} = await supabaseClient.rpc('get_personal_account');
|
||||
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-medium tracking-tight">Billing</h3>
|
||||
<p className="text-muted-foreground">
|
||||
Manage your subscription and billing details.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<AccountBillingStatus
|
||||
accountId={personalAccount.account_id}
|
||||
returnUrl={`${returnUrl}/dashboard/settings/billing`}
|
||||
/>
|
||||
<div>
|
||||
<AccountBillingStatus accountId={personalAccount.account_id} returnUrl={`${returnUrl}/dashboard/settings/billing`} />
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,74 +1,13 @@
|
|||
"use client";
|
||||
import EditPersonalAccountName from "@/components/basejump/edit-personal-account-name";
|
||||
import {createClient} from "@/lib/supabase/server";
|
||||
|
||||
import { createClient } from "@/lib/supabase/client";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { UserIcon } from "lucide-react";
|
||||
export default async function PersonalAccountSettingsPage() {
|
||||
const supabaseClient = await createClient();
|
||||
const {data: personalAccount} = await supabaseClient.rpc('get_personal_account');
|
||||
|
||||
export default function PersonalAccountSettingsPage() {
|
||||
const [user, setUser] = useState<any>(null);
|
||||
const [personalAccount, setPersonalAccount] = useState<any>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadData() {
|
||||
const supabase = createClient();
|
||||
|
||||
// Get user
|
||||
const { data: { user } } = await supabase.auth.getUser();
|
||||
setUser(user);
|
||||
|
||||
// Get personal account
|
||||
const { data: account } = await supabase.rpc('get_personal_account');
|
||||
setPersonalAccount(account);
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
loadData();
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center items-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto py-8">
|
||||
<h1 className="text-3xl font-bold mb-8">Personal Account</h1>
|
||||
|
||||
<Card className="mb-8">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<UserIcon size={20} />
|
||||
<span>User Profile</span>
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Manage your personal information
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground mb-1">Name</p>
|
||||
<p>{user?.user_metadata?.name || "Not set"}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground mb-1">Email</p>
|
||||
<p>{user?.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button variant="outline" className="w-fit">
|
||||
Edit Profile
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
<div>
|
||||
<EditPersonalAccountName account={personalAccount} />
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
import ManageTeams from "@/components/basejump/manage-teams";
|
||||
|
||||
export default async function PersonalAccountTeamsPage() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-card-title">Teams</h3>
|
||||
<p className="text-sm text-foreground/70">
|
||||
Manage your teams and team memberships.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ManageTeams />
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -765,7 +765,7 @@ export default function ThreadPage({ params }: { params: Promise<ThreadParams> }
|
|||
<div className="flex-1 flex flex-col relative overflow-hidden">
|
||||
<div
|
||||
ref={messagesContainerRef}
|
||||
className="flex-1 overflow-y-auto px-6 py-4 pb-[5.5rem]"
|
||||
className="flex-1 overflow-y-auto px-6 py-4 pb-[0.5rem]"
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<div className="mx-auto max-w-3xl">
|
||||
|
|
|
@ -28,6 +28,11 @@ import {
|
|||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger
|
||||
} from "@/components/ui/tooltip"
|
||||
import { getProjects, getThreads } from "@/lib/api"
|
||||
import Link from "next/link"
|
||||
|
||||
|
@ -76,14 +81,18 @@ export function NavAgents() {
|
|||
<SidebarGroup>
|
||||
<div className="flex justify-between items-center">
|
||||
<SidebarGroupLabel>Agents</SidebarGroupLabel>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="text-muted-foreground hover:text-foreground h-8 w-8 flex items-center justify-center rounded-md"
|
||||
title="New Agent"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
<span className="sr-only">New Agent</span>
|
||||
</Link>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="text-muted-foreground hover:text-foreground h-8 w-8 flex items-center justify-center rounded-md"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
<span className="sr-only">New Agent</span>
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>New Agent</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<SidebarMenu className="overflow-y-auto max-h-[calc(100vh-200px)]">
|
||||
|
@ -102,15 +111,26 @@ export function NavAgents() {
|
|||
<>
|
||||
{agents.map((item, index) => (
|
||||
<SidebarMenuItem key={`agent-${index}`}>
|
||||
<SidebarMenuButton
|
||||
asChild
|
||||
tooltip={state === "collapsed" ? item.name : undefined}
|
||||
>
|
||||
<Link href={item.url} title={item.name}>
|
||||
<MessagesSquare className="h-4 w-4" />
|
||||
<span>{item.name}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
{state === "collapsed" ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<SidebarMenuButton asChild>
|
||||
<Link href={item.url}>
|
||||
<MessagesSquare className="h-4 w-4" />
|
||||
<span>{item.name}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{item.name}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<SidebarMenuButton asChild>
|
||||
<Link href={item.url}>
|
||||
<MessagesSquare className="h-4 w-4" />
|
||||
<span>{item.name}</span>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
{state !== "collapsed" && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
|
|
|
@ -8,7 +8,13 @@ import {
|
|||
SidebarMenuBadge,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger
|
||||
} from "@/components/ui/tooltip"
|
||||
|
||||
export function NavSecondary({
|
||||
items,
|
||||
|
@ -21,18 +27,34 @@ export function NavSecondary({
|
|||
badge?: React.ReactNode
|
||||
}[]
|
||||
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
||||
const { state } = useSidebar()
|
||||
|
||||
return (
|
||||
<SidebarGroup {...props}>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
{state === "collapsed" ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{item.title}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<SidebarMenuButton asChild>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
)}
|
||||
{item.badge && <SidebarMenuBadge>{item.badge}</SidebarMenuBadge>}
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
|
|
|
@ -21,16 +21,21 @@ import {
|
|||
} from "@/components/ui/sidebar"
|
||||
import { useEffect, useState } from "react"
|
||||
import { createClient } from "@/lib/supabase/client"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger
|
||||
} from "@/components/ui/tooltip"
|
||||
|
||||
// Only keep necessary data
|
||||
const navSecondaryItems = [
|
||||
{
|
||||
title: "Careers",
|
||||
title: "Hiring!",
|
||||
url: "https://www.kortix.ai/careers",
|
||||
icon: BookOpen,
|
||||
},
|
||||
{
|
||||
title: "Book Demo",
|
||||
title: "Book Enterprise Demo",
|
||||
url: "https://cal.com/marko-kraemer/15min",
|
||||
icon: CalendarClock,
|
||||
},
|
||||
|
@ -82,7 +87,12 @@ export function SidebarLeft({
|
|||
</div>
|
||||
{state !== "collapsed" && (
|
||||
<div className="ml-auto">
|
||||
<SidebarTrigger className="h-8 w-8" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<SidebarTrigger className="h-8 w-8" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Toggle sidebar</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@ -92,7 +102,12 @@ export function SidebarLeft({
|
|||
<NavSecondary items={navSecondaryItems} className="mt-auto" />
|
||||
{state === "collapsed" && (
|
||||
<div className="mt-2 flex justify-center">
|
||||
<SidebarTrigger className="h-8 w-8" />
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<SidebarTrigger className="h-8 w-8" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Expand sidebar</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</SidebarContent>
|
||||
|
|
|
@ -316,6 +316,17 @@ export function FileViewerModal({
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Show selected file name in breadcrumb */}
|
||||
{selectedFile && (
|
||||
<div className="flex items-center min-w-fit">
|
||||
<ChevronRight className="h-4 w-4 mx-1 text-muted-foreground opacity-50" />
|
||||
<div className="flex items-center gap-1 h-7 px-2.5 text-sm font-medium bg-accent/30 rounded-md">
|
||||
<File className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
<span>{selectedFile.split('/').pop()}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -475,28 +486,49 @@ export function FileViewerModal({
|
|||
</div>
|
||||
) : (
|
||||
workspaceFiles.map((file, index) => (
|
||||
<Button
|
||||
<div
|
||||
key={file.path}
|
||||
variant={selectedFile === file.path ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
className={`w-full justify-start h-9 text-sm font-normal transition-colors ${
|
||||
selectedFile === file.path
|
||||
? "bg-accent/50 hover:bg-accent/60"
|
||||
: "hover:bg-accent/30"
|
||||
}`}
|
||||
onClick={() => handleFileClick(file)}
|
||||
className="relative group"
|
||||
>
|
||||
{file.is_dir ? (
|
||||
selectedFile === file.path ? (
|
||||
<FolderOpen className="h-4 w-4 mr-2 flex-shrink-0 text-foreground" />
|
||||
<Button
|
||||
variant={selectedFile === file.path ? "secondary" : "ghost"}
|
||||
size="sm"
|
||||
className={`w-full justify-start h-9 text-sm font-normal transition-colors ${
|
||||
selectedFile === file.path
|
||||
? "bg-accent/50 hover:bg-accent/60"
|
||||
: "hover:bg-accent/30"
|
||||
}`}
|
||||
onClick={() => handleFileClick(file)}
|
||||
>
|
||||
{file.is_dir ? (
|
||||
selectedFile === file.path ? (
|
||||
<FolderOpen className="h-4 w-4 mr-2 flex-shrink-0 text-foreground" />
|
||||
) : (
|
||||
<Folder className="h-4 w-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
)
|
||||
) : (
|
||||
<Folder className="h-4 w-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
)
|
||||
) : (
|
||||
<File className="h-4 w-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
<File className="h-4 w-4 mr-2 flex-shrink-0 text-muted-foreground" />
|
||||
)}
|
||||
<span className="truncate">{file.name}</span>
|
||||
</Button>
|
||||
|
||||
{/* Show download button on hover for files */}
|
||||
{!file.is_dir && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 h-6 w-6 opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setSelectedFile(file.path);
|
||||
handleDownload();
|
||||
}}
|
||||
title="Download file"
|
||||
>
|
||||
<Download className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
)}
|
||||
<span className="truncate">{file.name}</span>
|
||||
</Button>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
@ -548,27 +580,6 @@ export function FileViewerModal({
|
|||
|
||||
{/* File content pane */}
|
||||
<div className="w-full flex-1 flex flex-col h-full bg-muted/5">
|
||||
{/* File header */}
|
||||
<div className="px-3 py-2 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<File className="h-4 w-4 text-muted-foreground" />
|
||||
<h3 className="text-sm font-medium truncate">
|
||||
{selectedFile ? selectedFile.split('/').pop() : 'Select a file to view'}
|
||||
</h3>
|
||||
</div>
|
||||
{selectedFile && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-8 px-3 gap-2"
|
||||
onClick={handleDownload}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
Download
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* File content */}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<ScrollArea className="h-full">
|
||||
|
@ -578,8 +589,6 @@ export function FileViewerModal({
|
|||
</ScrollArea>
|
||||
</div>
|
||||
|
||||
{/* Empty footer to match left side height */}
|
||||
<div className="px-2 py-2 border-t bg-muted/5 h-[44px]" />
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
|
|
@ -21,7 +21,7 @@ export function ToolCallSidePanel({ isOpen, onClose, toolCallData }: ToolCallSid
|
|||
return (
|
||||
<div
|
||||
className={`
|
||||
${isOpen ? 'w-full sm:w-[80%] md:w-[65%] lg:w-[50%] xl:w-[40%] 2xl:w-[30%] max-w-[800px]' : 'w-0'}
|
||||
${isOpen ? 'w-full sm:w-[85%] md:w-[75%] lg:w-[60%] xl:w-[50%] 2xl:w-[40%] max-w-[1000px]' : 'w-0'}
|
||||
border-l bg-sidebar h-screen flex flex-col
|
||||
transition-all duration-300 ease-in-out overflow-hidden
|
||||
fixed sm:sticky top-0 right-0 z-30
|
||||
|
|
|
@ -5,7 +5,7 @@ import { createClient } from "../supabase/server";
|
|||
import { redirect } from "next/navigation";
|
||||
|
||||
export async function createInvitation(prevState: any, formData: FormData): Promise<{ token?: string, message?: string}> {
|
||||
"use server";
|
||||
|
||||
|
||||
const invitationType = formData.get("invitationType") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
@ -33,7 +33,7 @@ export async function createInvitation(prevState: any, formData: FormData): Prom
|
|||
};
|
||||
|
||||
export async function deleteInvitation(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const invitationId = formData.get("invitationId") as string;
|
||||
const returnPath = formData.get("returnPath") as string;
|
||||
|
@ -54,7 +54,7 @@ export async function deleteInvitation(prevState: any, formData: FormData) {
|
|||
};
|
||||
|
||||
export async function acceptInvitation(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const token = formData.get("token") as string;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
|
|||
import { createClient } from "../supabase/server";
|
||||
|
||||
export async function removeTeamMember(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const userId = formData.get("userId") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
@ -27,7 +27,7 @@ export async function removeTeamMember(prevState: any, formData: FormData) {
|
|||
|
||||
|
||||
export async function updateTeamMemberRole(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const userId = formData.get("userId") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
'use server'
|
||||
|
||||
import { createClient } from "../supabase/server";
|
||||
|
||||
export async function editPersonalAccountName(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
const name = formData.get("name") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { redirect } from "next/navigation";
|
|||
import { createClient } from "../supabase/server";
|
||||
|
||||
export async function createTeam(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const name = formData.get("name") as string;
|
||||
const slug = formData.get("slug") as string;
|
||||
|
@ -26,7 +26,7 @@ export async function createTeam(prevState: any, formData: FormData) {
|
|||
|
||||
|
||||
export async function editTeamName(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const name = formData.get("name") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
@ -45,7 +45,7 @@ export async function editTeamName(prevState: any, formData: FormData) {
|
|||
};
|
||||
|
||||
export async function editTeamSlug(prevState: any, formData: FormData) {
|
||||
"use server";
|
||||
|
||||
|
||||
const slug = formData.get("slug") as string;
|
||||
const accountId = formData.get("accountId") as string;
|
||||
|
|
Loading…
Reference in New Issue