feat(web): add header nav to landing page and local dev improvements

- Add sticky header with LanguageSwitcher and UserMenu to landing page
- Update token dialog and i18n translations
- Fix dev process script and compose release config
- Remove stale skillhub submodule reference
This commit is contained in:
yun-zhi-ztl 2026-03-14 17:32:37 +08:00
parent cfc296181f
commit fd790a9610
6 changed files with 50 additions and 11 deletions

View file

@ -7,6 +7,7 @@ import subprocess
import sys
import time
from pathlib import Path
from typing import Optional
def is_running(pid: int) -> bool:
@ -17,7 +18,7 @@ def is_running(pid: int) -> bool:
return True
def read_pid(pid_file: Path) -> int | None:
def read_pid(pid_file: Path) -> Optional[int]:
if not pid_file.exists():
return None
content = pid_file.read_text(encoding="utf-8").strip()

@ -1 +0,0 @@
Subproject commit a3c66108c6750a44f703e227ade992ee7715d099

View file

@ -14,6 +14,7 @@ import {
import { Button } from '@/shared/ui/button'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { toast } from '@/shared/lib/toast'
import type { CreateTokenRequest, CreateTokenResponse } from '@/api/types'
interface CreateTokenDialogProps {
@ -48,6 +49,18 @@ export function CreateTokenDialog({ children }: CreateTokenDialogProps) {
createMutation.reset()
}
const handleCopyToken = async () => {
if (!createdToken) return
try {
await navigator.clipboard.writeText(createdToken.token)
toast.success(t('createToken.copySuccess'))
} catch (error) {
console.error('Failed to copy token:', error)
toast.error(t('createToken.copyFailed'))
}
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>{children}</DialogTrigger>
@ -109,11 +122,7 @@ export function CreateTokenDialog({ children }: CreateTokenDialogProps) {
</div>
</div>
<DialogFooter>
<Button
onClick={() => {
navigator.clipboard.writeText(createdToken.token)
}}
>
<Button onClick={handleCopyToken}>
{t('createToken.copyToken')}
</Button>
<Button variant="outline" onClick={handleClose}>

View file

@ -399,7 +399,9 @@
"successDescription": "Copy and save this token now. It will only be shown once.",
"tokenLabel": "Token",
"nameDisplay": "Name",
"copyToken": "Copy Token"
"copyToken": "Copy Token",
"copySuccess": "Token copied to clipboard",
"copyFailed": "Failed to copy token. Please try again."
},
"starButton": {
"starred": "Starred",

View file

@ -167,7 +167,7 @@
"viewStars": "查看我的收藏",
"credentials": "访问凭证",
"openTokens": "打开 Token 页面",
"governanceTitle": "查看提交审核",
"governanceTitle": "审核与治理",
"viewPromotions": "查看提升审核"
},
"mySkills": {
@ -399,7 +399,9 @@
"successDescription": "请立即复制并保存此 Token,它只会显示一次",
"tokenLabel": "Token",
"nameDisplay": "名称",
"copyToken": "复制 Token"
"copyToken": "复制 Token",
"copySuccess": "Token 已复制到剪贴板",
"copyFailed": "复制 Token 失败,请重试"
},
"starButton": {
"starred": "已收藏",

View file

@ -1,12 +1,16 @@
import { useNavigate } from '@tanstack/react-router'
import { Link, useNavigate } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import { SearchBar } from '@/features/search/search-bar'
import { useAuth } from '@/features/auth/use-auth'
import { LanguageSwitcher } from '@/shared/components/language-switcher'
import { UserMenu } from '@/shared/components/user-menu'
import { Button } from '@/shared/ui/button'
import { useEffect, useRef, useState } from 'react'
export function LandingPage() {
const { t } = useTranslation()
const navigate = useNavigate()
const { user, isLoading } = useAuth()
const canvasRef = useRef<HTMLCanvasElement>(null)
const [stats] = useState({
skills: '1000+',
@ -146,6 +150,28 @@ export function LandingPage() {
return (
<div className="relative min-h-screen overflow-hidden bg-gradient-to-br from-slate-950 via-indigo-950 to-slate-900">
<header className="sticky top-0 z-50 border-b border-slate-700/30 bg-slate-950/60 backdrop-blur-lg">
<div className="container mx-auto flex items-center justify-between px-6 py-3">
<Link to="/" className="text-lg font-bold text-transparent bg-clip-text bg-gradient-to-r from-cyan-300 to-blue-400">
SkillHub
</Link>
<nav className="flex items-center gap-4">
<LanguageSwitcher />
{isLoading ? null : user ? (
<UserMenu user={user} />
) : (
<Link
to="/login"
search={{ returnTo: '' }}
className="text-sm text-slate-300 hover:text-cyan-400 transition-colors"
>
{t('nav.login')}
</Link>
)}
</nav>
</div>
</header>
<canvas
ref={canvasRef}
className="absolute inset-0 pointer-events-none"