PMFlow/frontend/src/api/accountSettings.ts

149 lines
4.5 KiB
TypeScript

export type AccountLanguage = "zh-CN" | "en-US";
export type AccountAnswerDepth = "concise" | "balanced" | "deep";
export type AccountEvidenceDisplay = "summary" | "inline" | "full";
export type AccountNotificationKey = "pendingConfirmation" | "executionWriteback" | "releaseGate" | "failureAlert";
export type AccountSettingsSaveScope = "local" | "account";
export interface AccountProfileSettings {
displayName: string;
roleTitle: string;
email: string;
defaultLanguage: AccountLanguage;
}
export interface AccountAiPreferenceSettings {
answerDepth: AccountAnswerDepth;
evidenceDisplay: AccountEvidenceDisplay;
}
export interface AccountNotificationPreference {
enabled: boolean;
desktop: boolean;
emailDigest: boolean;
}
export type AccountNotificationSettings = Record<AccountNotificationKey, AccountNotificationPreference>;
export interface AccountSettings {
profile: AccountProfileSettings;
aiPreferences: AccountAiPreferenceSettings;
notifications: AccountNotificationSettings;
saveScope: AccountSettingsSaveScope;
updatedAt: string;
}
export interface AccountSettingsSeed {
displayName?: string | null;
roleTitle?: string | null;
email?: string | null;
}
const STORAGE_KEY = "pmflow:account-settings:v1";
export const accountNotificationLabels: Record<AccountNotificationKey, { title: string; detail: string }> = {
pendingConfirmation: {
title: "待确认",
detail: "AI 生成草案、变更项或写入前确认需要你拍板时提醒。",
},
executionWriteback: {
title: "执行回写",
detail: "Agent、IDE 或集成系统回传开发进度、分支、PR 与测试结果时提醒。",
},
releaseGate: {
title: "发布门禁",
detail: "发布校验、合规检查、验收项阻塞或通过时提醒。",
},
failureAlert: {
title: "失败提醒",
detail: "AI 调用、同步任务、执行回写或外部集成失败时提醒。",
},
};
export const accountNotificationKeys: AccountNotificationKey[] = [
"pendingConfirmation",
"executionWriteback",
"releaseGate",
"failureAlert",
];
export function createDefaultAccountSettings(seed: AccountSettingsSeed = {}): AccountSettings {
return {
profile: {
displayName: seed.displayName?.trim() || "张子墨",
roleTitle: seed.roleTitle?.trim() || "产品负责人",
email: seed.email?.trim() || "",
defaultLanguage: "zh-CN",
},
aiPreferences: {
answerDepth: "balanced",
evidenceDisplay: "inline",
},
notifications: {
pendingConfirmation: { enabled: true, desktop: true, emailDigest: false },
executionWriteback: { enabled: true, desktop: true, emailDigest: false },
releaseGate: { enabled: true, desktop: true, emailDigest: true },
failureAlert: { enabled: true, desktop: true, emailDigest: true },
},
saveScope: "local",
updatedAt: new Date().toISOString(),
};
}
function isBrowserStorageAvailable(): boolean {
return typeof window !== "undefined" && typeof window.localStorage !== "undefined";
}
function readStoredAccountSettings(): Partial<AccountSettings> | null {
if (!isBrowserStorageAvailable()) return null;
const raw = window.localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
try {
const parsed = JSON.parse(raw) as Partial<AccountSettings>;
return parsed && typeof parsed === "object" ? parsed : null;
} catch {
return null;
}
}
function persistAccountSettings(settings: AccountSettings): void {
if (!isBrowserStorageAvailable()) {
throw new Error("当前环境不支持本机暂存");
}
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
}
function mergeAccountSettings(base: AccountSettings, stored: Partial<AccountSettings> | null): AccountSettings {
if (!stored) return base;
return {
...base,
...stored,
profile: {
...base.profile,
...stored.profile,
},
aiPreferences: {
...base.aiPreferences,
...stored.aiPreferences,
},
notifications: {
...base.notifications,
...stored.notifications,
},
};
}
export async function getAccountSettings(seed?: AccountSettingsSeed): Promise<AccountSettings> {
const defaults = createDefaultAccountSettings(seed);
return mergeAccountSettings(defaults, readStoredAccountSettings());
}
export async function updateAccountSettings(nextSettings: AccountSettings): Promise<AccountSettings> {
const persisted = {
...nextSettings,
saveScope: "local" as const,
updatedAt: new Date().toISOString(),
};
persistAccountSettings(persisted);
return persisted;
}