1
0
forked from baron/baron-sso
Files
baron-sso/common/shell/index.ts

127 lines
3.4 KiB
TypeScript

import {
readSessionExpiryEnabled,
SESSION_EXPIRY_STORAGE_KEY,
writeSessionExpiryEnabled,
} from "../core/session";
export type ShellTheme = "light" | "dark";
export type ShellTranslator = (
key: string,
fallback: string,
vars?: Record<string, string | number>,
) => string;
type ShellSessionStatusParams = {
expiresAtSec?: number | null;
nowMs: number;
t: ShellTranslator;
};
type ShellProfileSummaryParams = {
profileName?: string | null;
profileEmail?: string | null;
fallbackName: string;
fallbackEmail: string;
};
export const SHELL_THEME_STORAGE_KEY = "admin_theme";
export const SHELL_SESSION_EXPIRY_STORAGE_KEY = SESSION_EXPIRY_STORAGE_KEY;
export type { ShellSidebarNavItem } from "./AppSidebar";
export { AppSidebar } from "./AppSidebar";
export { shellLayoutClasses } from "./layout";
export function readShellTheme(): ShellTheme {
return window.localStorage.getItem(SHELL_THEME_STORAGE_KEY) === "dark"
? "dark"
: "light";
}
export function applyShellTheme(theme: ShellTheme) {
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(theme);
window.localStorage.setItem(SHELL_THEME_STORAGE_KEY, theme);
}
export function readShellSessionExpiryEnabled(defaultEnabled = true) {
return readSessionExpiryEnabled({ defaultEnabled });
}
export function writeShellSessionExpiryEnabled(isEnabled: boolean) {
writeSessionExpiryEnabled(isEnabled);
}
export function buildShellProfileSummary({
profileName,
profileEmail,
fallbackName,
fallbackEmail,
}: ShellProfileSummaryParams) {
const resolvedName = profileName?.trim() || fallbackName;
const resolvedEmail = profileEmail?.trim() || fallbackEmail;
return {
name: resolvedName,
email: resolvedEmail,
initial: resolvedName.charAt(0).toUpperCase(),
};
}
export function buildShellSessionStatus({
expiresAtSec,
nowMs,
t,
}: ShellSessionStatusParams) {
const remainingMs =
typeof expiresAtSec === "number" ? expiresAtSec * 1000 - nowMs : null;
const remainingTotalSec =
remainingMs !== null ? Math.max(0, Math.floor(remainingMs / 1000)) : null;
const remainingMinutes =
remainingTotalSec !== null ? Math.floor(remainingTotalSec / 60) : null;
const remainingSeconds =
remainingTotalSec !== null ? remainingTotalSec % 60 : null;
let toneClass =
"border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300";
let text = t("ui.shell.session.active", "세션 활성");
if (remainingMs === null) {
toneClass = "border-border bg-card text-muted-foreground";
text = t("ui.shell.session.unknown", "알 수 없음");
} else if (remainingMs <= 0) {
toneClass =
"border-rose-500/30 bg-rose-500/10 text-rose-700 dark:text-rose-300";
text = t("ui.shell.session.expired", "세션 만료");
} else if (
remainingMinutes !== null &&
remainingSeconds !== null &&
remainingMinutes <= 5
) {
toneClass =
"border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300";
text = t(
"ui.shell.session.expiring",
"만료 임박: {{minutes}}분 {{seconds}}초 남음",
{
minutes: remainingMinutes,
seconds: remainingSeconds,
},
);
} else {
text = t(
"ui.shell.session.remaining",
"만료 예정: {{minutes}}분 {{seconds}}초 남음",
{
minutes: remainingMinutes ?? 0,
seconds: remainingSeconds ?? 0,
},
);
}
return {
toneClass,
text,
};
}