Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/docs/personal-settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ identities, adjust the app, and manage profile details.
Use **Profile** to update account details such as your name, email,
avatar, and security settings.

Email editing requires account email verification to be enabled for the
deployment. Otherwise, the email field is read-only with an explanation; you
can still change your name. When email editing is available, the existing
verification flow must complete before the new address takes effect.

These settings affect your own Roomote account. They do not change deployment
membership, environment access, or integration setup for the rest of the team.

Expand Down
45 changes: 45 additions & 0 deletions apps/web/src/components/settings/UserProfileSection.test.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 19 additions & 4 deletions apps/web/src/components/settings/UserProfileSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ function getAuthErrorMessage(
}

export function UserProfileSection({
canChangeEmail = false,
canChangePassword = false,
profile,
}: {
canChangeEmail?: boolean;
canChangePassword?: boolean;
profile: UserProfileSectionProfile;
}) {
Expand All @@ -53,7 +55,7 @@ export function UserProfileSection({
const trimmedName = name.trim();
const trimmedEmail = email.trim();
const hasNameChange = trimmedName !== profile.name;
const hasEmailChange = trimmedEmail !== profile.email;
const hasEmailChange = canChangeEmail && trimmedEmail !== profile.email;
// Only show the Remove control for avatars uploaded through this app's
// /api/avatars/ endpoint. OAuth-provider avatar URLs are not removable here
// — the user would lose their provider-supplied picture with no way back.
Expand Down Expand Up @@ -285,8 +287,8 @@ export function UserProfileSection({
) : (
<form className="space-y-4 max-w-md" onSubmit={handleSubmit}>
<p className="text-muted-foreground">
Update your name or email. Your profile picture can be changed
from the profile view.
Update your name{canChangeEmail ? ' or email' : ''}. Your profile
picture can be changed from the profile view.
</p>
<div className="space-y-1">
<Label htmlFor="personal-name">Name</Label>
Expand All @@ -303,13 +305,26 @@ export function UserProfileSection({
<Label htmlFor="personal-email">Email</Label>
<Input
id="personal-email"
readOnly={!canChangeEmail}
aria-describedby={
canChangeEmail ? undefined : 'personal-email-unavailable'
}
type="email"
autoComplete="email"
required
value={email}
value={canChangeEmail ? email : profile.email}
onChange={(event) => setEmail(event.target.value)}
disabled={isSubmitting}
/>
{!canChangeEmail && (
<p
id="personal-email-unavailable"
className="text-xs text-muted-foreground"
>
Email changes require email verification to be enabled for
this deployment.
</p>
)}
</div>
<div className="flex flex-wrap gap-2">
<Button type="submit" size="sm" disabled={isSubmitting}>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ import {

export function PersonalSettingsPage({
profile,
canChangeEmail = false,
canChangePassword,
canSetPassword,
}: {
profile: UserProfileSectionProfile;
canChangeEmail?: boolean;
canChangePassword: boolean;
canSetPassword: boolean;
}) {
return (
<SettingsShell pageId="personal">
<UserProfileSection
canChangeEmail={canChangeEmail}
canChangePassword={canChangePassword}
profile={profile}
/>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function PersonalSettingsRoute() {

return (
<PersonalSettingsPage
canChangeEmail={accountCapabilities.data?.canChangeEmail ?? false}
canChangePassword={accountCapabilities.data?.canChangePassword ?? false}
canSetPassword={accountCapabilities.data?.canSetPassword ?? false}
profile={{
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/trpc/commands/preferences/index.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions apps/web/src/trpc/commands/preferences/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { headers } from 'next/headers';

import type { UserAuthSuccess } from '@/types';
import { getAuth } from '@/lib/server/auth';
import { isEmailChannelEnabled } from '@/lib/server/env';
import { userHasCredentialAccount } from '@/lib/server/user-management';
import {
DEFAULT_PERSONAL_PREFERENCES,
Expand Down Expand Up @@ -73,6 +74,7 @@ export async function getPersonalAccountCapabilitiesCommand(
const hasCredentialAccount = await userHasCredentialAccount(auth.userId);

return {
canChangeEmail: isEmailChannelEnabled(),
canChangePassword: hasCredentialAccount,
canSetPassword: !hasCredentialAccount,
};
Expand Down
Loading