B2B-87: add company statistics consent (#29)

* B2B-87: add company statistics consent

* add toggle for company statistics consent under profile

* add toggle for company statistics consent under profile

* add audit logging to accounts

* change policy

* add audit logging to accounts

* remove full account data query and just query the entire account every time

* add comment about consent toggle

* make constants hardcoded, as dynamic ones do not work

* add back pending check

---------

Co-authored-by: Helena <helena@Helenas-MacBook-Pro.local>
This commit is contained in:
Helena
2025-07-03 17:55:23 +03:00
committed by GitHub
parent 517dce3146
commit ad08155063
18 changed files with 298 additions and 36 deletions

View File

@@ -13,10 +13,10 @@ import { personalAccountNavigationConfig } from '~/config/personal-account-navig
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';
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';
function UserHomeLayout({ children }: React.PropsWithChildren) {
const state = use(getLayoutState());
@@ -58,8 +58,8 @@ function HeaderLayout({ children }: React.PropsWithChildren) {
return (
<UserWorkspaceContextProvider value={workspace}>
<Page style={'header'} >
<PageNavigation >
<Page style={'header'}>
<PageNavigation>
<HomeMenuNavigation workspace={workspace} />
</PageNavigation>

View File

@@ -3,11 +3,11 @@ import { Trans } from '@kit/ui/trans';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
import { withI18n } from '~/lib/i18n/with-i18n';
import Dashboard from './_components/dashboard';
import Dashboard from '../_components/dashboard';
// local imports
import { HomeLayoutPageHeader } from './_components/home-page-header';
import { HomeLayoutPageHeader } from '../_components/home-page-header';
import { use } from 'react';
import { loadUserWorkspace } from './_lib/server/load-user-workspace';
import { loadUserWorkspace } from '../_lib/server/load-user-workspace';
import { PageBody } from '@kit/ui/page';
export const generateMetadata = async () => {

View File

@@ -0,0 +1,73 @@
'use client';
import Image from 'next/image';
import { useRouter } from 'next/navigation';
import { CaretRightIcon } from '@radix-ui/react-icons';
import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data';
import { useUpdateAccountData } from '@kit/accounts/hooks/use-update-account';
import { Button } from '@kit/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@kit/ui/dialog';
import { toast } from '@kit/ui/sonner';
import { Trans } from '@kit/ui/trans';
export default function ConsentDialog({ userId }: { userId: string }) {
const router = useRouter();
const updateAccountMutation = useUpdateAccountData(userId);
const revalidateUserDataQuery = useRevalidatePersonalAccountDataQuery();
const updateConsent = (consentToCompanyStatistics: boolean) => {
const promise = updateAccountMutation
.mutateAsync({
has_consent_anonymized_company_statistics: consentToCompanyStatistics,
})
.then(() => {
revalidateUserDataQuery(userId);
});
toast.promise(() => promise, {
success: <Trans i18nKey={'account:updateConsentSuccess'} />,
error: <Trans i18nKey={'account:updateConsentError'} />,
loading: <Trans i18nKey={'account:updateConsentLoading'} />,
});
return router.refresh();
};
return (
<Dialog defaultOpen>
<DialogContent
className="flex max-w-[436px] flex-col items-center gap-4 space-y-4"
disableClose
>
<DialogHeader className="items-center text-center">
<Image alt="Toggle" src="/assets/toggle.png" width={96} height={96} />
<DialogTitle>
<Trans i18nKey="account:consentModal.title" />
</DialogTitle>
<DialogDescription>
<Trans i18nKey="account:consentModal.description" />
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="secondary" onClick={() => updateConsent(false)}>
<Trans i18nKey="account:consentModal.reject" />
</Button>
<Button onClick={() => updateConsent(true)}>
<Trans i18nKey="account:consentModal.accept" />
<CaretRightIcon />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View File

@@ -5,7 +5,7 @@ import { AppLogo } from '~/components/app-logo';
import { ProfileAccountDropdownContainer } from '~/components/personal-account-dropdown-container';
import { Search } from '~/components/ui/search';
import { SIDEBAR_WIDTH } from '../../../../packages/ui/src/shadcn/constants';
import { SIDEBAR_WIDTH_PROPERTY } from '../../../../packages/ui/src/shadcn/constants';
// home imports
import { UserNotifications } from '../_components/user-notifications';
import { type UserWorkspace } from '../_lib/server/load-user-workspace';
@@ -17,7 +17,7 @@ export function HomeMenuNavigation(props: { workspace: UserWorkspace }) {
return (
<div className={'flex w-full flex-1 items-center justify-between gap-3'}>
<div className={`flex items-center w-[${SIDEBAR_WIDTH}]`}>
<div className={`flex items-center ${SIDEBAR_WIDTH_PROPERTY}`}>
<AppLogo />
</div>

View File

@@ -0,0 +1,21 @@
import { cache } from 'react';
import { createAccountsApi } from '@kit/accounts/api';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export type UserAccount = Awaited<ReturnType<typeof loadUserAccount>>;
/**
* @name loadUserAccount
* @description
* Load the user account. It's a cached per-request function that fetches the user workspace data.
* It can be used across the server components to load the user workspace data.
*/
export const loadUserAccount = cache(accountLoader);
async function accountLoader(accountId: string) {
const client = getSupabaseServerClient();
const api = createAccountsApi(client);
return api.getAccount(accountId);
}

24
app/home/layout.tsx Normal file
View File

@@ -0,0 +1,24 @@
import { requireUserInServerComponent } from '../../lib/server/require-user-in-server-component';
import ConsentDialog from './(user)/_components/consent-dialog';
import { loadUserAccount } from './(user)/_lib/server/load-user-account';
export default async function HomeLayout({
children,
}: {
children: React.ReactNode;
}) {
const user = await requireUserInServerComponent();
const account = user?.identities?.[0]?.id
? await loadUserAccount(user?.identities?.[0]?.id)
: null;
if (account && account?.has_consent_anonymized_company_statistics === null) {
return (
<div className="container">
<ConsentDialog userId={user.id} />
</div>
);
}
return <>{children}</>;
}