B2B-30: adds personal code to account, company admins invites members

This commit is contained in:
devmc-ee
2025-06-22 15:22:07 +03:00
parent 39c02c6d34
commit 251f2a4ef1
50 changed files with 3546 additions and 2611 deletions

View File

@@ -55,7 +55,7 @@ async function AccountsPage(props: AdminAccountsPageProps) {
}
if (query) {
queryBuilder.or(`name.ilike.%${query}%,email.ilike.%${query}%`);
queryBuilder.or(`name.ilike.%${query}%,email.ilike.%${query}%,personal_code.ilike.%${query}%`);
}
return queryBuilder;

View File

@@ -1,13 +1,9 @@
import {
BorderedNavigationMenu,
BorderedNavigationMenuItem,
} from '@kit/ui/bordered-navigation-menu';
import { If } from '@kit/ui/if';
import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import featuresFlagConfig from '~/config/feature-flags.config';
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
// home imports
import { HomeAccountSelector } from '../_components/home-account-selector';
@@ -17,41 +13,17 @@ import { type UserWorkspace } from '../_lib/server/load-user-workspace';
export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
const { workspace, user, accounts } = props.workspace;
const routes = personalAccountNavigationConfig.routes.reduce<
Array<{
path: string;
label: string;
Icon?: React.ReactNode;
end?: boolean | ((path: string) => boolean);
}>
>((acc, item) => {
if ('children' in item) {
return [...acc, ...item.children];
}
if ('divider' in item) {
return acc;
}
return [...acc, item];
}, []);
return (
<div className={'flex w-full flex-1 justify-between'}>
<div className={'flex items-center space-x-8'}>
<AppLogo />
<BorderedNavigationMenu>
{routes.map((route) => (
<BorderedNavigationMenuItem {...route} key={route.path} />
))}
</BorderedNavigationMenu>
</div>
<div className={'flex justify-end space-x-2.5'}>
<UserNotifications userId={user.id} />
<If condition={featuresFlagConfig.enableTeamAccounts}>
<If condition={featuresFlagConfig.enableTeamAccounts && accounts.length}>
<HomeAccountSelector userId={user.id} accounts={accounts} />
</If>

View File

@@ -28,15 +28,20 @@ async function workspaceLoader() {
const workspacePromise = api.getAccountWorkspace();
const [accounts, workspace, user] = await Promise.all([
// TODO!: remove before deploy to prod
const tempAccountsPromise = () => api.loadTempUserAccounts();
const [accounts, workspace, user, tempVisibleAccounts] = await Promise.all([
accountsPromise(),
workspacePromise,
requireUserInServerComponent(),
tempAccountsPromise()
]);
return {
accounts,
workspace,
user,
tempVisibleAccounts
};
}

View File

@@ -1,4 +1,3 @@
import { PageBody } from '@kit/ui/page';
import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
@@ -6,6 +5,8 @@ import { withI18n } from '~/lib/i18n/with-i18n';
// local imports
import { HomeLayoutPageHeader } from './_components/home-page-header';
import { use } from 'react';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';
export const generateMetadata = async () => {
const i18n = await createI18nServerInstance();
@@ -17,14 +18,21 @@ export const generateMetadata = async () => {
};
function UserHomePage() {
const { tempVisibleAccounts } = use(loadUserWorkspace());
return (
<>
<HomeLayoutPageHeader
title={<Trans i18nKey={'common:routes.home'} />}
description={<Trans i18nKey={'common:homeTabDescription'} />}
/>
{tempVisibleAccounts.length && (
<>
Member of companies:
<pre>{JSON.stringify(tempVisibleAccounts, null, 2)}</pre>
</>
)}
<PageBody></PageBody>
</>
);
}

View File

@@ -4,7 +4,7 @@ import { cookies } from 'next/headers';
import { z } from 'zod';
import { TeamAccountWorkspaceContextProvider } from '@kit/team-accounts/components';
import { CompanyGuard, TeamAccountWorkspaceContextProvider } from '@kit/team-accounts/components';
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
import { SidebarProvider } from '@kit/ui/shadcn-sidebar';
@@ -144,4 +144,4 @@ async function getLayoutState(account: string) {
};
}
export default withI18n(TeamWorkspaceLayout);
export default withI18n(CompanyGuard(TeamWorkspaceLayout));

View File

@@ -9,6 +9,7 @@ import { withI18n } from '~/lib/i18n/with-i18n';
import { DashboardDemo } from './_components/dashboard-demo';
import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header';
import { CompanyGuard } from '@/packages/features/team-accounts/src/components';
interface TeamAccountHomePageProps {
params: Promise<{ account: string }>;
@@ -41,4 +42,4 @@ function TeamAccountHomePage({ params }: TeamAccountHomePageProps) {
);
}
export default withI18n(TeamAccountHomePage);
export default withI18n(CompanyGuard(TeamAccountHomePage));

View File

@@ -109,10 +109,7 @@ async function JoinTeamAccountPage(props: JoinTeamAccountPageProps) {
const signOutNext = `${pathsConfig.auth.signIn}?invite_token=${token}`;
// once the user accepts the invitation, we redirect them to the account home page
const accountHome = pathsConfig.app.accountHome.replace(
'[account]',
invitation.account.slug,
);
const accountHome = pathsConfig.app.home;
const email = auth.data.email ?? '';