OwnGPT.v2 / client /src /components /UI /AppHeader.jsx
parthib07's picture
Upload 199 files
212c959 verified
import {
FileText,
LogIn,
Monitor,
Moon,
MoreHorizontal,
PanelLeft,
Pencil,
Share2,
SquarePen,
Sun,
Trash2,
} from 'lucide-react'
import Button from './Button'
import Dropdown from './Dropdown'
function themeOptionItems(themePreference, onThemePreferenceChange) {
return [
{
key: 'system',
label: 'System',
icon: Monitor,
onSelect: () => onThemePreferenceChange('system'),
},
{
key: 'light',
label: 'Light',
icon: Sun,
onSelect: () => onThemePreferenceChange('light'),
},
{
key: 'dark',
label: 'Dark',
icon: Moon,
onSelect: () => onThemePreferenceChange('dark'),
},
]
}
export default function AppHeader({
title,
theme,
themePreference,
currentSessionId,
canRenameSession,
hasMessages,
onToggleSidebar,
onThemePreferenceChange,
onNewChat,
onRenameSession,
onDeleteSession,
onShare,
onExportMarkdown,
onExportPdf,
onFeedback,
user,
onLogin,
onLogout,
}) {
const accountName = user?.name || user?.username || ''
const actions = [
{
key: 'new',
label: 'New chat',
icon: SquarePen,
onSelect: onNewChat,
},
...(canRenameSession
? [
{
key: 'rename',
label: 'Rename',
icon: Pencil,
onSelect: onRenameSession,
},
]
: []),
...(currentSessionId
? [
{
key: 'delete',
label: 'Delete',
icon: Trash2,
onSelect: onDeleteSession,
destructive: true,
},
]
: []),
{
key: 'share',
label: 'Share',
icon: Share2,
onSelect: onShare,
disabled: !hasMessages,
},
{
key: 'export-md',
label: 'Export Markdown',
icon: FileText,
onSelect: onExportMarkdown,
disabled: !hasMessages,
},
{
key: 'export-pdf',
label: 'Export PDF',
icon: FileText,
onSelect: onExportPdf,
disabled: !hasMessages,
},
]
const userItems = user
? [
{
key: 'feedback',
label: 'Send feedback',
onSelect: onFeedback,
},
{
key: 'separator',
type: 'separator',
},
{
key: 'logout',
label: 'Sign out',
onSelect: onLogout,
},
]
: []
const ThemeIcon =
themePreference === 'system' ? Monitor : theme === 'dark' ? Moon : Sun
return (
<header className="h-14 border-b border-border bg-background/95 px-3 backdrop-blur-xl sm:px-4">
<div className="flex h-full items-center justify-between gap-3">
<div className="flex min-w-0 items-center gap-2">
<Button size="icon" onClick={onToggleSidebar} variant="secondary" className="shrink-0">
<PanelLeft className="h-4 w-4" />
</Button>
<h1 className="max-w-[52vw] truncate text-sm font-medium text-foreground sm:max-w-[22rem]">
{title}
</h1>
</div>
<div className="flex shrink-0 items-center gap-1.5">
<Button variant="ghost" size="sm" onClick={onNewChat} className="hidden sm:inline-flex">
<SquarePen className="h-4 w-4" />
<span>New chat</span>
</Button>
<Dropdown
trigger={
<Button variant="ghost" size="icon" aria-label="Theme options">
<ThemeIcon className="h-4 w-4" />
</Button>
}
items={themeOptionItems(themePreference, onThemePreferenceChange)}
/>
<Dropdown
trigger={
<Button variant="ghost" size="icon" aria-label="Workspace actions">
<MoreHorizontal className="h-4 w-4" />
</Button>
}
items={actions}
/>
{user ? (
<Dropdown
trigger={
<button
type="button"
className="flex h-11 min-w-11 items-center justify-center rounded-full border border-border/70 bg-card/95 px-3 text-sm font-semibold text-foreground shadow-soft transition hover:border-accent/25"
aria-label="Open account menu"
>
{user.avatar_url ? (
<img
src={user.avatar_url}
alt={accountName}
className="h-8 w-8 rounded-full object-cover"
/>
) : (
accountName.slice(0, 1).toUpperCase()
)}
</button>
}
items={userItems}
/>
) : (
<Button variant="primary" onClick={onLogin}>
<LogIn className="h-4 w-4" />
Sign in
</Button>
)}
</div>
</div>
</header>
)
}