Remove redundant files and update pnpm lockfile
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
import { PageHeader } from '@kit/ui/page';
|
||||
|
||||
import { UserNotifications } from '~/(dashboard)/home/(user)/_components/user-notifications';
|
||||
|
||||
import { UserLayoutMobileNavigation } from './user-layout-mobile-navigation';
|
||||
|
||||
export function UserAccountHeader(
|
||||
props: React.PropsWithChildren<{
|
||||
title: string | React.ReactNode;
|
||||
description?: string | React.ReactNode;
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<PageHeader
|
||||
title={props.title}
|
||||
description={props.description}
|
||||
mobileNavigation={<UserLayoutMobileNavigation />}
|
||||
>
|
||||
<div className={'flex space-x-4'}>
|
||||
{props.children}
|
||||
|
||||
<UserNotifications />
|
||||
</div>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Page } from '@kit/ui/page';
|
||||
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { HomeSidebar } from './_components/home-sidebar';
|
||||
|
||||
function UserHomeLayout({ children }: React.PropsWithChildren) {
|
||||
return <Page sidebar={<HomeSidebar />}>{children}</Page>;
|
||||
}
|
||||
|
||||
export default withI18n(UserHomeLayout);
|
||||
@@ -1,30 +0,0 @@
|
||||
import { PageHeader } from '@kit/ui/page';
|
||||
|
||||
import { AccountNotifications } from '~/(dashboard)/home/[account]/_components/account-notifications';
|
||||
|
||||
import { AccountLayoutMobileNavigation } from './account-layout-mobile-navigation';
|
||||
|
||||
export function AccountLayoutHeader({
|
||||
children,
|
||||
title,
|
||||
description,
|
||||
account,
|
||||
}: React.PropsWithChildren<{
|
||||
title: string | React.ReactNode;
|
||||
description?: string | React.ReactNode;
|
||||
account: string;
|
||||
}>) {
|
||||
return (
|
||||
<PageHeader
|
||||
title={title}
|
||||
description={description}
|
||||
mobileNavigation={<AccountLayoutMobileNavigation account={account} />}
|
||||
>
|
||||
<div className={'flex space-x-4'}>
|
||||
{children}
|
||||
|
||||
<AccountNotifications accountId={account} />
|
||||
</div>
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { Page } from '@kit/ui/page';
|
||||
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { AccountLayoutSidebar } from './_components/account-layout-sidebar';
|
||||
import { loadTeamWorkspace } from './_lib/server/team-account-workspace.loader';
|
||||
|
||||
interface Params {
|
||||
account: string;
|
||||
}
|
||||
|
||||
function TeamWorkspaceLayout({
|
||||
children,
|
||||
params,
|
||||
}: React.PropsWithChildren<{
|
||||
params: Params;
|
||||
}>) {
|
||||
const data = use(loadTeamWorkspace(params.account));
|
||||
|
||||
const accounts = data.accounts.map(({ name, slug, picture_url }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Page
|
||||
sidebar={
|
||||
<AccountLayoutSidebar
|
||||
collapsed={false}
|
||||
account={params.account}
|
||||
accounts={accounts}
|
||||
user={data?.user ?? null}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(TeamWorkspaceLayout);
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Page, PageBody, PageHeader } from '@kit/ui/page';
|
||||
import {
|
||||
Page,
|
||||
PageBody,
|
||||
PageHeader,
|
||||
PageMobileNavigation,
|
||||
PageNavigation,
|
||||
} from '@kit/ui/page';
|
||||
|
||||
import { AdminSidebar } from '~/admin/_components/admin-sidebar';
|
||||
import { AdminMobileNavigation } from '~/admin/_components/mobile-navigation';
|
||||
@@ -9,13 +15,20 @@ export const metadata = {
|
||||
|
||||
export default function AdminLayout(props: React.PropsWithChildren) {
|
||||
return (
|
||||
<Page sidebar={<AdminSidebar />}>
|
||||
<Page style={'sidebar'}>
|
||||
<PageHeader
|
||||
mobileNavigation={<AdminMobileNavigation />}
|
||||
title={'Super Admin'}
|
||||
description={`Your SaaS stats at a glance`}
|
||||
/>
|
||||
|
||||
<PageNavigation>
|
||||
<AdminSidebar />
|
||||
</PageNavigation>
|
||||
|
||||
<PageMobileNavigation>
|
||||
<AdminMobileNavigation />
|
||||
</PageMobileNavigation>
|
||||
|
||||
<PageBody>{props.children}</PageBody>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ const ErrorPage = ({
|
||||
|
||||
<div
|
||||
className={
|
||||
'm-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
'container m-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
}
|
||||
>
|
||||
<div className={'flex flex-col items-center space-y-16'}>
|
||||
|
||||
@@ -11,7 +11,7 @@ const features = {
|
||||
enableTeamCreation: featureFlagsConfig.enableTeamCreation,
|
||||
};
|
||||
|
||||
export function HomeSidebarAccountSelector(props: {
|
||||
export function HomeAccountSelector(props: {
|
||||
accounts: Array<{
|
||||
label: string | null;
|
||||
value: string | null;
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
BorderedNavigationMenu,
|
||||
BorderedNavigationMenuItem,
|
||||
} from '@kit/ui/bordered-navigation-menu';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
|
||||
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
|
||||
|
||||
// home imports
|
||||
import { HomeAccountSelector } from '../_components/home-account-selector';
|
||||
import { UserNotifications } from '../_components/user-notifications';
|
||||
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 | undefined;
|
||||
}>
|
||||
>((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-4'}>
|
||||
<AppLogo />
|
||||
|
||||
<BorderedNavigationMenu>
|
||||
{routes.map((route) => (
|
||||
<BorderedNavigationMenuItem {...route} key={route.path} />
|
||||
))}
|
||||
</BorderedNavigationMenu>
|
||||
</div>
|
||||
|
||||
<div className={'flex justify-end space-x-4'}>
|
||||
<HomeAccountSelector accounts={accounts} collapsed={false} />
|
||||
|
||||
<UserNotifications userId={user.id} />
|
||||
|
||||
<ProfileAccountDropdownContainer
|
||||
collapsed={true}
|
||||
user={user}
|
||||
account={workspace}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -8,18 +8,26 @@ import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { personalAccountSidebarConfig } from '~/config/personal-account-sidebar.config';
|
||||
import featuresFlagConfig from '~/config/feature-flags.config';
|
||||
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
|
||||
|
||||
export function UserLayoutMobileNavigation() {
|
||||
// home imports
|
||||
import { HomeAccountSelector } from '../_components/home-account-selector';
|
||||
import type { UserWorkspace } from '../_lib/server/load-user-workspace';
|
||||
|
||||
export function HomeMobileNavigation(props: { workspace: UserWorkspace }) {
|
||||
const signOut = useSignOut();
|
||||
|
||||
const Links = personalAccountSidebarConfig.routes.map((item, index) => {
|
||||
const Links = personalAccountNavigationConfig.routes.map((item, index) => {
|
||||
if ('children' in item) {
|
||||
return item.children.map((child) => {
|
||||
return (
|
||||
@@ -54,7 +62,22 @@ export function UserLayoutMobileNavigation() {
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent sideOffset={10} className={'w-screen rounded-none'}>
|
||||
{Links}
|
||||
<If condition={featuresFlagConfig.enableTeamAccounts}>
|
||||
<DropdownMenuGroup>
|
||||
<DropdownMenuLabel>
|
||||
<Trans i18nKey={'common:yourAccounts'} />
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<HomeAccountSelector
|
||||
accounts={props.workspace.accounts}
|
||||
collapsed={false}
|
||||
/>
|
||||
</DropdownMenuGroup>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
</If>
|
||||
|
||||
<DropdownMenuGroup>{Links}</DropdownMenuGroup>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
14
apps/web/app/home/(user)/_components/home-page-header.tsx
Normal file
14
apps/web/app/home/(user)/_components/home-page-header.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { PageHeader } from '@kit/ui/page';
|
||||
|
||||
export function HomeLayoutPageHeader(
|
||||
props: React.PropsWithChildren<{
|
||||
title: string | React.ReactNode;
|
||||
description: string | React.ReactNode;
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<PageHeader title={props.title} description={props.description}>
|
||||
{props.children}
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
@@ -1,45 +1,37 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Sidebar, SidebarContent, SidebarNavigation } from '@kit/ui/sidebar';
|
||||
|
||||
import { loadUserWorkspace } from '~/(dashboard)/home/(user)/_lib/server/load-user-workspace';
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
|
||||
import featuresFlagConfig from '~/config/feature-flags.config';
|
||||
import { personalAccountSidebarConfig } from '~/config/personal-account-sidebar.config';
|
||||
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
|
||||
|
||||
// home imports
|
||||
import { HomeSidebarAccountSelector } from '../_components/home-sidebar-account-selector';
|
||||
import type { UserWorkspace } from '../_lib/server/load-user-workspace';
|
||||
import { HomeAccountSelector } from './home-account-selector';
|
||||
|
||||
export function HomeSidebar() {
|
||||
const collapsed = getSidebarCollapsed();
|
||||
const { accounts, user, workspace } = use(loadUserWorkspace());
|
||||
export function HomeSidebar(props: { workspace: UserWorkspace }) {
|
||||
const { workspace, user, accounts } = props.workspace;
|
||||
|
||||
return (
|
||||
<Sidebar collapsed={collapsed}>
|
||||
<Sidebar>
|
||||
<SidebarContent className={'h-16 justify-center'}>
|
||||
<If
|
||||
condition={featuresFlagConfig.enableTeamAccounts}
|
||||
fallback={<AppLogo className={'py-2'} />}
|
||||
>
|
||||
<HomeSidebarAccountSelector
|
||||
collapsed={collapsed}
|
||||
accounts={accounts}
|
||||
/>
|
||||
<HomeAccountSelector collapsed={false} accounts={accounts} />
|
||||
</If>
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarContent className={`mt-5 h-[calc(100%-160px)] overflow-y-auto`}>
|
||||
<SidebarNavigation config={personalAccountSidebarConfig} />
|
||||
<SidebarNavigation config={personalAccountNavigationConfig} />
|
||||
</SidebarContent>
|
||||
|
||||
<div className={'absolute bottom-4 left-0 w-full'}>
|
||||
<SidebarContent>
|
||||
<ProfileAccountDropdownContainer
|
||||
collapsed={collapsed}
|
||||
collapsed={false}
|
||||
user={user}
|
||||
account={workspace}
|
||||
/>
|
||||
@@ -48,7 +40,3 @@ export function HomeSidebar() {
|
||||
</Sidebar>
|
||||
);
|
||||
}
|
||||
|
||||
function getSidebarCollapsed() {
|
||||
return cookies().get('sidebar-collapsed')?.value === 'true';
|
||||
}
|
||||
@@ -1,21 +1,15 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { NotificationsPopover } from '@kit/notifications/components';
|
||||
|
||||
import featuresFlagConfig from '~/config/feature-flags.config';
|
||||
|
||||
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
|
||||
|
||||
export function UserNotifications() {
|
||||
const { user } = use(loadUserWorkspace());
|
||||
|
||||
export function UserNotifications(props: { userId: string }) {
|
||||
if (!featuresFlagConfig.enableNotifications) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationsPopover
|
||||
accountIds={[user.id]}
|
||||
accountIds={[props.userId]}
|
||||
realtime={featuresFlagConfig.realtimeNotifications}
|
||||
/>
|
||||
);
|
||||
@@ -7,6 +7,8 @@ import featureFlagsConfig from '~/config/feature-flags.config';
|
||||
|
||||
const shouldLoadAccounts = featureFlagsConfig.enableTeamAccounts;
|
||||
|
||||
export type UserWorkspace = Awaited<ReturnType<typeof loadUserWorkspace>>;
|
||||
|
||||
/**
|
||||
* @name loadUserWorkspace
|
||||
* @description
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
// We reuse the page from the billing module
|
||||
// as there is no need to create a new one.
|
||||
import BillingErrorPage from '~/(dashboard)/home/[account]/billing/error';
|
||||
import BillingErrorPage from '~/home/[account]/billing/error';
|
||||
|
||||
export default BillingErrorPage;
|
||||
@@ -15,10 +15,10 @@ import billingConfig from '~/config/billing.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { UserAccountHeader } from '../_components/user-account-header';
|
||||
// local imports
|
||||
import { HomeLayoutPageHeader } from '../_components/home-page-header';
|
||||
import { createPersonalAccountBillingPortalSession } from '../billing/_lib/server/server-actions';
|
||||
import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form';
|
||||
// user billing imports
|
||||
import { loadPersonalAccountBillingPageData } from './_lib/server/personal-account-billing-page.loader';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
@@ -44,7 +44,7 @@ async function PersonalAccountBillingPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserAccountHeader
|
||||
<HomeLayoutPageHeader
|
||||
title={<Trans i18nKey={'common:billingTabLabel'} />}
|
||||
description={<Trans i18nKey={'common:billingTabDescription'} />}
|
||||
/>
|
||||
@@ -1,5 +1,5 @@
|
||||
// We reuse the page from the billing module
|
||||
// as there is no need to create a new one.
|
||||
import ReturnCheckoutSessionPage from '~/(dashboard)/home/[account]/billing/return/page';
|
||||
import ReturnCheckoutSessionPage from '~/home/[account]/billing/return/page';
|
||||
|
||||
export default ReturnCheckoutSessionPage;
|
||||
48
apps/web/app/home/(user)/layout.tsx
Normal file
48
apps/web/app/home/(user)/layout.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { personalAccountNavigationConfig } from '~/config/personal-account-navigation.config';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
// home imports
|
||||
import { HomeMenuNavigation } from './_components/home-menu-navigation';
|
||||
import { HomeMobileNavigation } from './_components/home-mobile-navigation';
|
||||
import { HomeSidebar } from './_components/home-sidebar';
|
||||
import { loadUserWorkspace } from './_lib/server/load-user-workspace';
|
||||
|
||||
const style = personalAccountNavigationConfig.style;
|
||||
|
||||
function UserHomeLayout({ children }: React.PropsWithChildren) {
|
||||
const workspace = use(loadUserWorkspace());
|
||||
|
||||
return (
|
||||
<Page style={style}>
|
||||
<PageNavigation>
|
||||
<If condition={style === 'header'}>
|
||||
<HomeMenuNavigation workspace={workspace} />
|
||||
</If>
|
||||
|
||||
<If condition={style === 'sidebar'}>
|
||||
<HomeSidebar workspace={workspace} />
|
||||
</If>
|
||||
</PageNavigation>
|
||||
|
||||
<PageMobileNavigation className={'flex items-center justify-between'}>
|
||||
<div>
|
||||
<AppLogo />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<HomeMobileNavigation workspace={workspace} />
|
||||
</div>
|
||||
</PageMobileNavigation>
|
||||
|
||||
{children}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(UserHomeLayout);
|
||||
@@ -1,10 +1,12 @@
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UserAccountHeader } from '~/(dashboard)/home/(user)/_components/user-account-header';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
// local imports
|
||||
import { HomeLayoutPageHeader } from './_components/home-page-header';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
const i18n = await createI18nServerInstance();
|
||||
const title = i18n.t('account:homePage');
|
||||
@@ -17,7 +19,7 @@ export const generateMetadata = async () => {
|
||||
function UserHomePage() {
|
||||
return (
|
||||
<>
|
||||
<UserAccountHeader
|
||||
<HomeLayoutPageHeader
|
||||
title={<Trans i18nKey={'common:homeTabLabel'} />}
|
||||
description={<Trans i18nKey={'common:homeTabDescription'} />}
|
||||
/>
|
||||
@@ -1,12 +1,14 @@
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { UserAccountHeader } from '~/(dashboard)/home/(user)/_components/user-account-header';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
// local imports
|
||||
import { HomeLayoutPageHeader } from '../_components/home-page-header';
|
||||
|
||||
function UserSettingsLayout(props: React.PropsWithChildren) {
|
||||
return (
|
||||
<>
|
||||
<UserAccountHeader
|
||||
<HomeLayoutPageHeader
|
||||
title={<Trans i18nKey={'account:accountTabLabel'} />}
|
||||
description={<Trans i18nKey={'account:accountTabDescription'} />}
|
||||
/>
|
||||
@@ -26,7 +26,7 @@ export const generateMetadata = async () => {
|
||||
function PersonalAccountSettingsPage() {
|
||||
return (
|
||||
<PageBody>
|
||||
<div className={'mx-auto flex w-full flex-1 flex-col lg:max-w-2xl'}>
|
||||
<div className={'flex w-full flex-1 flex-col lg:max-w-2xl'}>
|
||||
<PersonalAccountSettingsContainer features={features} paths={paths} />
|
||||
</div>
|
||||
</PageBody>
|
||||
@@ -0,0 +1,39 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { AccountSelector } from '@kit/accounts/account-selector';
|
||||
|
||||
import featureFlagsConfig from '~/config/feature-flags.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
|
||||
const features = {
|
||||
enableTeamCreation: featureFlagsConfig.enableTeamCreation,
|
||||
};
|
||||
|
||||
export function TeamAccountAccountsSelector(params: {
|
||||
selectedAccount: string;
|
||||
accounts: Array<{
|
||||
label: string | null;
|
||||
value: string | null;
|
||||
image: string | null;
|
||||
}>;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<AccountSelector
|
||||
selectedAccount={params.selectedAccount}
|
||||
accounts={params.accounts}
|
||||
collapsed={false}
|
||||
features={features}
|
||||
onAccountChange={(value) => {
|
||||
const path = value
|
||||
? pathsConfig.app.accountHome.replace('[account]', value)
|
||||
: pathsConfig.app.home;
|
||||
|
||||
router.replace(path);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -25,14 +25,14 @@ import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import featureFlagsConfig from '~/config/feature-flags.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-sidebar.config';
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
|
||||
|
||||
const features = {
|
||||
enableTeamAccounts: featureFlagsConfig.enableTeamAccounts,
|
||||
enableTeamCreation: featureFlagsConfig.enableTeamCreation,
|
||||
};
|
||||
|
||||
export const AccountLayoutMobileNavigation = (
|
||||
export const TeamAccountLayoutMobileNavigation = (
|
||||
props: React.PropsWithChildren<{
|
||||
account: string;
|
||||
}>,
|
||||
@@ -0,0 +1,15 @@
|
||||
import { PageHeader } from '@kit/ui/page';
|
||||
|
||||
export function TeamAccountLayoutPageHeader(
|
||||
props: React.PropsWithChildren<{
|
||||
title: string | React.ReactNode;
|
||||
description: string | React.ReactNode;
|
||||
account: string;
|
||||
}>,
|
||||
) {
|
||||
return (
|
||||
<PageHeader title={props.title} description={props.description}>
|
||||
{props.children}
|
||||
</PageHeader>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { SidebarDivider, SidebarGroup, SidebarItem } from '@kit/ui/sidebar';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-sidebar.config';
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
|
||||
|
||||
export function AccountLayoutSidebarNavigation({
|
||||
export function TeamAccountLayoutSidebarNavigation({
|
||||
account,
|
||||
}: React.PropsWithChildren<{
|
||||
account: string;
|
||||
@@ -1,12 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { User } from '@supabase/supabase-js';
|
||||
|
||||
import { ArrowLeftCircle, ArrowRightCircle } from 'lucide-react';
|
||||
|
||||
import { AccountSelector } from '@kit/accounts/account-selector';
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Sidebar, SidebarContent } from '@kit/ui/sidebar';
|
||||
import {
|
||||
@@ -19,10 +16,9 @@ import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { ProfileAccountDropdownContainer } from '~/components//personal-account-dropdown-container';
|
||||
import featureFlagsConfig from '~/config/feature-flags.config';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
|
||||
import { AccountLayoutSidebarNavigation } from './account-layout-sidebar-navigation';
|
||||
import { TeamAccountAccountsSelector } from '../_components/team-account-accounts-selector';
|
||||
import { TeamAccountLayoutSidebarNavigation } from './team-account-layout-sidebar-navigation';
|
||||
|
||||
type AccountModel = {
|
||||
label: string | null;
|
||||
@@ -30,16 +26,11 @@ type AccountModel = {
|
||||
image: string | null;
|
||||
};
|
||||
|
||||
const features = {
|
||||
enableTeamAccounts: featureFlagsConfig.enableTeamAccounts,
|
||||
enableTeamCreation: featureFlagsConfig.enableTeamCreation,
|
||||
};
|
||||
|
||||
export function AccountLayoutSidebar(props: {
|
||||
export function TeamAccountLayoutSidebar(props: {
|
||||
account: string;
|
||||
accounts: AccountModel[];
|
||||
collapsed: boolean;
|
||||
user: User | null;
|
||||
user: User;
|
||||
}) {
|
||||
return (
|
||||
<Sidebar collapsed={props.collapsed}>
|
||||
@@ -62,39 +53,31 @@ function SidebarContainer(props: {
|
||||
collapsed: boolean;
|
||||
setCollapsed: (collapsed: boolean) => void;
|
||||
collapsible?: boolean;
|
||||
user: User | null;
|
||||
user: User;
|
||||
}) {
|
||||
const { account, accounts } = props;
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarContent className={'h-16 justify-center'}>
|
||||
<AccountSelector
|
||||
<SidebarContent className={'mt-4 justify-center'}>
|
||||
<TeamAccountAccountsSelector
|
||||
selectedAccount={account}
|
||||
accounts={accounts}
|
||||
collapsed={props.collapsed}
|
||||
features={features}
|
||||
onAccountChange={(value) => {
|
||||
const path = value
|
||||
? pathsConfig.app.accountHome.replace('[account]', value)
|
||||
: pathsConfig.app.home;
|
||||
|
||||
router.replace(path);
|
||||
}}
|
||||
/>
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarContent className={`mt-5 h-[calc(100%-160px)] overflow-y-auto`}>
|
||||
<AccountLayoutSidebarNavigation account={account} />
|
||||
<TeamAccountLayoutSidebarNavigation account={account} />
|
||||
</SidebarContent>
|
||||
|
||||
<div className={'absolute bottom-4 left-0 w-full'}>
|
||||
<SidebarContent>
|
||||
<ProfileAccountDropdownContainer
|
||||
user={props.user}
|
||||
collapsed={props.collapsed}
|
||||
/>
|
||||
<div className={'flex space-x-2'}>
|
||||
<ProfileAccountDropdownContainer
|
||||
user={props.user}
|
||||
collapsed={props.collapsed}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<If condition={props.collapsible}>
|
||||
<AppSidebarFooterMenu
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
BorderedNavigationMenu,
|
||||
BorderedNavigationMenuItem,
|
||||
} from '@kit/ui/bordered-navigation-menu';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
|
||||
import { TeamAccountAccountsSelector } from '~/home/[account]/_components/team-account-accounts-selector';
|
||||
|
||||
// local imports
|
||||
import { TeamAccountWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
import { TeamAccountNotifications } from './team-account-notifications';
|
||||
|
||||
export function TeamAccountNavigationMenu(props: {
|
||||
workspace: TeamAccountWorkspace;
|
||||
}) {
|
||||
const { account, user, accounts } = props.workspace;
|
||||
|
||||
const routes = getTeamAccountSidebarConfig(account.slug).routes.reduce<
|
||||
Array<{
|
||||
path: string;
|
||||
label: string;
|
||||
Icon?: React.ReactNode;
|
||||
end?: boolean | undefined;
|
||||
}>
|
||||
>((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-4'}>
|
||||
<AppLogo />
|
||||
|
||||
<BorderedNavigationMenu>
|
||||
{routes.map((route) => (
|
||||
<BorderedNavigationMenuItem {...route} key={route.path} />
|
||||
))}
|
||||
</BorderedNavigationMenu>
|
||||
</div>
|
||||
|
||||
<div className={'flex justify-end space-x-4'}>
|
||||
<TeamAccountAccountsSelector
|
||||
selectedAccount={account.id}
|
||||
accounts={accounts.map((account) => ({
|
||||
label: account.name,
|
||||
value: account.id,
|
||||
image: account.picture_url,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<TeamAccountNotifications accountId={account.id} userId={user.id} />
|
||||
|
||||
<ProfileAccountDropdownContainer
|
||||
collapsed={true}
|
||||
user={user}
|
||||
account={account}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +1,18 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { NotificationsPopover } from '@kit/notifications/components';
|
||||
|
||||
import featuresFlagConfig from '~/config/feature-flags.config';
|
||||
|
||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
|
||||
export function AccountNotifications(params: { accountId: string }) {
|
||||
const { user, account } = use(loadTeamWorkspace(params.accountId));
|
||||
|
||||
export function TeamAccountNotifications(params: {
|
||||
userId: string;
|
||||
accountId: string;
|
||||
}) {
|
||||
if (!featuresFlagConfig.enableNotifications) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NotificationsPopover
|
||||
accountIds={[user.id, account.id]}
|
||||
accountIds={[params.userId, params.accountId]}
|
||||
realtime={featuresFlagConfig.realtimeNotifications}
|
||||
/>
|
||||
);
|
||||
@@ -9,6 +9,10 @@ import { createTeamAccountsApi } from '@kit/team-accounts/api';
|
||||
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
|
||||
export type TeamAccountWorkspace = Awaited<
|
||||
ReturnType<typeof loadTeamWorkspace>
|
||||
>;
|
||||
|
||||
/**
|
||||
* Load the account workspace data.
|
||||
* We place this function into a separate file so it can be reused in multiple places across the server components.
|
||||
@@ -15,7 +15,8 @@ import billingConfig from '~/config/billing.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { AccountLayoutHeader } from '../_components/account-layout-header';
|
||||
// local imports
|
||||
import { TeamAccountLayoutPageHeader } from '../_components/team-account-layout-page-header';
|
||||
import { loadTeamAccountBillingPage } from '../_lib/server/team-account-billing-page.loader';
|
||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
import { TeamAccountCheckoutForm } from './_components/team-account-checkout-form';
|
||||
@@ -72,10 +73,10 @@ async function TeamAccountBillingPage({ params }: Params) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AccountLayoutHeader
|
||||
<TeamAccountLayoutPageHeader
|
||||
account={params.account}
|
||||
title={<Trans i18nKey={'common:billingTabLabel'} />}
|
||||
description={<Trans i18nKey={'common:billingTabDescription'} />}
|
||||
account={params.account}
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
66
apps/web/app/home/[account]/layout.tsx
Normal file
66
apps/web/app/home/[account]/layout.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { use } from 'react';
|
||||
|
||||
import { If } from '@kit/ui/if';
|
||||
import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page';
|
||||
|
||||
import { AppLogo } from '~/components/app-logo';
|
||||
import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config';
|
||||
import { TeamAccountNotifications } from '~/home/[account]/_components/team-account-notifications';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
// local imports
|
||||
import { TeamAccountLayoutMobileNavigation } from './_components/team-account-layout-mobile-navigation';
|
||||
import { TeamAccountLayoutSidebar } from './_components/team-account-layout-sidebar';
|
||||
import { TeamAccountNavigationMenu } from './_components/team-account-navigation-menu';
|
||||
import { loadTeamWorkspace } from './_lib/server/team-account-workspace.loader';
|
||||
|
||||
interface Params {
|
||||
account: string;
|
||||
}
|
||||
|
||||
function TeamWorkspaceLayout({
|
||||
children,
|
||||
params,
|
||||
}: React.PropsWithChildren<{
|
||||
params: Params;
|
||||
}>) {
|
||||
const data = use(loadTeamWorkspace(params.account));
|
||||
const style = getTeamAccountSidebarConfig(params.account).style;
|
||||
|
||||
const accounts = data.accounts.map(({ name, slug, picture_url }) => ({
|
||||
label: name,
|
||||
value: slug,
|
||||
image: picture_url,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Page style={style}>
|
||||
<PageNavigation>
|
||||
<If condition={style === 'sidebar'}>
|
||||
<TeamAccountLayoutSidebar
|
||||
collapsed={false}
|
||||
account={params.account}
|
||||
accounts={accounts}
|
||||
user={data.user}
|
||||
/>
|
||||
</If>
|
||||
|
||||
<If condition={style === 'header'}>
|
||||
<TeamAccountNavigationMenu workspace={data} />
|
||||
</If>
|
||||
</PageNavigation>
|
||||
|
||||
<PageMobileNavigation className={'flex items-center justify-between'}>
|
||||
<AppLogo />
|
||||
|
||||
<div className={'flex space-x-4'}>
|
||||
<TeamAccountLayoutMobileNavigation account={params.account} />
|
||||
</div>
|
||||
</PageMobileNavigation>
|
||||
|
||||
{children}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
export default withI18n(TeamWorkspaceLayout);
|
||||
@@ -21,7 +21,8 @@ import { Trans } from '@kit/ui/trans';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { AccountLayoutHeader } from '../_components/account-layout-header';
|
||||
// local imports
|
||||
import { TeamAccountLayoutPageHeader } from '../_components/team-account-layout-page-header';
|
||||
import { loadMembersPageData } from './_lib/server/members-page.loader';
|
||||
|
||||
interface Params {
|
||||
@@ -53,16 +54,14 @@ async function TeamAccountMembersPage({ params }: Params) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AccountLayoutHeader
|
||||
<TeamAccountLayoutPageHeader
|
||||
title={<Trans i18nKey={'common:membersTabLabel'} />}
|
||||
description={<Trans i18nKey={'common:membersTabDescription'} />}
|
||||
account={params.account}
|
||||
account={account.slug}
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<div
|
||||
className={'mx-auto flex w-full max-w-3xl flex-col space-y-6 pb-32'}
|
||||
>
|
||||
<div className={'flex w-full max-w-4xl flex-col space-y-6 pb-32'}>
|
||||
<Card>
|
||||
<CardHeader className={'flex flex-row justify-between'}>
|
||||
<div className={'flex flex-col space-y-1.5'}>
|
||||
@@ -4,13 +4,18 @@ import { PlusCircle } from 'lucide-react';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import Spinner from '@kit/ui/spinner';
|
||||
import { Spinner } from '@kit/ui/spinner';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { AccountLayoutHeader } from '~/(dashboard)/home/[account]/_components/account-layout-header';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
|
||||
import { TeamAccountLayoutPageHeader } from './_components/team-account-layout-page-header';
|
||||
|
||||
interface Params {
|
||||
account: string;
|
||||
}
|
||||
|
||||
const DashboardDemo = loadDynamic(
|
||||
() => import('./_components/dashboard-demo'),
|
||||
{
|
||||
@@ -41,25 +46,19 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
};
|
||||
|
||||
function TeamAccountHomePage({
|
||||
params,
|
||||
}: {
|
||||
params: {
|
||||
account: string;
|
||||
};
|
||||
}) {
|
||||
function TeamAccountHomePage({ params }: { params: Params }) {
|
||||
return (
|
||||
<>
|
||||
<AccountLayoutHeader
|
||||
<TeamAccountLayoutPageHeader
|
||||
account={params.account}
|
||||
title={<Trans i18nKey={'common:dashboardTabLabel'} />}
|
||||
description={<Trans i18nKey={'common:dashboardTabDescription'} />}
|
||||
account={params.account}
|
||||
>
|
||||
<Button size={'sm'}>
|
||||
<PlusCircle className={'mr-1 h-4'} />
|
||||
<span>Add Widget</span>
|
||||
</Button>
|
||||
</AccountLayoutHeader>
|
||||
</TeamAccountLayoutPageHeader>
|
||||
|
||||
<PageBody>
|
||||
<DashboardDemo />
|
||||
@@ -5,7 +5,8 @@ import { Trans } from '@kit/ui/trans';
|
||||
import pathsConfig from '~/config/paths.config';
|
||||
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||
|
||||
import { AccountLayoutHeader } from '../_components/account-layout-header';
|
||||
// local imports
|
||||
import { TeamAccountLayoutPageHeader } from '../_components/team-account-layout-page-header';
|
||||
import { loadTeamWorkspace } from '../_lib/server/team-account-workspace.loader';
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
@@ -40,18 +41,14 @@ async function TeamAccountSettingsPage(props: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AccountLayoutHeader
|
||||
<TeamAccountLayoutPageHeader
|
||||
account={account.slug}
|
||||
title={<Trans i18nKey={'teams:settings.pageTitle'} />}
|
||||
description={<Trans i18nKey={'teams:settings.pageDescription'} />}
|
||||
account={props.params.account}
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<div
|
||||
className={
|
||||
'container flex w-full max-w-2xl flex-1 flex-col items-center p-0'
|
||||
}
|
||||
>
|
||||
<div className={'flex max-w-2xl flex-1 flex-col'}>
|
||||
<TeamAccountSettingsContainer account={account} paths={paths} />
|
||||
</div>
|
||||
</PageBody>
|
||||
@@ -33,12 +33,12 @@ const NotFoundPage = async () => {
|
||||
|
||||
<div
|
||||
className={
|
||||
'm-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
'container m-auto flex w-full flex-1 flex-col items-center justify-center'
|
||||
}
|
||||
>
|
||||
<div className={'flex flex-col items-center space-y-12'}>
|
||||
<div>
|
||||
<h1 className={'font-heading text-9xl font-extrabold'}>
|
||||
<h1 className={'font-heading text-8xl font-extrabold xl:text-9xl'}>
|
||||
<Trans i18nKey={'common:pageNotFoundHeading'} />
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user