Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .changeset/shy-pages-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { withUniquePaths } from '../profile-pages/profile-pages.layout';
import type {
CustomOrganizationProfilePage,
OrganizationProfilePageId,
OrganizationProfilePageListOptions,
OrganizationProfilePages,
} from './organization-profile.types';
import type { OrganizationProfilePageListOptions } from './organization-profile.view';

export const ORGANIZATION_PROFILE_PAGE_IDS: readonly OrganizationProfilePageId[] = [
'general',
Expand Down Expand Up @@ -58,12 +58,14 @@ export function resolveOrganizationProfilePages(
return applyOrder(order, entries, entry => entry.id);
}

export function organizationProfilePageIds({
export function organizationProfileNavEntries({
pages,
customPages,
pageOrder,
}: OrganizationProfilePageListOptions): string[] {
return resolveOrganizationProfilePages(getAvailableOrganizationProfilePages(pages), customPages, pageOrder).map(
entry => entry.id,
);
}: OrganizationProfilePageListOptions): OrganizationProfileNavEntry[] {
return resolveOrganizationProfilePages(getAvailableOrganizationProfilePages(pages), customPages, pageOrder);
}

export function organizationProfilePageIds(options: OrganizationProfilePageListOptions): string[] {
return organizationProfileNavEntries(options).map(entry => entry.id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import type { ReactNode } from 'react';

import type { APIKeysPageProps } from '../api-keys/api-keys-table';
import { useOrganizationProfileController } from './organization-profile.controller';
import { useProfilePagesController } from '../profile-pages/profile-pages.controller';
import { organizationProfilePageIds } from './organization-profile.layout';
import { useOrganizationProfileModel } from './organization-profile.model';
import type { OrganizationProfileOptions } from './organization-profile.provider';
import { OrganizationProfileProvider } from './organization-profile.provider';
Expand Down Expand Up @@ -88,7 +89,10 @@ export function OrganizationProfile({
}: OrganizationProfileProps) {
const model = useOrganizationProfileModel();
const pages = model.status === 'ready' ? organizationProfilePanels(model.pages, apiKeysProps) : undefined;
const navigation = useOrganizationProfileController({ pages, customPages, pageOrder, defaultPage });
const navigation = useProfilePagesController(
defaultPage ?? pageOrder?.[0] ?? 'general',
pages && organizationProfilePageIds({ pages, customPages, pageOrder }),
);

if (model.status === 'loading') {
return fallback ?? null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,16 @@ export interface CustomOrganizationProfilePage {
icon?: ReactNode;
content: ReactNode;
}

/** What the organization's navigation is built from. */
export interface OrganizationProfilePageListOptions {
/** The built-in pages. One left undefined is left out of the navigation. */
pages: OrganizationProfilePages;
/** Pages of the consumer's own, added to the navigation after the built-ins. */
customPages?: readonly CustomOrganizationProfilePage[];
/**
* The order the navigation runs in, by id: a built-in page's id, or a custom page's `path`. Ids
* left out keep their default place behind the ones named.
*/
pageOrder?: readonly (OrganizationProfilePageId | (string & {}))[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,8 @@ import { Icon } from '../../components/icon';
import { useMessages } from '../../localization';
import type { ProfilePage, ProfilePagesViewProps } from '../profile-pages/profile-pages.view';
import { ProfilePagesView } from '../profile-pages/profile-pages.view';
import {
getAvailableOrganizationProfilePages,
ORGANIZATION_PROFILE_PAGE_ICONS,
resolveOrganizationProfilePages,
} from './organization-profile.layout';
import type {
CustomOrganizationProfilePage,
OrganizationProfilePageId,
OrganizationProfilePages,
} from './organization-profile.types';

/** What the organization's navigation is built from. */
export interface OrganizationProfilePageListOptions {
/** The built-in pages. One left undefined is left out of the navigation. */
pages: OrganizationProfilePages;
/** Pages of the consumer's own, added to the navigation after the built-ins. */
customPages?: readonly CustomOrganizationProfilePage[];
/**
* The order the navigation runs in, by id: a built-in page's id, or a custom page's `path`. Ids
* left out keep their default place behind the ones named.
*/
pageOrder?: readonly (OrganizationProfilePageId | (string & {}))[];
}
import { ORGANIZATION_PROFILE_PAGE_ICONS, organizationProfileNavEntries } from './organization-profile.layout';
import type { OrganizationProfilePageId, OrganizationProfilePageListOptions } from './organization-profile.types';

export interface OrganizationProfileViewProps
extends
Expand Down Expand Up @@ -59,23 +38,22 @@ export const OrganizationProfileView = React.forwardRef<HTMLDivElement, Organiza

/** The organization's navigation, in order: the built-ins it was given content for, then the consumer's pages. */
export function organizationProfilePageList(
{ pages, customPages, pageOrder }: OrganizationProfilePageListOptions,
options: OrganizationProfilePageListOptions,
labels: Record<OrganizationProfilePageId, string>,
): ProfilePage[] {
return resolveOrganizationProfilePages(getAvailableOrganizationProfilePages(pages), customPages, pageOrder).map(
entry =>
entry.custom
? { id: entry.id, label: entry.custom.label, icon: entry.custom.icon, content: entry.custom.content }
: {
id: entry.id,
label: labels[entry.id],
icon: (
<Icon
glyph={ORGANIZATION_PROFILE_PAGE_ICONS[entry.id]}
size='sm'
/>
),
content: pages[entry.id],
},
return organizationProfileNavEntries(options).map(entry =>
entry.custom
? { id: entry.id, label: entry.custom.label, icon: entry.custom.icon, content: entry.custom.content }
: {
id: entry.id,
label: labels[entry.id],
icon: (
<Icon
glyph={ORGANIZATION_PROFILE_PAGE_ICONS[entry.id]}
size='sm'
/>
),
content: options.pages[entry.id],
},
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useState } from 'react';

export function useProfilePagesController(initialPage: string, pageIds: readonly string[] | undefined) {
const [activePage, setActivePage] = useState(initialPage);

if (pageIds) {
const [firstPage] = pageIds;
if (firstPage !== undefined && !pageIds.includes(activePage)) {
setActivePage(firstPage);
}
}

return { activePage, onPageChange: setActivePage };
}

This file was deleted.

27 changes: 26 additions & 1 deletion packages/mosaic/src/features/user-profile/user-profile.layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { CreditCard } from '../../icons/glyphs/credit-card';
import { ShieldCheck } from '../../icons/glyphs/shield-check';
import { UserCircle } from '../../icons/glyphs/user-circle';
import { applyOrder } from '../../utils/apply-order';
import { organizationProfilePageIds } from '../organization-profile/organization-profile.layout';
import type { OrganizationProfilePageListOptions } from '../organization-profile/organization-profile.types';
import { withUniquePaths } from '../profile-pages/profile-pages.layout';
import type { CustomProfilePage, UserProfilePageId, UserProfilePages } from './user-profile.types';

Expand Down Expand Up @@ -58,7 +60,30 @@ export function resolveUserProfilePages(
return applyOrder(order, entries, entry => entry.id);
}

export function withoutOrganizationPaths(customPages: readonly CustomProfilePage[] = []): CustomProfilePage[] {
type UserProfilePageListOptions = {
pages: UserProfilePages;
customPages?: readonly CustomProfilePage[];
pageOrder?: readonly string[];
organization?: OrganizationProfilePageListOptions;
};

export function userProfileNavEntries({
pages,
customPages,
pageOrder,
}: UserProfilePageListOptions): UserProfileNavEntry[] {
return resolveUserProfilePages(getAvailableUserProfilePages(pages), withoutOrganizationPaths(customPages), pageOrder);
}

export function userProfilePageIds(options: UserProfilePageListOptions): string[] {
const accountPageIds = userProfileNavEntries(options).map(entry => entry.id);
const organizationPageIds = options.organization
? organizationProfilePageIds(options.organization).map(id => `${ORGANIZATION_PAGE_PREFIX}${id}`)
: [];
return [...accountPageIds, ...organizationPageIds];
}

function withoutOrganizationPaths(customPages: readonly CustomProfilePage[] = []): CustomProfilePage[] {
return customPages.filter(page => {
if (!page.path.startsWith(ORGANIZATION_PAGE_PREFIX)) {
return true;
Expand Down
23 changes: 10 additions & 13 deletions packages/mosaic/src/features/user-profile/user-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import type { OrganizationProfileProps } from '../organization-profile/organizat
import { organizationProfilePanels } from '../organization-profile/organization-profile';
import { useOrganizationProfileModel } from '../organization-profile/organization-profile.model';
import { OrganizationProfileProvider } from '../organization-profile/organization-profile.provider';
import { useProfilePagesController } from '../profile-pages/profile-pages.controller';
import { useUserProfilePages } from '../switcher/switcher.pages';
import type { SwitcherMode } from '../switcher/switcher.types';
import { useUserProfileController } from './user-profile.controller';
import { userProfilePageIds } from './user-profile.layout';
import type { UserProfileOptions } from './user-profile.provider';
import { UserProfileProvider } from './user-profile.provider';
import type { UserProfileViewProps } from './user-profile.view';
Expand Down Expand Up @@ -174,7 +175,7 @@ type AccountViewProps = Omit<UserProfileViewProps, 'activePage' | 'onPageChange'
};

function AccountUserProfileView({ initialPage, ...viewProps }: AccountViewProps) {
const navigation = useUserProfileController({ initialPage, ...viewProps });
const navigation = useProfilePagesController(initialPage, userProfilePageIds(viewProps));

return (
<UserProfileView
Expand All @@ -190,25 +191,21 @@ function CombinedUserProfileView({
...viewProps
}: AccountViewProps & Pick<UserProfileProps, 'organizationProfileProps'>) {
const model = useOrganizationProfileModel();
const organizationState =
const organization =
model.status === 'ready'
? {
status: 'ready' as const,
pageList: { pages: organizationProfilePanels(model.pages, apiKeysProps), customPages, pageOrder },
}
: { status: model.status };
const navigation = useUserProfileController({
? { pages: organizationProfilePanels(model.pages, apiKeysProps), customPages, pageOrder }
: undefined;
const navigation = useProfilePagesController(
initialPage,
...viewProps,
organization: organizationState,
});
model.status === 'loading' ? undefined : userProfilePageIds({ ...viewProps, organization }),
);

return (
<OrganizationProfileProvider afterLeaveOrganizationUrl={afterLeaveOrganizationUrl}>
<UserProfileView
{...viewProps}
{...navigation}
organization={organizationState.status === 'ready' ? organizationState.pageList : undefined}
organization={organization}
/>
</OrganizationProfileProvider>
);
Expand Down
21 changes: 7 additions & 14 deletions packages/mosaic/src/features/user-profile/user-profile.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@ import React from 'react';

import { Icon } from '../../components/icon';
import { useMessages } from '../../localization';
import type { OrganizationProfilePageId } from '../organization-profile/organization-profile.types';
import type { OrganizationProfilePageListOptions } from '../organization-profile/organization-profile.view';
import type {
OrganizationProfilePageId,
OrganizationProfilePageListOptions,
} from '../organization-profile/organization-profile.types';
import { organizationProfilePageList } from '../organization-profile/organization-profile.view';
import type { ProfilePage, ProfilePageGroup, ProfilePagesViewProps } from '../profile-pages/profile-pages.view';
import { ProfilePagesView } from '../profile-pages/profile-pages.view';
import {
getAvailableUserProfilePages,
ORGANIZATION_PAGE_PREFIX,
resolveUserProfilePages,
USER_PROFILE_PAGE_ICONS,
withoutOrganizationPaths,
} from './user-profile.layout';
import { ORGANIZATION_PAGE_PREFIX, USER_PROFILE_PAGE_ICONS, userProfileNavEntries } from './user-profile.layout';
import type { CustomProfilePage, UserProfilePageId, UserProfilePages } from './user-profile.types';

export interface UserProfileViewProps extends Omit<
Expand Down Expand Up @@ -53,10 +49,7 @@ export const UserProfileView = React.forwardRef<HTMLDivElement, UserProfileViewP
) {
const m = useMessages('userProfile');
const organizationLabels = useMessages('organizationProfile').pages;
const accountPages = userProfilePageList(
{ pages, customPages: withoutOrganizationPaths(customPages), pageOrder },
m.pages,
);
const accountPages = userProfilePageList({ pages, customPages, pageOrder }, m.pages);
const groups: ProfilePageGroup[] = organization
? [
{ label: m.accountGroup, pages: accountPages },
Expand Down Expand Up @@ -88,7 +81,7 @@ function userProfilePageList(
{ pages, customPages, pageOrder }: Pick<UserProfileViewProps, 'pages' | 'customPages' | 'pageOrder'>,
labels: Record<UserProfilePageId, string>,
): ProfilePage[] {
return resolveUserProfilePages(getAvailableUserProfilePages(pages), customPages, pageOrder).map(entry =>
return userProfileNavEntries({ pages, customPages, pageOrder }).map(entry =>
entry.custom
? { id: entry.id, label: entry.custom.label, icon: entry.custom.icon, content: entry.custom.content }
: {
Expand Down
Loading