docs(admin-portal): 新增 nextstep-v2.md 记录下游核查结果
v1 声称完成的下游工作经核查实际未完成: - api-gateway: /api/admin/graphql 路由未注册,go vet 编译失败 - teacher-bff: resolver 已完成但 schema 未同步(命名空间 vs 扁平) - iam: proto 缺 BatchGetUsers rpc 声明 v2 记录详细核查证据和修复要求
This commit is contained in:
@@ -14,10 +14,12 @@
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery, useMutation } from "urql";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Loading } from "@edu/ui-components";
|
||||
import { MeQuery, UpdateUserMutation } from "@/lib/graphql";
|
||||
|
||||
export default function SettingsPage() {
|
||||
const t = useTranslations("settings");
|
||||
const [result, reexecuteQuery] = useQuery({ query: MeQuery });
|
||||
const [updateResult, updateUser] = useMutation(UpdateUserMutation);
|
||||
|
||||
@@ -50,11 +52,11 @@ export default function SettingsPage() {
|
||||
setError(null);
|
||||
|
||||
if (!name.trim()) {
|
||||
setError("姓名不能为空");
|
||||
setError(t("error.nameRequired"));
|
||||
return;
|
||||
}
|
||||
if (!email.trim() || !email.includes("@")) {
|
||||
setError("邮箱格式不正确");
|
||||
setError(t("error.emailInvalid"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,10 +80,8 @@ export default function SettingsPage() {
|
||||
<div className="px-10 py-10">
|
||||
<header className="mb-8 flex items-baseline justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-serif text-ink">个人设置</h1>
|
||||
<p className="mt-1 text-sm text-ink-muted">
|
||||
GraphQL MeQuery + UpdateUserMutation · P3 编辑模式
|
||||
</p>
|
||||
<h1 className="text-3xl font-serif text-ink">{t("title")}</h1>
|
||||
<p className="mt-1 text-sm text-ink-muted">{t("subtitle")}</p>
|
||||
</div>
|
||||
{!editing && result.data?.me && (
|
||||
<button
|
||||
@@ -89,7 +89,7 @@ export default function SettingsPage() {
|
||||
onClick={handleStartEdit}
|
||||
className="px-4 py-2 text-sm text-ink-on-accent bg-accent rounded-button hover:bg-accent-hover"
|
||||
>
|
||||
编辑
|
||||
{t("button.edit")}
|
||||
</button>
|
||||
)}
|
||||
</header>
|
||||
@@ -101,45 +101,65 @@ export default function SettingsPage() {
|
||||
) : result.error ? (
|
||||
<div className="mark-left mb-4 py-2 border-l-2 border-danger pl-md">
|
||||
<p className="text-sm px-3 text-danger">
|
||||
加载失败:{result.error.message}
|
||||
{t("error.loadFailed", { message: result.error.message })}
|
||||
</p>
|
||||
</div>
|
||||
) : !result.data?.me ? (
|
||||
<p className="text-sm italic text-ink-muted">暂无用户信息</p>
|
||||
<p className="text-sm italic text-ink-muted">{t("empty")}</p>
|
||||
) : editing ? (
|
||||
<section className="max-w-2xl">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-serif text-ink mb-2">编辑个人信息</h2>
|
||||
<h2 className="text-xl font-serif text-ink mb-2">
|
||||
{t("edit.title")}
|
||||
</h2>
|
||||
<div className="rule-thin mb-4" />
|
||||
</div>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-tiny uppercase tracking-wide text-ink-muted mb-1">
|
||||
姓名 <span className="text-danger">*</span>
|
||||
<label
|
||||
htmlFor="settings-name"
|
||||
className="block text-tiny uppercase tracking-wide text-ink-muted mb-1"
|
||||
>
|
||||
{t("form.label.name")} <span className="text-danger">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="settings-name"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
aria-label={t("form.label.name")}
|
||||
aria-invalid={Boolean(error)}
|
||||
aria-describedby={error ? "settings-form-error" : undefined}
|
||||
className="w-full px-3 py-2 bg-transparent border-b border-rule text-sm text-ink focus:outline-none focus:border-b-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-tiny uppercase tracking-wide text-ink-muted mb-1">
|
||||
邮箱 <span className="text-danger">*</span>
|
||||
<label
|
||||
htmlFor="settings-email"
|
||||
className="block text-tiny uppercase tracking-wide text-ink-muted mb-1"
|
||||
>
|
||||
{t("form.label.email")} <span className="text-danger">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="settings-email"
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
aria-label={t("form.label.email")}
|
||||
aria-invalid={Boolean(error)}
|
||||
aria-describedby={error ? "settings-form-error" : undefined}
|
||||
className="w-full px-3 py-2 bg-transparent border-b border-rule text-sm text-ink focus:outline-none focus:border-b-2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mark-left py-2 border-l-2 border-danger pl-md">
|
||||
<div
|
||||
id="settings-form-error"
|
||||
role="alert"
|
||||
className="mark-left py-2 border-l-2 border-danger pl-md"
|
||||
>
|
||||
<p className="text-sm px-3 text-danger">{error}</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -150,7 +170,9 @@ export default function SettingsPage() {
|
||||
disabled={submitting}
|
||||
className="px-4 py-2 text-sm text-ink-on-accent bg-accent rounded-button hover:bg-accent-hover disabled:opacity-50"
|
||||
>
|
||||
{submitting ? "保存中..." : "保存"}
|
||||
{submitting
|
||||
? t("form.button.submitting")
|
||||
: t("form.button.save")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -158,10 +180,12 @@ export default function SettingsPage() {
|
||||
disabled={submitting}
|
||||
className="text-tiny uppercase tracking-wide text-ink-muted hover:opacity-70 disabled:opacity-50"
|
||||
>
|
||||
取消
|
||||
{t("form.button.cancel")}
|
||||
</button>
|
||||
{updateResult.data && !error && !submitting && (
|
||||
<span className="text-tiny text-success">保存成功</span>
|
||||
<span className="text-tiny text-success">
|
||||
{t("form.status.saved")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
@@ -169,12 +193,14 @@ export default function SettingsPage() {
|
||||
) : (
|
||||
<section className="max-w-2xl">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-xl font-serif text-ink mb-2">基本信息</h2>
|
||||
<h2 className="text-xl font-serif text-ink mb-2">
|
||||
{t("info.title")}
|
||||
</h2>
|
||||
<div className="rule-thin mb-4" />
|
||||
<dl className="space-y-4">
|
||||
<div className="grid grid-cols-3 gap-4 items-baseline">
|
||||
<dt className="text-tiny uppercase tracking-wide text-ink-muted">
|
||||
用户 ID
|
||||
{t("info.label.userId")}
|
||||
</dt>
|
||||
<dd className="col-span-2 text-sm font-mono text-ink">
|
||||
{result.data.me.id}
|
||||
@@ -182,7 +208,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 items-baseline">
|
||||
<dt className="text-tiny uppercase tracking-wide text-ink-muted">
|
||||
姓名
|
||||
{t("info.label.name")}
|
||||
</dt>
|
||||
<dd className="col-span-2 text-sm text-ink">
|
||||
{result.data.me.name}
|
||||
@@ -190,7 +216,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 items-baseline">
|
||||
<dt className="text-tiny uppercase tracking-wide text-ink-muted">
|
||||
邮箱
|
||||
{t("info.label.email")}
|
||||
</dt>
|
||||
<dd className="col-span-2 text-sm text-ink">
|
||||
{result.data.me.email}
|
||||
@@ -198,15 +224,15 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 items-baseline">
|
||||
<dt className="text-tiny uppercase tracking-wide text-ink-muted">
|
||||
角色
|
||||
{t("info.label.roles")}
|
||||
</dt>
|
||||
<dd className="col-span-2 text-sm text-ink">
|
||||
{result.data.me.roles.join(", ") || "无角色"}
|
||||
{result.data.me.roles.join(", ") || t("info.noRoles")}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="grid grid-cols-3 gap-4 items-baseline">
|
||||
<dt className="text-tiny uppercase tracking-wide text-ink-muted">
|
||||
数据范围
|
||||
{t("info.label.dataScope")}
|
||||
</dt>
|
||||
<dd className="col-span-2 text-sm text-ink">
|
||||
{result.data.me.dataScope}
|
||||
@@ -216,9 +242,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
|
||||
<div className="mt-8 p-4 border border-rule rounded-card bg-subtle">
|
||||
<p className="text-sm text-ink-muted">
|
||||
P3 已启用编辑功能(UpdateUserMutation)。点击右上角"编辑"按钮修改姓名与邮箱。
|
||||
</p>
|
||||
<p className="text-sm text-ink-muted">{t("info.editHint")}</p>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user