Files
NextEdu/src/modules/settings/components/ai-provider-selector.tsx
SpecialX f3c223d914 feat(settings,questions,school,textbooks): add brand config, question components, school dialogs, textbooks hooks
settings:

- Add actions-brand, brand-config, data-access-brand for brand management

- Add admin-file-upload-card, admin-notification-config-card, admin-school-info-card, admin-security-policy-card

- Add ai-provider-delete-dialog, ai-provider-selector, brand-config-card

- Add security-recent-logins-section, security-two-factor-section

- Add config/profile-overview-config, data-access-profile-overview, lib/system-settings-utils

questions:

- Add batch-operations, import-export-buttons, knowledge-point-selector, options-editor

- Add question-bank-results-client, question-cascade-filter, question-content-renderer, utils

school:

- Add grade-delete-dialog, grade-form-dialog, grade-list-toolbar, grade-overview-cards

- Add use-grade-data hook

textbooks:

- Add textbook-form-fields component

- Add use-kp-create, use-kp-delete, use-kp-update hooks
2026-07-03 10:26:00 +08:00

109 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { useTranslations } from "next-intl"
import { type ReactElement } from "react"
import { type AiProviderSummary } from "@/modules/settings/actions"
import { Badge } from "@/shared/components/ui/badge"
import { Label } from "@/shared/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select"
const NEW_PROVIDER_VALUE = "__new__"
interface AiProviderSelectorProps {
/** 已有服务商列表 */
providers: AiProviderSummary[]
/** 当前选中的服务商 ID空字符串表示新建 */
selectedId: string
/** 当前用户 ID用于判断是否为创建者 */
currentUserId?: string
/** 选择变更回调 */
onSelectChange: (value: string) => void
}
/**
* AI 服务商选择器
*
* 负责:
* - 渲染已有服务商下拉选择(含"新建"选项)
* - 展示当前选中服务商的密钥状态与可见性徽章
*
* 不包含任何业务逻辑,仅做展示与事件转发。
*/
export function AiProviderSelector({
providers,
selectedId,
currentUserId,
onSelectChange,
}: AiProviderSelectorProps): ReactElement {
const t = useTranslations("settings.ai.providers")
const selectedProvider = providers.find((item) => item.id === selectedId) ?? null
const renderProviderLabel = (item: AiProviderSummary): string => {
const parts = [item.provider, "·", item.model]
if (item.isDefault) parts.push(`(${t("setDefault")})`)
return parts.join(" ")
}
const renderVisibilityBadge = (item: AiProviderSummary): ReactElement => {
const isOwner = currentUserId !== undefined && item.createdBy === currentUserId
return (
<span className="ml-2 inline-flex gap-1">
{item.visibility === "public" ? (
<Badge variant="secondary" className="text-[10px] px-1.5 py-0 h-4">
{t("badgePublic")}
</Badge>
) : (
<Badge variant="outline" className="text-[10px] px-1.5 py-0 h-4">
{t("badgePrivate")}
</Badge>
)}
{isOwner ? (
<Badge variant="outline" className="text-[10px] px-1.5 py-0 h-4 text-muted-foreground">
{t("badgeOwner")}
</Badge>
) : null}
</span>
)
}
return (
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label>{t("existing")}</Label>
<Select value={selectedId || NEW_PROVIDER_VALUE} onValueChange={onSelectChange}>
<SelectTrigger>
<SelectValue placeholder={t("selectPlaceholder")} />
</SelectTrigger>
<SelectContent>
<SelectItem value={NEW_PROVIDER_VALUE}>{t("createNew")}</SelectItem>
{providers.map((item) => (
<SelectItem key={item.id} value={item.id}>
{renderProviderLabel(item)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>{t("keyStatus")}</Label>
<div className="flex items-center rounded-md border px-3 py-2 text-sm text-muted-foreground">
{selectedProvider ? renderVisibilityBadge(selectedProvider) : null}
<span className="ml-auto">
{selectedProvider?.apiKeyLast4
? `${t("stored")} • ****${selectedProvider.apiKeyLast4}`
: t("noKey")}
</span>
</div>
</div>
</div>
)
}