fix: null display name, settings scrollbar, redundant passkey offer
- Profile API returns empty string instead of "null" when Logto user has no display name set (String.valueOf(null) → "null" bug). - SettingsPage: add overflowY auto + flex 1 so content scrolls within the AppShell layout (which uses overflow: hidden). - Remove redundant passkey offer from onboarding page — passkey enrollment now happens during sign-in via the Experience API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,10 +46,12 @@ public class AccountService {
|
|||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
|
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found");
|
||||||
}
|
}
|
||||||
|
Object nameVal = user.get("name");
|
||||||
|
Object emailVal = user.get("primaryEmail");
|
||||||
return new ProfileData(
|
return new ProfileData(
|
||||||
userId,
|
userId,
|
||||||
String.valueOf(user.getOrDefault("name", "")),
|
nameVal != null ? String.valueOf(nameVal) : "",
|
||||||
String.valueOf(user.getOrDefault("primaryEmail", ""))
|
emailVal != null ? String.valueOf(emailVal) : ""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export function OnboardingPage() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [slugAvailable, setSlugAvailable] = useState<boolean | null>(null);
|
const [slugAvailable, setSlugAvailable] = useState<boolean | null>(null);
|
||||||
const [checkingSlug, setCheckingSlug] = useState(false);
|
const [checkingSlug, setCheckingSlug] = useState(false);
|
||||||
const [showPasskeyOffer, setShowPasskeyOffer] = useState(false);
|
|
||||||
const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
||||||
|
|
||||||
const slug = toSlug(name);
|
const slug = toSlug(name);
|
||||||
@@ -51,17 +50,6 @@ export function OnboardingPage() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
await api.post<TenantResponse>('/onboarding/tenant', { name, slug });
|
await api.post<TenantResponse>('/onboarding/tenant', { name, slug });
|
||||||
// Check if passkeys are enabled in vendor policy
|
|
||||||
try {
|
|
||||||
const config = await fetch('/platform/api/config').then(r => r.json());
|
|
||||||
if (config.vendorAuthPolicy?.passkeyEnabled) {
|
|
||||||
setShowPasskeyOffer(true);
|
|
||||||
setLoading(false);
|
|
||||||
return; // Don't redirect yet
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Ignore — proceed without passkey offer
|
|
||||||
}
|
|
||||||
// Tenant created — force a fresh OIDC sign-in so the Logto SDK gets
|
// Tenant created — force a fresh OIDC sign-in so the Logto SDK gets
|
||||||
// new tokens that include the org membership just created. The existing
|
// new tokens that include the org membership just created. The existing
|
||||||
// Logto session cookie means the user won't see a login form — Logto
|
// Logto session cookie means the user won't see a login form — Logto
|
||||||
@@ -78,34 +66,6 @@ export function OnboardingPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSkipPasskey() {
|
|
||||||
await signIn(`${window.location.origin}/platform/callback`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showPasskeyOffer) {
|
|
||||||
return (
|
|
||||||
<div className={styles.page}>
|
|
||||||
<div className={styles.wrapper}>
|
|
||||||
<Card className={styles.card}>
|
|
||||||
<div className={styles.inner}>
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
|
||||||
<h2 style={{ margin: '16px 0 8px' }}>Secure your account</h2>
|
|
||||||
<p style={{ color: 'var(--text-muted)', marginBottom: 24 }}>
|
|
||||||
Add a passkey to sign in faster with your fingerprint, face, or security key.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
|
||||||
<Button variant="secondary" onClick={handleSkipPasskey}>
|
|
||||||
Set up later
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ export function SettingsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 20 }}>
|
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', gap: 20, overflowY: 'auto', flex: 1 }}>
|
||||||
<h1 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 600 }}>Settings</h1>
|
<h1 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 600 }}>Settings</h1>
|
||||||
<PasskeyNudgeBanner />
|
<PasskeyNudgeBanner />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user