R64 A1-A6:新员工入门引导

这个提交包含在:
2026-04-14 16:45:10 +08:00
父节点 86644a6766
当前提交 7ac0de660c
共修改 12 个文件,包含 453 行新增和 5 行删除
+32 -3
查看文件
@@ -9,7 +9,7 @@ import {
useState,
} from "react";
import { configureHttpAuth } from "../api/http";
import type { AuthState, LoginResponse, MeResponse } from "../api/types";
import type { AuthState, LoginResponse, MeResponse, ProfileCompleteness } from "../api/types";
import { changePassword as changePasswordApi } from "../api/auth";
import { clearPwaRuntimeCaches } from "../pwa";
import {
@@ -53,6 +53,13 @@ const OFFLINE_AUTH_SNAPSHOT_VERSION = 1;
const OFFLINE_AUTH_SNAPSHOT_TTL_MS = 24 * 60 * 60 * 1000;
const PROACTIVE_REFRESH_RATIO = 0.8;
const MIN_REFRESH_DELAY_MS = 1000;
const DEFAULT_PROFILE_COMPLETENESS: ProfileCompleteness = {
hasEmergencyContact: false,
hasBankAccount: false,
hasAvatar: false,
hasIdNumber: false,
score: 0,
};
function isWindowAvailable() {
return typeof window !== "undefined";
@@ -69,6 +76,28 @@ function clearSessionHint() {
document.cookie = "has_session=; path=/; max-age=0";
}
function normalizeProfileCompleteness(value: unknown): ProfileCompleteness {
if (!value || typeof value !== "object") {
return DEFAULT_PROFILE_COMPLETENESS;
}
const payload = value as Partial<ProfileCompleteness>;
return {
hasEmergencyContact: Boolean(payload.hasEmergencyContact),
hasBankAccount: Boolean(payload.hasBankAccount),
hasAvatar: Boolean(payload.hasAvatar),
hasIdNumber: Boolean(payload.hasIdNumber),
score: typeof payload.score === "number" && Number.isFinite(payload.score) ? payload.score : 0,
};
}
function normalizeMeResponse(value: MeResponse | Partial<MeResponse>) {
return {
...value,
profileCompleteness: normalizeProfileCompleteness(value.profileCompleteness),
} as MeResponse;
}
function readMustChangePasswordFromStorage() {
return isWindowAvailable() && window.localStorage.getItem(MUST_CHANGE_PASSWORD_STORAGE_KEY) === "1";
}
@@ -110,7 +139,7 @@ function readOfflineAuthSnapshot() {
return {
version: OFFLINE_AUTH_SNAPSHOT_VERSION,
me: parsed.me,
me: normalizeMeResponse(parsed.me as Partial<MeResponse>),
permissions: parsed.permissions.map((item) => String(item)),
savedAt: savedAt.toISOString(),
} satisfies OfflineAuthSnapshot;
@@ -127,7 +156,7 @@ function writeOfflineAuthSnapshot(me: MeResponse, permissions: string[]) {
const snapshot: OfflineAuthSnapshot = {
version: OFFLINE_AUTH_SNAPSHOT_VERSION,
me,
me: normalizeMeResponse(me),
permissions,
savedAt: new Date().toISOString(),
};