Skip to content

Commit b2724b2

Browse files
authored
feat(admin): enhance operations dashboards
Squash merge PR #79 after resolving conflicts and verifying checks.
1 parent b1e6ee1 commit b2724b2

30 files changed

Lines changed: 4202 additions & 1373 deletions

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "2.4.1"
2+
".": "2.4.2"
33
}

client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "monolith-client",
3-
"version": "2.4.1",
3+
"version": "2.4.2",
44
"private": true,
55
"type": "module",
66
"scripts": {

client/src/components/admin-gate.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function AdminGate({
121121
<div className="overflow-hidden rounded-md border border-border/35 bg-card/95 shadow-[0_28px_90px_oklch(0_0_0_/_38%)] backdrop-blur-xl">
122122
<div className="flex items-center justify-between border-b border-border/20 px-[18px] py-[14px]">
123123
<div className="flex items-center gap-[10px]">
124-
<div className="flex h-[32px] w-[32px] items-center justify-center rounded-md border border-cyan-400/20 bg-cyan-400/10 text-cyan-300">
124+
<div className="flex h-[32px] w-[32px] items-center justify-center rounded-md border border-foreground/16 bg-foreground/[0.06] text-foreground/72">
125125
<ShieldCheck className="h-[16px] w-[16px]" />
126126
</div>
127127
<div>
@@ -191,7 +191,7 @@ export function AdminGate({
191191
placeholder="输入密码"
192192
autoComplete="current-password"
193193
aria-label="管理员密码"
194-
className="h-[46px] w-full rounded-md border border-border/45 bg-background/55 px-[14px] pr-[48px] text-[15px] text-foreground outline-none transition-all placeholder:text-muted-foreground/35 focus:border-cyan-400/45 focus:ring-1 focus:ring-cyan-400/20"
194+
className="h-[46px] w-full rounded-md border border-border/45 bg-background/55 px-[14px] pr-[48px] text-[15px] text-foreground outline-none transition-all placeholder:text-muted-foreground/35 focus:border-foreground/35 focus:ring-1 focus:ring-foreground/12"
195195
/>
196196

197197
{error && (

client/src/components/admin-layout.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,26 @@ interface AdminLayoutProps {
2424

2525
const NAV_GROUPS = [
2626
{
27-
title: "内容管理",
27+
title: "内容运营",
2828
items: [
29-
{ href: "/admin", icon: LayoutDashboard, label: "控制台" },
30-
{ href: "/admin/pages", icon: StickyNote, label: "独立页面" },
31-
{ href: "/admin/comments", icon: MessageCircle, label: "评论审核" },
29+
{ href: "/admin", icon: LayoutDashboard, label: "运营总览" },
30+
{ href: "/admin/pages", icon: StickyNote, label: "页面管理" },
31+
{ href: "/admin/comments", icon: MessageCircle, label: "互动审核" },
3232
],
3333
},
3434
{
35-
title: "资源与数据",
35+
title: "增长工具",
3636
items: [
37-
{ href: "/admin/media", icon: ImageIcon, label: "媒体库" },
38-
{ href: "/admin/analytics", icon: BarChart3, label: "数据分析" },
39-
{ href: "/admin/seo", icon: Sparkles, label: "SEO 优化" },
40-
{ href: "/admin/backup", icon: HardDrive, label: "安全备份" },
37+
{ href: "/admin/media", icon: ImageIcon, label: "媒体资产" },
38+
{ href: "/admin/analytics", icon: BarChart3, label: "运营洞察" },
39+
{ href: "/admin/seo", icon: Sparkles, label: "搜索优化" },
40+
{ href: "/admin/backup", icon: HardDrive, label: "备份恢复" },
4141
],
4242
},
4343
{
44-
title: "系统配置",
44+
title: "系统维护",
4545
items: [
46-
{ href: "/admin/settings", icon: Settings, label: "站点设置" },
46+
{ href: "/admin/settings", icon: Settings, label: "站点配置" },
4747
],
4848
},
4949
];
@@ -113,7 +113,7 @@ export function AdminLayout({ children }: AdminLayoutProps) {
113113
</div>
114114
<div>
115115
<span className="block font-heading text-[17px] font-semibold tracking-[-0.02em]">Monolith</span>
116-
<span className="block text-[10px] text-muted-foreground/45">Admin Console</span>
116+
<span className="block text-[10px] text-muted-foreground/45">Operations Desk</span>
117117
</div>
118118
</Link>
119119
<label className="mt-[14px] flex min-h-[44px] items-center gap-[8px] rounded-md border border-border/25 bg-background/45 px-[12px] text-[13px] text-foreground shadow-[0_8px_24px_oklch(0_0_0_/_10%)] transition-colors focus-within:border-foreground/25 focus-within:bg-background/65 focus-within:ring-1 focus-within:ring-foreground/10">
@@ -163,7 +163,7 @@ export function AdminLayout({ children }: AdminLayoutProps) {
163163
: "text-muted-foreground/55 hover:bg-muted/35 hover:text-foreground/85"
164164
}`}
165165
>
166-
{isActive && <span className="absolute left-[4px] top-1/2 h-[18px] w-[2px] -translate-y-1/2 rounded-full bg-cyan-300/80" />}
166+
{isActive && <span className="absolute left-[4px] top-1/2 h-[18px] w-[2px] -translate-y-1/2 rounded-full bg-background/75" />}
167167
<item.icon className="w-[14px] h-[14px]" />
168168
{item.label}
169169
</Link>
@@ -237,7 +237,7 @@ export function AdminLayout({ children }: AdminLayoutProps) {
237237

238238
<div className="hidden h-[56px] items-center justify-between border-b border-border/20 bg-background/72 px-[24px] backdrop-blur-xl md:flex">
239239
<div className="flex items-center gap-[8px]">
240-
<span className="font-heading text-[13px] font-medium text-foreground/90">Monolith 管理后台</span>
240+
<span className="font-heading text-[13px] font-medium text-foreground/90">Monolith 运营后台</span>
241241
<span className="text-[12px] text-muted-foreground/25">/</span>
242242
<span className="font-heading text-[13px] font-semibold text-foreground/75">{currentTitle}</span>
243243
</div>

client/src/components/comments.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ function CommentForm({ slug, onSubmitted }: { slug: string; onSubmitted: () => v
7474
}
7575
};
7676

77-
const inputClass = "w-full rounded-md border border-border/40 bg-card/20 px-[12px] py-[8px] text-[14px] text-foreground placeholder:text-muted-foreground/40 outline-none transition-all duration-200 focus:border-blue-500/40 focus:ring-1 focus:ring-blue-500/20";
77+
const inputClass = "w-full rounded-md border border-border/40 bg-card/20 px-[12px] py-[8px] text-[14px] text-foreground placeholder:text-muted-foreground/40 outline-none transition-all duration-200 focus:border-foreground/35 focus:ring-1 focus:ring-foreground/12";
7878

7979
return (
8080
<form onSubmit={handleSubmit} className="space-y-[12px]">
@@ -134,7 +134,7 @@ function CommentForm({ slug, onSubmitted }: { slug: string; onSubmitted: () => v
134134
{message && (
135135
<div className={`rounded-md px-[12px] py-[8px] text-[13px] ${
136136
message.type === "success"
137-
? "bg-green-500/10 text-green-400 border border-green-500/20"
137+
? "border border-foreground/12 bg-foreground/[0.06] text-foreground/82"
138138
: "bg-red-500/10 text-red-400 border border-red-500/20"
139139
}`}>
140140
{message.text}
@@ -144,7 +144,7 @@ function CommentForm({ slug, onSubmitted }: { slug: string; onSubmitted: () => v
144144
<button
145145
type="submit"
146146
disabled={submitting || !authorName.trim() || !content.trim()}
147-
className="inline-flex items-center gap-[6px] rounded-md bg-blue-600/80 px-[16px] py-[8px] text-[13px] font-medium text-white transition-all duration-200 hover:bg-blue-600 disabled:opacity-40 disabled:cursor-not-allowed"
147+
className="inline-flex min-h-[44px] items-center gap-[6px] rounded-md bg-foreground px-[16px] py-[8px] text-[13px] font-medium text-background transition-all duration-200 hover:-translate-y-[2px] hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:translate-y-0 disabled:cursor-not-allowed disabled:opacity-40 sm:min-h-[36px]"
148148
>
149149
<Send className="h-[14px] w-[14px]" />
150150
{submitting ? "提交中..." : "发表评论"}
@@ -173,11 +173,12 @@ export function CommentsSection({ slug }: { slug: string }) {
173173

174174
return (
175175
<section className="mt-[40px] animate-fade-in delay-5">
176-
<div className="rounded-xl border border-border/40 bg-card/10 overflow-hidden transition-all duration-300">
176+
<div className="overflow-hidden rounded-md border border-border/32 bg-card/10 transition-all duration-300">
177177
<button
178178
onClick={() => setIsOpen(!isOpen)}
179-
className="w-full flex items-center justify-between p-[16px] md:px-[20px] bg-transparent hover:bg-card/30 transition-colors"
179+
className="flex min-h-[56px] w-full items-center justify-between bg-transparent p-[16px] text-left transition-colors hover:bg-card/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring md:px-[20px]"
180180
title={isOpen ? "收起评论区" : "展开评论区"}
181+
aria-expanded={isOpen}
181182
>
182183
<div className="flex items-center gap-[8px]">
183184
<MessageCircle className="h-[18px] w-[18px] text-muted-foreground/60" />

client/src/components/hero.tsx

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function Hero() {
1+
export function LegacyHero() {
22
return (
33
<section className="relative border-b border-border/20 py-[44px] sm:py-[56px] lg:py-[68px]">
44
<div className="pointer-events-none absolute inset-0 hero-grid opacity-45" />
@@ -40,3 +40,94 @@ export function Hero() {
4040
</section>
4141
);
4242
}
43+
44+
export type HeroTopic = {
45+
title: string;
46+
desc: string;
47+
};
48+
49+
export type HeroAction = {
50+
label: string;
51+
href: string;
52+
};
53+
54+
type HeroProps = {
55+
title?: string;
56+
kicker?: string;
57+
subtitle?: string;
58+
description?: string;
59+
actions?: HeroAction[];
60+
topics?: HeroTopic[];
61+
};
62+
63+
const DEFAULT_ACTIONS: HeroAction[] = [
64+
{ label: "最新文章", href: "#latest-posts" },
65+
{ label: "主题索引", href: "#content-index" },
66+
{ label: "工程笔记", href: "/archive" },
67+
];
68+
69+
const DEFAULT_TOPICS: HeroTopic[] = [
70+
{ title: "系统设计", desc: "从边界、接口和运维成本切入" },
71+
{ title: "阅读体验", desc: "让长文、代码与目录保持同一节奏" },
72+
{ title: "边缘部署", desc: "Workers / D1 / R2 的真实工程路径" },
73+
];
74+
75+
export function Hero({
76+
title = "Monolith",
77+
kicker = "EDGE JOURNAL / CODE ARCHIVE",
78+
subtitle = "技术写作、系统设计与边缘实践的索引页",
79+
description = "用更冷静的网格整理长期主题:前端架构、设计系统、边缘计算与工程排障。每一篇文章都尽量给出可复用的上下文,而不是只留下零散记录。",
80+
actions = DEFAULT_ACTIONS,
81+
topics = DEFAULT_TOPICS,
82+
}: HeroProps) {
83+
const visibleActions = actions.filter((item) => item.label.trim() && item.href.trim()).slice(0, 3);
84+
const visibleTopics = topics.filter((item) => item.title.trim() && item.desc.trim()).slice(0, 3);
85+
86+
return (
87+
<section className="relative border-b border-border/18 py-[40px] sm:py-[52px] lg:py-[64px]">
88+
<div className="pointer-events-none absolute inset-0 hero-grid opacity-35" />
89+
<div className="relative grid gap-[24px] lg:grid-cols-[minmax(0,1fr)_320px] lg:items-stretch">
90+
<div className="min-w-0 border-l border-border/35 pl-[16px] sm:pl-[20px]">
91+
<div className="mb-[20px] flex items-center gap-[12px]">
92+
<div className="min-w-0">
93+
<p className="font-mono text-[11px] text-muted-foreground/45">{kicker}</p>
94+
<p className="mt-[4px] text-[12px] text-muted-foreground/42">{subtitle}</p>
95+
</div>
96+
</div>
97+
98+
<h1 className="max-w-[820px] font-heading text-[42px] font-semibold leading-[0.96] tracking-[-0.045em] text-foreground sm:text-[60px] lg:text-[76px]">
99+
{title}
100+
</h1>
101+
<p className="mt-[20px] max-w-[660px] text-[16px] leading-[1.85] text-muted-foreground sm:text-[17px]">
102+
{description}
103+
</p>
104+
105+
<div className="mt-[24px] flex flex-wrap gap-[8px]">
106+
{visibleActions.map((item) => (
107+
<a
108+
key={`${item.label}-${item.href}`}
109+
href={item.href}
110+
className="inline-flex min-h-[44px] items-center rounded-md border border-border/18 bg-background/32 px-[12px] text-[13px] text-muted-foreground/72 transition-all duration-200 hover:-translate-y-[2px] hover:border-border/36 hover:bg-card/22 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring sm:min-h-[36px]"
111+
>
112+
{item.label}
113+
</a>
114+
))}
115+
</div>
116+
</div>
117+
118+
<div className="grid gap-[10px] rounded-md border border-border/20 bg-background/35 p-[14px] backdrop-blur-sm">
119+
<p className="font-mono text-[11px] text-muted-foreground/42">CURRENT THREADS</p>
120+
{visibleTopics.map((item) => (
121+
<div key={item.title} className="rounded-md border border-border/14 bg-card/[0.10] px-[12px] py-[10px]">
122+
<div className="flex items-center justify-between gap-[12px]">
123+
<span className="text-[13px] font-medium text-foreground/86">{item.title}</span>
124+
<span className="h-[6px] w-[6px] rounded-full bg-foreground/42" />
125+
</div>
126+
<p className="mt-[6px] text-[12px] leading-[1.6] text-muted-foreground/55">{item.desc}</p>
127+
</div>
128+
))}
129+
</div>
130+
</div>
131+
</section>
132+
);
133+
}

0 commit comments

Comments
 (0)