Files
NextEdu/src/modules/settings/actions-brand.ts
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

82 lines
2.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 server"
import { z } from "zod"
import { revalidatePath } from "next/cache"
import type { ActionState } from "@/shared/types/action-state"
import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { getSession } from "@/shared/lib/session"
import { getBrandConfig, saveBrandConfig } from "./data-access-brand"
import type { BrandConfig } from "./brand-config"
const BrandConfigSchema = z.object({
schoolName: z.string().min(1).max(255),
logoUrl: z.string().url().or(z.literal("")).default(""),
testimonialQuote: z.string().min(1).max(500),
testimonialAuthor: z.string().min(1).max(100),
})
/**
* 获取品牌配置 Server Actionaudit-P2-6 新增)
*/
export async function getBrandConfigAction(): Promise<ActionState<BrandConfig>> {
try {
await requirePermission(Permissions.SCHOOL_MANAGE)
const config = await getBrandConfig()
return { success: true, data: config }
} catch (e) {
return {
success: false,
message: e instanceof Error ? e.message : "Failed to get brand config",
}
}
}
/**
* 保存品牌配置 Server Actionaudit-P2-6 新增)
*/
export async function saveBrandConfigAction(
prevState: ActionState<BrandConfig>,
formData: FormData,
): Promise<ActionState<BrandConfig>> {
try {
await requirePermission(Permissions.SCHOOL_MANAGE)
const session = await getSession()
const parsed = BrandConfigSchema.safeParse({
schoolName: formData.get("schoolName"),
logoUrl: formData.get("logoUrl"),
testimonialQuote: formData.get("testimonialQuote"),
testimonialAuthor: formData.get("testimonialAuthor"),
})
if (!parsed.success) {
return {
success: false,
message: parsed.error.issues[0]?.message ?? "Invalid brand config",
}
}
const config: BrandConfig = {
schoolName: parsed.data.schoolName,
logoUrl: parsed.data.logoUrl || null,
testimonialQuote: parsed.data.testimonialQuote,
testimonialAuthor: parsed.data.testimonialAuthor,
}
await saveBrandConfig(config, session?.user?.id)
revalidatePath("/admin/settings")
revalidatePath("/(auth)/login")
revalidatePath("/(auth)/register")
return { success: true, data: config, message: "Brand configuration saved" }
} catch (e) {
return {
success: false,
message: e instanceof Error ? e.message : "Failed to save brand config",
}
}
}