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>
|
<p className="text-[10px] text-text-secondary">{user?.email || user?.username}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-1">
|
<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} />
|
<User size={14} />
|
||||||
<span>Profile</span>
|
<span>Profile</span>
|
||||||
</a>
|
</button>
|
||||||
<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=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} />
|
<Palette size={14} />
|
||||||
<span>Appearance</span>
|
<span>Appearance</span>
|
||||||
</a>
|
</button>
|
||||||
<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=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} />
|
<Shield size={14} />
|
||||||
<span>Security</span>
|
<span>Security</span>
|
||||||
</a>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setUserMenuOpen(false); navigate('/help') }}
|
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"
|
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 { useEffect, useRef, useState } from 'react'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
import { api, ApiError, type Integration } from '../lib/api'
|
import { api, ApiError, type Integration } from '../lib/api'
|
||||||
import { useAuth } from '../lib/AuthContext'
|
import { useAuth } from '../lib/AuthContext'
|
||||||
import {
|
import {
|
||||||
|
|
@ -17,11 +18,13 @@ import {
|
||||||
Camera,
|
Camera,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
|
Shield,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
const navSections = [
|
const navSections = [
|
||||||
{ id: 'profile', label: 'Profile', icon: User },
|
{ id: 'profile', label: 'Profile', icon: User },
|
||||||
{ id: 'appearance', label: 'Appearance', icon: Palette },
|
{ id: 'appearance', label: 'Appearance', icon: Palette },
|
||||||
|
{ id: 'security', label: 'Security', icon: Shield },
|
||||||
{ id: 'integrations', label: 'Integrations', icon: Plug },
|
{ id: 'integrations', label: 'Integrations', icon: Plug },
|
||||||
{ id: 'notifications', label: 'Notifications', icon: Bell },
|
{ id: 'notifications', label: 'Notifications', icon: Bell },
|
||||||
{ id: 'data', label: 'Data & Backup', icon: Database },
|
{ 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> = {
|
const sectionComponents: Record<string, () => React.ReactElement> = {
|
||||||
profile: ProfileSection,
|
profile: ProfileSection,
|
||||||
appearance: AppearanceSection,
|
appearance: AppearanceSection,
|
||||||
|
security: SecuritySection,
|
||||||
integrations: IntegrationsSection,
|
integrations: IntegrationsSection,
|
||||||
notifications: NotificationsSection,
|
notifications: NotificationsSection,
|
||||||
data: DataBackupSection,
|
data: DataBackupSection,
|
||||||
|
|
@ -1264,9 +1279,15 @@ const sectionComponents: Record<string, () => React.ReactElement> = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Settings() {
|
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]
|
const ActiveSection = sectionComponents[active]
|
||||||
|
|
||||||
|
function setActive(id: string) {
|
||||||
|
setSearchParams({ tab: id })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full gap-5">
|
<div className="flex h-full w-full gap-5">
|
||||||
{/* Settings nav */}
|
{/* Settings nav */}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue