Wire up Profile/Appearance/Security in user menu (#21)
* Add editable display-name field to generic integrations Lets users set a custom name for Proxmox, Docker, AWS, Remote Desktop, Netbird, Cloudflare, Uptime Kuma, and Weather integrations, separate from the host/IP field, mirroring the SSH host rename pattern. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4 * Surface the new-integration name field as a labeled input The name field for new generic integrations was a faint header input with only placeholder text, easy to miss. Move it into the form grid as a proper labeled "Name" field next to the other connection fields. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kF4hZWEkRCPPvCZTeXxn4 * Add file upload for SSH private key and certificate fields Lets users pick a key file from disk (e.g. ~/.ssh) instead of pasting its contents into the Private Key / OPKSSH Certificate fields. * Fix SSH private key paste corrupting multi-line PEM format Private Key and Certificate fields were single-line <input> elements, which strip newlines on paste and corrupt PEM-formatted keys (causing 'Unsupported key format' errors). Render them as multi-line textareas instead so pasted keys keep their line breaks. * Add JSON-converted bookmark import file for Archnest data import Converts homarr-bookmarks.md into the format expected by /api/data/import. * Auto-populate bookmark icons via favicon service in import JSON Each bookmark now points to Google's favicon endpoint for its domain instead of having no icon at all. * Wire up Profile/Appearance/Security in the user menu Profile, Appearance, and Security were dead "#" links. Added URL-based tab deep-linking to the Settings page (?tab=profile|appearance|security|...) and pointed the menu items at it. Added a Security tab placeholder ahead of password/sessions/login-log/SSO work. --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d9d9f3f610
commit
ea14486f6e
2 changed files with 37 additions and 7 deletions
|
|
@ -220,18 +220,27 @@ export default function TopBar() {
|
|||
<p className="text-[10px] text-text-secondary">{user?.email || user?.username}</p>
|
||||
</div>
|
||||
<div className="py-1">
|
||||
<a href="#" className="flex items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors no-underline">
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/settings?tab=profile') }}
|
||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors cursor-pointer border-none bg-transparent text-left"
|
||||
>
|
||||
<User size={14} />
|
||||
<span>Profile</span>
|
||||
</a>
|
||||
<a href="#" className="flex items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors no-underline">
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/settings?tab=appearance') }}
|
||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors cursor-pointer border-none bg-transparent text-left"
|
||||
>
|
||||
<Palette size={14} />
|
||||
<span>Appearance</span>
|
||||
</a>
|
||||
<a href="#" className="flex items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors no-underline">
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/settings?tab=security') }}
|
||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors cursor-pointer border-none bg-transparent text-left"
|
||||
>
|
||||
<Shield size={14} />
|
||||
<span>Security</span>
|
||||
</a>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/help') }}
|
||||
className="flex w-full items-center gap-2.5 px-3 py-2 text-[12px] text-text-secondary hover:text-text-primary hover:bg-page transition-colors cursor-pointer border-none bg-transparent text-left"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { api, ApiError, type Integration } from '../lib/api'
|
||||
import { useAuth } from '../lib/AuthContext'
|
||||
import {
|
||||
|
|
@ -17,11 +18,13 @@ import {
|
|||
Camera,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Shield,
|
||||
} from 'lucide-react'
|
||||
|
||||
const navSections = [
|
||||
{ id: 'profile', label: 'Profile', icon: User },
|
||||
{ id: 'appearance', label: 'Appearance', icon: Palette },
|
||||
{ id: 'security', label: 'Security', icon: Shield },
|
||||
{ id: 'integrations', label: 'Integrations', icon: Plug },
|
||||
{ id: 'notifications', label: 'Notifications', icon: Bell },
|
||||
{ id: 'data', label: 'Data & Backup', icon: Database },
|
||||
|
|
@ -1254,9 +1257,21 @@ function AboutSection() {
|
|||
)
|
||||
}
|
||||
|
||||
function SecuritySection() {
|
||||
return (
|
||||
<div style={cardBase}>
|
||||
<h3 style={sectionTitle}>Security</h3>
|
||||
<p style={{ fontSize: '13px', color: '#7A7D85' }}>
|
||||
Password changes, active sessions, login activity, and SSO are coming soon.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const sectionComponents: Record<string, () => React.ReactElement> = {
|
||||
profile: ProfileSection,
|
||||
appearance: AppearanceSection,
|
||||
security: SecuritySection,
|
||||
integrations: IntegrationsSection,
|
||||
notifications: NotificationsSection,
|
||||
data: DataBackupSection,
|
||||
|
|
@ -1264,9 +1279,15 @@ const sectionComponents: Record<string, () => React.ReactElement> = {
|
|||
}
|
||||
|
||||
export default function Settings() {
|
||||
const [active, setActive] = useState('profile')
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const requestedTab = searchParams.get('tab')
|
||||
const active = requestedTab && sectionComponents[requestedTab] ? requestedTab : 'profile'
|
||||
const ActiveSection = sectionComponents[active]
|
||||
|
||||
function setActive(id: string) {
|
||||
setSearchParams({ tab: id })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full gap-5">
|
||||
{/* Settings nav */}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue