diff --git a/apps/web/app/(dashboard)/home/(user)/billing/page.tsx b/apps/web/app/(dashboard)/home/(user)/billing/page.tsx index 51135d1c0..da33f0d76 100644 --- a/apps/web/app/(dashboard)/home/(user)/billing/page.tsx +++ b/apps/web/app/(dashboard)/home/(user)/billing/page.tsx @@ -1,5 +1,3 @@ -import { redirect } from 'next/navigation'; - import { SupabaseClient } from '@supabase/supabase-js'; import { @@ -14,23 +12,15 @@ import { Trans } from '@kit/ui/trans'; import { createPersonalAccountBillingPortalSession } from '~/(dashboard)/home/(user)/billing/server-actions'; import billingConfig from '~/config/billing.config'; -import pathsConfig from '~/config/paths.config'; import { withI18n } from '~/lib/i18n/with-i18n'; -import { loadUserWorkspace } from '../../_lib/load-user-workspace'; import { PersonalAccountCheckoutForm } from './_components/personal-account-checkout-form'; type Subscription = Database['public']['Tables']['subscriptions']['Row']; async function PersonalAccountBillingPage() { const client = getSupabaseServerComponentClient(); - const { session } = await loadUserWorkspace(); - - if (!session?.user) { - redirect(pathsConfig.auth.signIn); - } - - const [subscription, customerId] = await loadData(client, session.user.id); + const [subscription, customerId] = await loadData(client); return ( <> @@ -68,18 +58,26 @@ async function PersonalAccountBillingPage() { export default withI18n(PersonalAccountBillingPage); -function loadData(client: SupabaseClient, userId: string) { +async function loadData(client: SupabaseClient) { + const { data, error } = await client.auth.getUser(); + + if (error ?? !data?.user) { + throw new Error('Authentication required'); + } + + const user = data.user; + const subscription = client .from('subscriptions') .select('*') - .eq('account_id', userId) + .eq('account_id', user.id) .maybeSingle() .then(({ data }) => data); const customer = client .from('billing_customers') .select('customer_id') - .eq('account_id', userId) + .eq('account_id', user.id) .maybeSingle() .then(({ data }) => data?.customer_id); diff --git a/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar-navigation.tsx b/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar-navigation.tsx index b2d5e556b..492c2be68 100644 --- a/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar-navigation.tsx +++ b/apps/web/app/(dashboard)/home/[account]/_components/app-sidebar-navigation.tsx @@ -3,7 +3,7 @@ import { SidebarDivider, SidebarGroup, SidebarItem } from '@kit/ui/sidebar'; import { Trans } from '@kit/ui/trans'; -import { getOrganizationAccountSidebarConfig } from '~/config/organization-account-sidebar.config'; +import { getOrganizationAccountSidebarConfig } from '~/config/team-account-sidebar.config'; export function AppSidebarNavigation({ account, diff --git a/apps/web/app/(dashboard)/home/[account]/_components/mobile-app-navigation.tsx b/apps/web/app/(dashboard)/home/[account]/_components/mobile-app-navigation.tsx index 0c2b79a8d..6b70aa386 100644 --- a/apps/web/app/(dashboard)/home/[account]/_components/mobile-app-navigation.tsx +++ b/apps/web/app/(dashboard)/home/[account]/_components/mobile-app-navigation.tsx @@ -24,8 +24,8 @@ import { import { Trans } from '@kit/ui/trans'; import featureFlagsConfig from '~/config/feature-flags.config'; -import { getOrganizationAccountSidebarConfig } from '~/config/organization-account-sidebar.config'; import pathsConfig from '~/config/paths.config'; +import { getOrganizationAccountSidebarConfig } from '~/config/team-account-sidebar.config'; const features = { enableTeamAccounts: featureFlagsConfig.enableTeamAccounts, diff --git a/apps/web/app/(dashboard)/home/[account]/members/page.tsx b/apps/web/app/(dashboard)/home/[account]/members/page.tsx index 36977dd58..d62fe2029 100644 --- a/apps/web/app/(dashboard)/home/[account]/members/page.tsx +++ b/apps/web/app/(dashboard)/home/[account]/members/page.tsx @@ -1,5 +1,8 @@ +import { SupabaseClient } from '@supabase/supabase-js'; + import { PlusCircle } from 'lucide-react'; +import { Database } from '@kit/supabase/database'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { AccountInvitationsTable, @@ -26,9 +29,20 @@ interface Params { }; } -async function loadAccountMembers(account: string) { - const client = getSupabaseServerComponentClient(); +async function loadUser(client: SupabaseClient) { + const { data, error } = await client.auth.getUser(); + if (error) { + throw error; + } + + return data.user; +} + +async function loadAccountMembers( + client: SupabaseClient, + account: string, +) { const { data, error } = await client.rpc('get_account_members', { account_slug: account, }); @@ -41,9 +55,10 @@ async function loadAccountMembers(account: string) { return data ?? []; } -async function loadInvitations(account: string) { - const client = getSupabaseServerComponentClient(); - +async function loadInvitations( + client: SupabaseClient, + account: string, +) { const { data, error } = await client.rpc('get_account_invitations', { account_slug: account, }); @@ -56,14 +71,22 @@ async function loadInvitations(account: string) { return data ?? []; } -async function TeamAccountMembersPage({ params }: Params) { - const slug = params.account; - - const [{ account, user }, members, invitations] = await Promise.all([ +async function loadData(client: SupabaseClient, slug: string) { + return Promise.all([ loadTeamWorkspace(slug), - loadAccountMembers(slug), - loadInvitations(slug), + loadAccountMembers(client, slug), + loadInvitations(client, slug), + loadUser(client), ]); +} + +async function TeamAccountMembersPage({ params }: Params) { + const client = getSupabaseServerComponentClient(); + + const [{ account }, members, invitations, user] = await loadData( + client, + params.account, + ); const canManageRoles = account.permissions.includes('roles.manage'); const isPrimaryOwner = account.primary_owner_user_id === user.id; diff --git a/apps/web/config/organization-account-sidebar.config.tsx b/apps/web/config/team-account-sidebar.config.tsx similarity index 95% rename from apps/web/config/organization-account-sidebar.config.tsx rename to apps/web/config/team-account-sidebar.config.tsx index f8668ec79..5f0aef6f8 100644 --- a/apps/web/config/organization-account-sidebar.config.tsx +++ b/apps/web/config/team-account-sidebar.config.tsx @@ -7,7 +7,7 @@ import pathsConfig from '~/config/paths.config'; const iconClasses = 'w-4'; -const routes = (account: string) => [ +const getRoutes = (account: string) => [ { label: 'common:dashboardTabLabel', path: pathsConfig.app.accountHome.replace('[account]', account), @@ -41,7 +41,7 @@ const routes = (account: string) => [ export function getOrganizationAccountSidebarConfig(account: string) { return SidebarConfigSchema.parse({ - routes: routes(account), + routes: getRoutes(account), }); } diff --git a/apps/web/public/locales/en/teams.json b/apps/web/public/locales/en/teams.json index ebeb293a7..68d2db00d 100644 --- a/apps/web/public/locales/en/teams.json +++ b/apps/web/public/locales/en/teams.json @@ -9,8 +9,10 @@ "dangerZone": "Danger Zone", "dangerZoneDescription": "This section contains actions that are irreversible" }, - "yourTeam": "Your Teams", - "createTeam": "Create Team", + "yourTeams": "Your Teams", + "createTeam": "Create a Team", + "personalAccount": "Personal Account", + "searchAccount": "Search Account...", "membersTabLabel": "Members", "memberName": "Name", "youLabel": "You", diff --git a/packages/features/accounts/src/components/account-selector.tsx b/packages/features/accounts/src/components/account-selector.tsx index 980ef9be4..8cf9c9685 100644 --- a/packages/features/accounts/src/components/account-selector.tsx +++ b/packages/features/accounts/src/components/account-selector.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'; import { CaretSortIcon, PersonIcon } from '@radix-ui/react-icons'; import { Check, Plus } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar'; import { Button } from '@kit/ui/button'; @@ -59,6 +60,8 @@ export function AccountSelector({ selectedAccount ?? PERSONAL_ACCOUNT_SLUG, ); + const { t } = useTranslation('teams'); + useEffect(() => { setValue(selectedAccount ?? PERSONAL_ACCOUNT_SLUG); }, [selectedAccount]); @@ -105,7 +108,7 @@ export function AccountSelector({ hidden: collapsed, })} > - Personal Account + } @@ -137,7 +140,7 @@ export function AccountSelector({ - + @@ -147,7 +150,9 @@ export function AccountSelector({ > - Personal Account + + + diff --git a/packages/features/accounts/src/components/personal-account-settings/mfa/multi-factor-auth-setup-dialog.tsx b/packages/features/accounts/src/components/personal-account-settings/mfa/multi-factor-auth-setup-dialog.tsx index 52a30e032..fe0daa957 100644 --- a/packages/features/accounts/src/components/personal-account-settings/mfa/multi-factor-auth-setup-dialog.tsx +++ b/packages/features/accounts/src/components/personal-account-settings/mfa/multi-factor-auth-setup-dialog.tsx @@ -364,7 +364,7 @@ function FactorNameForm( }} /> -
+
diff --git a/packages/features/accounts/src/hooks/use-personal-account-data.ts b/packages/features/accounts/src/hooks/use-personal-account-data.ts index efabfb7a8..281df9d99 100644 --- a/packages/features/accounts/src/hooks/use-personal-account-data.ts +++ b/packages/features/accounts/src/hooks/use-personal-account-data.ts @@ -3,16 +3,16 @@ import { useCallback } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useSupabase } from '@kit/supabase/hooks/use-supabase'; +import { useUser } from '@kit/supabase/hooks/use-user'; const queryKey = ['personal-account:data']; export function usePersonalAccountData() { const client = useSupabase(); + const user = useUser(); const queryFn = async () => { - const { data, error } = await client.auth.getSession(); - - if (!data.session || error) { + if (!user.data?.id) { return null; } @@ -25,7 +25,7 @@ export function usePersonalAccountData() { picture_url `, ) - .eq('primary_owner_user_id', data.session.user.id) + .eq('primary_owner_user_id', user.data?.id) .eq('is_personal_account', true) .single(); @@ -39,6 +39,7 @@ export function usePersonalAccountData() { return useQuery({ queryKey, queryFn, + enabled: !!user.data?.id, }); } diff --git a/packages/features/team-accounts/src/server/services/account-members.service.ts b/packages/features/team-accounts/src/server/services/account-members.service.ts index 6e45e8de8..8eb49ce06 100644 --- a/packages/features/team-accounts/src/server/services/account-members.service.ts +++ b/packages/features/team-accounts/src/server/services/account-members.service.ts @@ -12,7 +12,7 @@ export class AccountMembersService { .from('accounts_memberships') .delete() .match({ - id: params.accountId, + account_id: params.accountId, user_id: params.userId, });