111 lines
3.8 KiB
Plaintext
111 lines
3.8 KiB
Plaintext
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { formatBytes } from "@/app/(main)/account/storage/format";
|
|
import { getStatusView } from "@/app/(main)/apps/deployment-status";
|
|
import { presentSessionCreateError } from "@/lib/api/balance-error";
|
|
import { type AppDetail, createAppSession, refreshAppQueries } from "@/lib/apps";
|
|
import { liveUrl as toLiveUrl } from "@/lib/deployments/live-url";
|
|
import { formatRelativeTime } from "@/lib/format/relative-time";
|
|
import { SESSION_ROUTE } from "@/lib/sessions";
|
|
|
|
import { SectionCard, SettingsRow } from "../SettingsRow";
|
|
import { sectionHref } from "../nav";
|
|
import { DeployTabLink } from "./DeployTabLink";
|
|
import { useResumeDeployment } from "./use-resume-deployment";
|
|
|
|
/** 概览: 一行一件事, 值在左、说明在下、次要动作靠右. */
|
|
export const OverviewSection = ({ detail }: { detail: AppDetail }) => {
|
|
const router = useRouter();
|
|
const queryClient = useQueryClient();
|
|
const deployment = detail.deployment ?? null;
|
|
const liveUrl = toLiveUrl(detail.custom_domain, deployment?.url);
|
|
|
|
const resume = useResumeDeployment(detail.id);
|
|
|
|
const newSession = useMutation({
|
|
mutationFn: () => createAppSession(detail.id),
|
|
onSuccess: (session) => {
|
|
void refreshAppQueries(queryClient, detail.id);
|
|
router.push(`${SESSION_ROUTE}?session_id=${session.id}`);
|
|
},
|
|
onError: (err) => presentSessionCreateError(err, router),
|
|
});
|
|
|
|
return (
|
|
<SectionCard title="概览">
|
|
<SettingsRow
|
|
label="线上地址"
|
|
value={
|
|
liveUrl ? (
|
|
<a href={liveUrl} target="_blank" rel="noreferrer" className="text-primary underline underline-offset-2">
|
|
{liveUrl.replace(/^https?:\/\//, "")}
|
|
</a>
|
|
) : (
|
|
"未发布"
|
|
)
|
|
}
|
|
note={detail.custom_domain ? null : "未绑自定义域名"}
|
|
action={
|
|
detail.custom_domain ? null : (
|
|
<Button variant="outline" size="sm" render={<Link href={sectionHref(detail.id, "domain")} />}>
|
|
绑定域名
|
|
</Button>
|
|
)
|
|
}
|
|
/>
|
|
<SettingsRow
|
|
label="状态"
|
|
value={deployment ? getStatusView(deployment.status).label : "未发布"}
|
|
note={deployment ? `${formatRelativeTime(deployment.updated_at)}发布` : null}
|
|
action={
|
|
deployment?.status === "suspended" ? (
|
|
<Button variant="outline" size="sm" onClick={() => resume.mutate(deployment.deployment_id)} disabled={resume.isPending}>
|
|
恢复部署
|
|
</Button>
|
|
) : deployment != null ? (
|
|
<DeployTabLink appId={detail.id} />
|
|
) : null
|
|
}
|
|
/>
|
|
<SettingsRow
|
|
label="规格"
|
|
value={deployment?.instance_type_code ?? "—"}
|
|
note="重新发布时可换档"
|
|
/>
|
|
<SettingsRow
|
|
label="托管库"
|
|
value={
|
|
detail.database
|
|
? `线上库 ${formatBytes(detail.database.prod_bytes)} · 开发库 ${formatBytes(detail.database.dev_bytes)}`
|
|
: "未创建"
|
|
}
|
|
/>
|
|
<SettingsRow
|
|
label="会话"
|
|
value={`${detail.session_count} 个`}
|
|
note={detail.archived_session_count > 0 ? `已归档 ${detail.archived_session_count} 个` : null}
|
|
action={
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => newSession.mutate()}
|
|
disabled={newSession.isPending}
|
|
>
|
|
新建会话
|
|
</Button>
|
|
}
|
|
/>
|
|
<SettingsRow
|
|
label="创建于"
|
|
value={new Date(detail.created_at).toLocaleDateString("zh-CN")}
|
|
note={detail.slug}
|
|
/>
|
|
</SectionCard>
|
|
);
|
|
};
|