Brand the Portal as 自驱战略 · 定三责 and close self sign-up

- Seal-and-wordmark brand, favicon, document title and header copy.
- Chinese sign-in, forgot-password and sign-in method messages; the login
  hero shows the three 责 stages from the shared catalog.
- Accounts are provisioned by 运营管理: the sign-in form no longer links to
  sign-up and /register redirects to /login.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MMDFmVTvRksi1ZT8aPGXoL
This commit is contained in:
Claude
2026-10-09 15:11:25 +00:00
parent 8a65c1e688
commit 9dba4ceba2
13 changed files with 85 additions and 145 deletions
+3 -3
View File
@@ -2,12 +2,12 @@
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#171717" />
<meta
name="description"
content="NocoBase administration console"
content="自驱战略 · 定三责 企业战略咨询平台"
/>
<meta
data-rh="true"
@@ -53,7 +53,7 @@
}
}
</style>
<title>NocoBase</title>
<title>自驱战略 · 定三责</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#c8a27a"/><text x="32" y="45" text-anchor="middle" font-family="'Noto Serif SC','Songti SC',serif" font-size="38" font-weight="700" fill="#0c3d52">责</text></svg>

After

Width:  |  Height:  |  Size: 267 B

+1 -1
View File
@@ -73,7 +73,7 @@ function App() {
<Toaster />
<UnsavedChangesNotifier />
<DocumentTitleHandler appName="NocoBase" />
<DocumentTitleHandler appName="定三责" />
</Refine>
</AclStoreProvider>
</SystemSettingsProvider>
+2 -3
View File
@@ -1,6 +1,6 @@
import { Authenticated } from "@refinedev/core";
import { CatchAllNavigate } from "@refinedev/react-router";
import { Outlet, Route, Routes } from "react-router";
import { Navigate, Outlet, Route, Routes } from "react-router";
import { AclBootstrap } from "@/components/access-control/acl-bootstrap";
import { NavigateToAccessibleResource } from "@/components/access-control/navigate-to-accessible-resource";
@@ -8,7 +8,6 @@ import { ErrorComponent } from "@/components/app-shell/error-component";
import { Layout } from "@/components/app-shell/layout";
import { ForgotPassword } from "@/pages/forgot-password";
import { Login } from "@/pages/login";
import { Register } from "@/pages/register";
import {
AppExtensionProviders,
configuredRouteElements,
@@ -57,7 +56,7 @@ export function AppRoutes() {
>
<Route path="/login" element={<Login />} />
<Route path="/signin" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/register" element={<Navigate to="/login" replace />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
</Route>
</Routes>
+17 -29
View File
@@ -1,52 +1,40 @@
import { assetUrl, cn } from "@/lib/utils";
import { cn } from "@/lib/utils";
type BrandLogoProps = {
className?: string;
};
/** The 定三责 seal: a gold “责” on the brand colour. */
export function BrandLogo({ className }: BrandLogoProps) {
return (
<span
aria-hidden
className={cn(
"inline-flex size-10 shrink-0 items-center justify-center overflow-hidden",
className
"inline-flex size-10 shrink-0 items-center justify-center rounded-lg bg-gold font-heading text-lg font-bold text-brand dark:text-brand-foreground",
className,
)}
>
<img
src={assetUrl("/logo-mark.png")}
alt=""
className="size-full object-contain dark:hidden"
/>
<img
src={assetUrl("/logo-mark-dark.png")}
alt=""
className="hidden size-full object-contain dark:block"
/>
责
</span>
);
}
export function BrandWordmark({ className }: BrandLogoProps) {
return (
<span
className={cn(
"inline-flex h-8 shrink-0 items-center overflow-hidden",
className
)}
>
<img
src={assetUrl("/logo.png")}
alt="NocoBase"
className="h-full w-auto object-contain dark:hidden"
/>
<img
src={assetUrl("/logo-dark.png")}
alt="NocoBase"
className="hidden h-full w-auto object-contain dark:block"
/>
<span className={cn("inline-flex min-w-0 items-center gap-2.5", className)}>
<BrandLogo className="size-9" />
<span className="flex min-w-0 flex-col leading-tight">
<span className="truncate font-heading text-base font-bold tracking-wide text-brand dark:text-foreground">
自驱战略 · 定三责
</span>
<span className="truncate text-[11px] text-muted-foreground">
企业战略咨询平台
</span>
</span>
</span>
);
}
type BrandProps = {
className?: string;
logoClassName?: string;
-8
View File
@@ -307,14 +307,6 @@ function SidebarFooter() {
{translate("shell.footer.freedom", "AI builds freely.")}
</div>
<div className="text-muted-foreground">
<a
href="https://nocobase.com"
target="_blank"
rel="noopener noreferrer"
className="font-medium text-sidebar-foreground hover:underline"
>
NocoBase
</a>{" "}
{translate(
"shell.footer.reliabilitySuffix",
"keeps it reliable."
+29 -40
View File
@@ -1,5 +1,5 @@
import type { PropsWithChildren, ReactNode } from "react";
import { Blocks, ShieldCheck, Sparkles } from "lucide-react";
import { STAGES } from "@dingze/shared";
import { Brand } from "@/components/app-shell/brand";
@@ -31,50 +31,39 @@ export function AuthLayout({
</div>
</main>
<section className="relative hidden overflow-hidden bg-neutral-950 p-12 text-white md:grid md:place-items-center">
<div className="absolute inset-0 opacity-[0.08] [background-image:linear-gradient(currentColor_1px,transparent_1px),linear-gradient(90deg,currentColor_1px,transparent_1px)] [background-size:48px_48px]" />
<section className="relative hidden overflow-hidden bg-brand p-12 text-brand-foreground md:grid md:place-items-center">
<div className="absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-black/20 to-transparent" />
<div className="relative w-full max-w-xl">
<div className="text-xs font-semibold uppercase tracking-[0.14em] text-primary-foreground/60">
AI-native application platform
<div className="text-xs font-semibold tracking-[0.3em] text-gold">
企业战略咨询平台
</div>
<h2 className="mt-3 max-w-lg text-5xl font-semibold leading-[1.08] tracking-[-0.04em]">
Let AI build freely. NocoBase keeps it reliable.
<h2 className="mt-4 font-heading text-5xl font-bold leading-[1.15] tracking-wide">
自驱战略 · 定三责
</h2>
<p className="mt-5 max-w-lg text-base leading-7 text-white/60">
Give AI a flexible frontend framework to shape each experience,
while NocoBase secures the data, permissions, workflows and
governance underneath.
<p className="mt-5 max-w-lg text-base leading-7 text-brand-foreground/75">
咨询师与企业团队在同一张表上达成共识:战略说得清,目标对得上,行动落到人。
</p>
<div className="mt-10 max-w-md rounded-xl border border-white/15 bg-white p-5 text-neutral-900 shadow-2xl shadow-black/20">
<div className="flex items-center gap-3">
<div className="grid size-10 place-items-center rounded-lg bg-neutral-100">
<Sparkles className="size-4" />
</div>
<div>
<div className="font-semibold">AI-native frontend</div>
<div className="text-sm text-neutral-500">
Compose interfaces freely on a flexible framework.
</div>
</div>
</div>
<div className="my-5 h-px bg-neutral-200" />
<div className="flex items-center gap-3">
<div className="grid size-10 place-items-center rounded-lg bg-neutral-100">
<ShieldCheck className="size-4" />
</div>
<div>
<div className="font-semibold">NocoBase foundation</div>
<div className="text-sm text-neutral-500">
Reliable data, access control, workflows and governance.
</div>
</div>
</div>
<div className="mt-5 flex items-center gap-2 rounded-lg bg-neutral-100 px-3 py-2 text-xs font-medium text-neutral-600">
<Blocks className="size-3.5" />
Freedom above. Confidence below.
</div>
</div>
<ol className="mt-10 flex max-w-md flex-col gap-3">
{STAGES.map((stage, index) => (
<li
key={stage.key}
className="flex items-center gap-4 rounded-xl border border-white/15 bg-white/5 px-5 py-4"
>
<span className="grid size-10 shrink-0 place-items-center rounded-lg bg-gold font-heading text-lg font-bold text-brand dark:text-brand-foreground">
{index + 1}
</span>
<span>
<span className="block font-heading text-lg font-bold">
{stage.name}
</span>
<span className="text-sm text-brand-foreground/70">
{stage.goal}
</span>
</span>
</li>
))}
</ol>
</div>
</section>
</div>
+15 -36
View File
@@ -22,7 +22,6 @@ export function BasicSignInForm({
const [password, setPassword] = useState("");
const Link = useLink();
const { mutate: login, isPending } = useLogin<LoginVariables>();
const allowSignUp = authenticator.options?.allowSignUp === true;
const enableResetPassword =
authenticator.options?.enableResetPassword === true;
@@ -34,9 +33,7 @@ export function BasicSignInForm({
return (
<form onSubmit={handleSignIn} className="space-y-5">
<div className="space-y-2">
<Label htmlFor={`${authenticator.name}-account`}>
Username or email
</Label>
<Label htmlFor={`${authenticator.name}-account`}>用户名或邮箱</Label>
<Input
id={`${authenticator.name}-account`}
type="text"
@@ -49,7 +46,7 @@ export function BasicSignInForm({
</div>
<div className="space-y-2">
<Label htmlFor={`${authenticator.name}-password`}>Password</Label>
<Label htmlFor={`${authenticator.name}-password`}>密码</Label>
<InputPassword
id={`${authenticator.name}-password`}
value={password}
@@ -59,39 +56,21 @@ export function BasicSignInForm({
/>
</div>
<Button
type="submit"
className="w-full"
disabled={isPending}
>
{isPending ? "Signing in…" : "Sign in"}
<Button type="submit" className="w-full" disabled={isPending}>
{isPending ? "登录中…" : "登录"}
</Button>
{(allowSignUp || enableResetPassword) && (
<div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2 text-sm text-muted-foreground">
{enableResetPassword ? (
<Link
to={`/forgot-password?name=${encodeURIComponent(
authenticator.name
)}`}
className="transition-colors hover:text-foreground hover:underline hover:underline-offset-4"
>
Forgot password?
</Link>
) : (
<span />
)}
{allowSignUp && (
<span>
No account?{" "}
<Link
to={`/register?name=${encodeURIComponent(authenticator.name)}`}
className="font-semibold text-foreground underline underline-offset-4"
>
Sign up
</Link>
</span>
)}
{/* Accounts are provisioned by 运营管理; self sign-up stays closed in this Portal. */}
{enableResetPassword && (
<div className="text-sm text-muted-foreground">
<Link
to={`/forgot-password?name=${encodeURIComponent(
authenticator.name
)}`}
className="transition-colors hover:text-foreground hover:underline hover:underline-offset-4"
>
忘记密码?
</Link>
</div>
)}
</form>
+1 -4
View File
@@ -11,10 +11,7 @@ export function DefaultSignInPage({
renderAuthenticator,
}: DefaultSignInPageProps) {
return (
<AuthLayout
title="Welcome back"
description="Choose a sign-in method configured in NocoBase."
>
<AuthLayout title="登录" description="使用咨询机构为你开通的账号登录。">
<DynamicSignIn renderAuthenticator={renderAuthenticator} />
</AuthLayout>
);
+4 -5
View File
@@ -107,7 +107,7 @@ export function DynamicSignIn({
return (
<Alert variant="destructive">
<AlertCircle />
<AlertTitle>Unable to load sign-in methods</AlertTitle>
<AlertTitle>登录方式加载失败</AlertTitle>
<AlertDescription>
{import.meta.env.DEV && error instanceof Error
? error.message
@@ -127,10 +127,9 @@ export function DynamicSignIn({
<EmptyMedia variant="icon">
<KeyRound />
</EmptyMedia>
<EmptyTitle>No supported sign-in methods</EmptyTitle>
<EmptyTitle>暂无可用的登录方式</EmptyTitle>
<EmptyDescription>
No sign-in method is currently available. Contact your
administrator for access.
当前没有可用的登录方式,请联系咨询机构的运营管理员。
</EmptyDescription>
</EmptyHeader>
</Empty>
@@ -174,7 +173,7 @@ export function DynamicSignIn({
<div className="flex items-center gap-4 py-1">
<Separator className="flex-1" />
<span className="text-xs text-muted-foreground">
Or continue with
或使用以下方式
</span>
<Separator className="flex-1" />
</div>
+6 -10
View File
@@ -26,21 +26,21 @@ export const ForgotPasswordForm = () => {
return (
<AuthLayout
title="Forgot password"
description="Enter your email to reset your password."
title="找回密码"
description="输入账号绑定的邮箱,我们会发送重置链接。"
footer={
<Link
to="/login"
className="inline-flex items-center gap-2 text-muted-foreground transition-colors hover:text-foreground"
>
<ArrowLeft className="size-4" />
Back to sign in
返回登录
</Link>
}
>
<form onSubmit={handleForgotPassword} className="space-y-5">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Label htmlFor="email">邮箱</Label>
<Input
id="email"
type="email"
@@ -51,12 +51,8 @@ export const ForgotPasswordForm = () => {
required
/>
</div>
<Button
type="submit"
className="w-full"
disabled={isPending}
>
{isPending ? "Sending…" : "Send reset link"}
<Button type="submit" className="w-full" disabled={isPending}>
{isPending ? "发送中…" : "发送重置链接"}
</Button>
</form>
</AuthLayout>
+3 -3
View File
@@ -1,8 +1,8 @@
export const starter = {
"shell.workspace": "AI application workspace",
"shell.workspace": "Strategy consulting platform",
"shell.settings": "Settings",
"shell.footer.freedom": "AI builds freely.",
"shell.footer.reliabilitySuffix": "keeps it reliable.",
"shell.footer.freedom": "Strategy · Goals · Actions",
"shell.footer.reliabilitySuffix": "Every responsibility has an owner.",
"auth.signOut": "Sign out",
"auth.signingOut": "Signing out...",
"acl.portalAccessDenied.title": "You do not have access to this Portal",
+3 -3
View File
@@ -1,8 +1,8 @@
export const starter = {
"shell.workspace": "AI 应用工作区",
"shell.workspace": "企业战略咨询平台",
"shell.settings": "设置",
"shell.footer.freedom": "AI 自由构建。",
"shell.footer.reliabilitySuffix": "保障可靠运行。",
"shell.footer.freedom": "定战略责 · 定目标责 · 定行动责",
"shell.footer.reliabilitySuffix": "上下同欲,责任到人",
"auth.signOut": "退出登录",
"auth.signingOut": "正在退出...",
"acl.portalAccessDenied.title": "无权访问此门户",