This commit is contained in:
marko-kraemer 2025-04-16 08:50:32 +01:00
parent eedb4cfdaf
commit 3fd6d25826
14 changed files with 160 additions and 170 deletions

View File

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

View File

@ -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`}
/>
return (
<div>
<AccountBillingStatus accountId={personalAccount.account_id} returnUrl={`${returnUrl}/dashboard/settings/billing`} />
</div>
)
}

View File

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

View File

@ -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>
return (
<div>
<ManageTeams />
</div>
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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