diff --git a/.trellis/spec/frontend/components.md b/.trellis/spec/frontend/components.md index 84bcb7c..45bc9f0 100644 --- a/.trellis/spec/frontend/components.md +++ b/.trellis/spec/frontend/components.md @@ -155,8 +155,13 @@ The project Dialog adapter must: - Override Kumo minimum width so mobile dialogs stay inside `100vw`. - Keep `transform: none` and `translate: 0 0` on `.teatea-dialog`; use opacity/scale-only motion if animation is needed. +- Kumo may inject `-translate-x-1/2 -translate-y-1/2` before project classes in the + runtime class list. Do not rely only on Tailwind class order or a plain CSS rule; + the Dialog adapter should enforce `style={{ transform: "none", translate: "0 0" }}` + or an equally strong adapter-owned override. - Browser validation should assert the panel center is within 2px of the viewport center - on desktop and mobile. + on desktop and mobile and should inspect computed `translate` when a dialog is visibly + offset. ### Static Module Data Contract diff --git a/app/(app)/app/settings/global/page.tsx b/app/(app)/app/settings/global/page.tsx index e43aecc..34f618f 100644 --- a/app/(app)/app/settings/global/page.tsx +++ b/app/(app)/app/settings/global/page.tsx @@ -17,10 +17,11 @@ export default async function GlobalSettingsPage(): Promise } const settings = await getSystemSettings(); + const organizationSettingsHref = getWorkspaceHref(context.organization?.slug, "/settings/organizations"); return (
- +
); } diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 97b082b..5006e56 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -40,12 +40,13 @@ export function Dialog({ >
diff --git a/modules/settings/components/GlobalSettingsClient.tsx b/modules/settings/components/GlobalSettingsClient.tsx index 277b305..deaf8b9 100644 --- a/modules/settings/components/GlobalSettingsClient.tsx +++ b/modules/settings/components/GlobalSettingsClient.tsx @@ -1,11 +1,11 @@ "use client"; import { FormEvent, useState } from "react"; -import { Globe2, Save, ShieldCheck } from "lucide-react"; +import { Building2, Globe2, KeyRound, Save, ShieldCheck, UserPlus } from "lucide-react"; import { useRouter } from "next/navigation"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import { Button, LinkButton } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; @@ -13,17 +13,22 @@ import type { ApiResult } from "@/modules/core/server/api"; import type { SystemSettings } from "@/modules/core/types"; type GlobalSettingsClientProps = { + organizationSettingsHref: string; settings: SystemSettings; }; -type SettingsTab = "registration" | "oidc"; +type SettingsTab = "registration" | "invitation" | "oidc"; const tabs: Array<{ label: string; value: SettingsTab }> = [ { label: "注册入口", value: "registration" }, + { label: "邀请注册", value: "invitation" }, { label: "OIDC 默认配置", value: "oidc" }, ]; -export function GlobalSettingsClient({ settings }: GlobalSettingsClientProps): React.ReactElement { +export function GlobalSettingsClient({ + organizationSettingsHref, + settings, +}: GlobalSettingsClientProps): React.ReactElement { const router = useRouter(); const [activeTab, setActiveTab] = useState("registration"); const [registrationEnabled, setRegistrationEnabled] = useState(settings.registrationEnabled); @@ -135,6 +140,58 @@ export function GlobalSettingsClient({ settings }: GlobalSettingsClientProps): R
+