UI - NODEDC LAUNCHER: invite screen по auth design-code
This commit is contained in:
+58
-47
@@ -587,6 +587,10 @@ export function LauncherApp() {
|
||||
state={inviteFlow ?? { status: "loading" }}
|
||||
currentEmail={authSession.user.email}
|
||||
onAccept={() => void handleAcceptInvite()}
|
||||
onSwitchAccount={() => {
|
||||
const returnTo = `${window.location.pathname}${window.location.search}${window.location.hash}`;
|
||||
window.location.replace(`/auth/logout?global=1&returnTo=${encodeURIComponent(returnTo)}`);
|
||||
}}
|
||||
onGoHome={() => {
|
||||
window.history.replaceState(null, "", "/");
|
||||
window.location.replace("/");
|
||||
@@ -740,23 +744,16 @@ function InviteFlowScreen({
|
||||
state,
|
||||
currentEmail,
|
||||
onAccept,
|
||||
onSwitchAccount,
|
||||
onGoHome,
|
||||
}: {
|
||||
state: InviteFlowState;
|
||||
currentEmail: string;
|
||||
onAccept: () => void;
|
||||
onSwitchAccount: () => void;
|
||||
onGoHome: () => void;
|
||||
}) {
|
||||
const payload = "payload" in state ? state.payload : undefined;
|
||||
const title =
|
||||
state.status === "accepted"
|
||||
? "Доступ подключён"
|
||||
: state.status === "error"
|
||||
? "Инвайт недоступен"
|
||||
: "Приглашение в NODE.DC";
|
||||
const description = payload
|
||||
? `Клиент: ${payload.client.name}. Роль: ${membershipRoleLabel(payload.invite.role)}.`
|
||||
: "Проверяем приглашение и платформенную сессию.";
|
||||
const emailMismatch = payload && payload.invite.email.toLowerCase() !== currentEmail.toLowerCase();
|
||||
const inviteStatus = payload?.invite.status;
|
||||
const isAccepting = state.status === "accepting";
|
||||
@@ -767,52 +764,46 @@ function InviteFlowScreen({
|
||||
inviteStatus !== "expired" &&
|
||||
inviteStatus !== "revoked"
|
||||
);
|
||||
const isTerminalInvite = inviteStatus === "accepted" || inviteStatus === "expired" || inviteStatus === "revoked";
|
||||
const details = payload
|
||||
? [
|
||||
`Рабочая область: ${payload.client.name}`,
|
||||
`Роль: ${membershipRoleLabel(payload.invite.role)}`,
|
||||
`Почта инвайта: ${payload.invite.email}`,
|
||||
]
|
||||
: ["Проверяем приглашение и платформенную сессию"];
|
||||
const statusMessage = resolveInviteStatusMessage(state, Boolean(emailMismatch), inviteStatus);
|
||||
|
||||
return (
|
||||
<div className="launcher-app">
|
||||
<main
|
||||
style={{
|
||||
display: "grid",
|
||||
minHeight: "100vh",
|
||||
placeItems: "center",
|
||||
padding: "2rem",
|
||||
}}
|
||||
>
|
||||
<section
|
||||
style={{
|
||||
display: "grid",
|
||||
width: "min(34rem, 100%)",
|
||||
gap: "1rem",
|
||||
padding: "2rem",
|
||||
borderRadius: "1.75rem",
|
||||
background: "linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.035))",
|
||||
textAlign: "center",
|
||||
}}
|
||||
>
|
||||
<img src="/nodedc-logo.svg" alt="NODE.DC" style={{ justifySelf: "center", width: "11rem" }} />
|
||||
<p className="eyebrow" style={{ margin: 0 }}>
|
||||
Invite flow
|
||||
</p>
|
||||
<h1 style={{ margin: 0 }}>{title}</h1>
|
||||
<p style={{ margin: 0, color: "var(--text-secondary)", lineHeight: 1.5 }}>{description}</p>
|
||||
{payload ? (
|
||||
<p style={{ margin: 0, color: "var(--text-secondary)", lineHeight: 1.5 }}>
|
||||
Инвайт: <strong>{payload.invite.email}</strong>. Текущий вход: <strong>{currentEmail}</strong>.
|
||||
</p>
|
||||
) : null}
|
||||
<div className="launcher-app nodedc-auth-page">
|
||||
<NodeDcAuthBrandHeader />
|
||||
<main className="nodedc-auth-page__main">
|
||||
<section className="nodedc-auth-card nodedc-invite-card" aria-live="polite">
|
||||
<div className="nodedc-auth-card__copy">
|
||||
<h1>Работайте во всех измерениях.</h1>
|
||||
<p>Приглашение в NODE.DC.</p>
|
||||
</div>
|
||||
|
||||
<div className="nodedc-invite-card__details">
|
||||
{details.map((detail) => (
|
||||
<span key={detail}>{detail}</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{statusMessage ? <p className="nodedc-auth-card__status">{statusMessage}</p> : null}
|
||||
{state.status === "error" ? <p className="nodedc-auth-card__status">{state.message}</p> : null}
|
||||
|
||||
{emailMismatch ? (
|
||||
<p style={{ margin: 0, color: "var(--warning)", lineHeight: 1.45 }}>
|
||||
Нужно войти под почтой, на которую выписан инвайт.
|
||||
</p>
|
||||
) : null}
|
||||
{state.status === "error" ? <p style={{ margin: 0, color: "var(--warning)", lineHeight: 1.45 }}>{state.message}</p> : null}
|
||||
{state.status === "accepted" ? (
|
||||
<button className="button button--primary" type="button" onClick={onSwitchAccount}>
|
||||
Сменить аккаунт
|
||||
</button>
|
||||
) : state.status === "error" || state.status === "accepted" || isTerminalInvite ? (
|
||||
<button className="button button--primary" type="button" onClick={onGoHome}>
|
||||
Перейти в витрину
|
||||
</button>
|
||||
) : (
|
||||
<button className="button button--primary" type="button" disabled={!canAccept || isAccepting} onClick={onAccept}>
|
||||
{isAccepting ? "Подключаем доступ" : "Принять приглашение"}
|
||||
{state.status === "loading" ? "Проверяем" : isAccepting ? "Подключаем доступ" : "Принять приглашение"}
|
||||
</button>
|
||||
)}
|
||||
</section>
|
||||
@@ -821,6 +812,26 @@ function InviteFlowScreen({
|
||||
);
|
||||
}
|
||||
|
||||
function NodeDcAuthBrandHeader() {
|
||||
return (
|
||||
<header className="nodedc-auth-brand-shell">
|
||||
<a href="/" className="nodedc-expanded-brand-link" aria-label="NODE.DC">
|
||||
<img src="/nodedc-logo.svg" alt="NODE DC" className="nodedc-expanded-brand-logo" />
|
||||
</a>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function resolveInviteStatusMessage(state: InviteFlowState, emailMismatch: boolean, inviteStatus?: Invite["status"]) {
|
||||
if (state.status === "loading") return "Проверяем приглашение.";
|
||||
if (state.status === "accepting") return "Подключаем доступ к рабочей области.";
|
||||
if (state.status === "accepted" || inviteStatus === "accepted") return "Доступ уже подключён.";
|
||||
if (inviteStatus === "expired") return "Срок действия приглашения истёк.";
|
||||
if (inviteStatus === "revoked") return "Приглашение отозвано.";
|
||||
if (emailMismatch) return "Нужно войти под почтой, на которую выписан инвайт.";
|
||||
return null;
|
||||
}
|
||||
|
||||
function AuthStateScreen({
|
||||
title,
|
||||
description,
|
||||
|
||||
Reference in New Issue
Block a user