mirror of
https://gh-proxy.org/https://github.com/klinsmaya/portal-template-default.git
synced 2026-11-08 00:57:57 +08:00
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:
+3
-3
@@ -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>
|
||||
|
||||
@@ -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
@@ -73,7 +73,7 @@ function App() {
|
||||
|
||||
<Toaster />
|
||||
<UnsavedChangesNotifier />
|
||||
<DocumentTitleHandler appName="NocoBase" />
|
||||
<DocumentTitleHandler appName="定三责" />
|
||||
</Refine>
|
||||
</AclStoreProvider>
|
||||
</SystemSettingsProvider>
|
||||
|
||||
+2
-3
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "无权访问此门户",
|
||||
|
||||
Reference in New Issue
Block a user