fix: stabilize kumo ui components

This commit is contained in:
2026-07-02 18:00:58 -07:00
parent c181e6fbdd
commit 381233c675
10 changed files with 685 additions and 54 deletions

View File

@@ -25,6 +25,26 @@ const modeCopy: Record<AuthMode, { title: string; badge: string; submit: string
setup: { title: "初始化机构", badge: "First run", submit: "完成初始化" },
};
async function readApiResult<T extends Record<string, unknown>>(response: Response): Promise<ApiResult<T>> {
const text = await response.text();
if (!text) {
return {
success: false,
reason: response.ok ? "服务器返回了空响应" : `请求失败:${response.status}`,
};
}
try {
return JSON.parse(text) as ApiResult<T>;
} catch {
return {
success: false,
reason: response.ok ? "服务器返回了无法解析的数据" : `请求失败:${response.status}`,
};
}
}
function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
const router = useRouter();
const searchParams = useSearchParams();
@@ -48,11 +68,14 @@ function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
fetch("/api/auth/session"),
fetch("/api/organizations"),
]);
const bootstrap = (await bootstrapResponse.json()) as ApiResult<{ setupRequired: boolean }>;
const session = (await sessionResponse.json()) as ApiResult<{ account: unknown | null }>;
const organizationsResult = (await organizationsResponse.json()) as ApiResult<{ organizations: Organization[] }>;
const bootstrap = await readApiResult<{ setupRequired: boolean }>(bootstrapResponse);
const session = await readApiResult<{ account: unknown | null }>(sessionResponse);
const organizationsResult = await readApiResult<{ organizations: Organization[] }>(organizationsResponse);
if (!isMounted || !bootstrap.success || !session.success) {
if (isMounted) {
setMessage(!bootstrap.success ? bootstrap.reason : session.reason);
}
return;
}
@@ -122,7 +145,7 @@ function AuthPanelContent({ mode }: AuthPanelProps): React.ReactElement {
invitationToken,
}),
});
const result = (await response.json()) as ApiResult<{ account: unknown; pendingApproval?: boolean }>;
const result = await readApiResult<{ account: unknown; pendingApproval?: boolean }>(response);
setIsPending(false);
if (!result.success) {

View File

@@ -5,8 +5,13 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
export function SignOutButton(): React.ReactElement {
type SignOutButtonProps = {
className?: string;
};
export function SignOutButton({ className }: SignOutButtonProps): React.ReactElement {
const router = useRouter();
const [isPending, setIsPending] = useState(false);
@@ -25,6 +30,7 @@ export function SignOutButton(): React.ReactElement {
onClick={handleSignOut}
disabled={isPending}
aria-label="退出登录"
className={cn(className)}
>
<LogOut className="size-4" aria-hidden="true" />
<span className="hidden sm:inline">退</span>