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:
@@ -16,6 +16,7 @@ import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { usePersonalAccountData } from '../../hooks/use-personal-account-data';
|
||||
import { AccountDangerZone } from './account-danger-zone';
|
||||
import ConsentToggle from './consent/consent-toggle';
|
||||
import { UpdateEmailFormContainer } from './email/update-email-form-container';
|
||||
import { MultiFactorAuthFactorsList } from './mfa/multi-factor-auth-list';
|
||||
import { UpdatePasswordFormContainer } from './password/update-password-container';
|
||||
@@ -150,6 +151,32 @@ export function PersonalAccountSettingsContainer(
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<p className="font-medium">
|
||||
<Trans
|
||||
i18nKey={'account:consentToAnonymizedCompanyData.label'}
|
||||
/>
|
||||
</p>
|
||||
|
||||
<CardDescription>
|
||||
<Trans
|
||||
i18nKey={'account:consentToAnonymizedCompanyData.description'}
|
||||
/>
|
||||
</CardDescription>
|
||||
</div>
|
||||
<ConsentToggle
|
||||
userId={props.userId}
|
||||
initialState={
|
||||
!!user.data.has_consent_anonymized_company_statistics
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
<If condition={props.features.enableAccountDeletion}>
|
||||
<Card className={'border-destructive'}>
|
||||
<CardHeader>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { Switch } from '@kit/ui/switch';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
import { useRevalidatePersonalAccountDataQuery } from '../../../hooks/use-personal-account-data';
|
||||
import { useUpdateAccountData } from '../../../hooks/use-update-account';
|
||||
|
||||
// This is temporary. When the profile views are ready, all account values included in the form will be updated together on form submit.
|
||||
export default function ConsentToggle({
|
||||
userId,
|
||||
initialState,
|
||||
}: {
|
||||
userId: string;
|
||||
initialState: boolean;
|
||||
}) {
|
||||
const [isConsent, setIsConsent] = useState(initialState);
|
||||
const updateAccountMutation = useUpdateAccountData(userId);
|
||||
const revalidateUserDataQuery = useRevalidatePersonalAccountDataQuery();
|
||||
|
||||
const updateConsent = (consent: boolean) => {
|
||||
const promise = updateAccountMutation
|
||||
.mutateAsync({
|
||||
has_consent_anonymized_company_statistics: consent,
|
||||
})
|
||||
.then(() => {
|
||||
revalidateUserDataQuery(userId);
|
||||
});
|
||||
|
||||
return toast.promise(() => promise, {
|
||||
success: <Trans i18nKey={'account:updateConsentSuccess'} />,
|
||||
error: <Trans i18nKey={'account:updateConsentError'} />,
|
||||
loading: <Trans i18nKey={'account:updateConsentLoading'} />,
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Switch
|
||||
checked={isConsent}
|
||||
onCheckedChange={setIsConsent}
|
||||
onClick={() => updateConsent(!isConsent)}
|
||||
disabled={updateAccountMutation.isPending}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -22,14 +22,7 @@ export function usePersonalAccountData(
|
||||
|
||||
const response = await client
|
||||
.from('accounts')
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
name,
|
||||
picture_url,
|
||||
last_name
|
||||
`,
|
||||
)
|
||||
.select()
|
||||
.eq('primary_owner_user_id', userId)
|
||||
.eq('is_personal_account', true)
|
||||
.single();
|
||||
|
||||
@@ -189,6 +189,7 @@ export type Database = {
|
||||
created_at: string | null
|
||||
created_by: string | null
|
||||
email: string | null
|
||||
has_consent_anonymized_company_statistics: boolean | null
|
||||
has_consent_personal_data: boolean | null
|
||||
id: string
|
||||
is_personal_account: boolean
|
||||
@@ -208,6 +209,7 @@ export type Database = {
|
||||
created_at?: string | null
|
||||
created_by?: string | null
|
||||
email?: string | null
|
||||
has_consent_anonymized_company_statistics?: boolean | null
|
||||
has_consent_personal_data?: boolean | null
|
||||
id?: string
|
||||
is_personal_account?: boolean
|
||||
@@ -227,6 +229,7 @@ export type Database = {
|
||||
created_at?: string | null
|
||||
created_by?: string | null
|
||||
email?: string | null
|
||||
has_consent_anonymized_company_statistics?: boolean | null
|
||||
has_consent_personal_data?: boolean | null
|
||||
id?: string
|
||||
is_personal_account?: boolean
|
||||
@@ -1498,6 +1501,7 @@ export type Database = {
|
||||
created_at: string | null
|
||||
created_by: string | null
|
||||
email: string | null
|
||||
has_consent_anonymized_company_statistics: boolean | null
|
||||
has_consent_personal_data: boolean | null
|
||||
id: string
|
||||
is_personal_account: boolean
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
export const SIDEBAR_WIDTH = '16rem';
|
||||
export const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||
export const SIDEBAR_WIDTH_ICON = '4rem';
|
||||
|
||||
export const SIDEBAR_WIDTH_PROPERTY = 'w-[16rem]';
|
||||
export const SIDEBAR_WIDTH_MOBILE_PROPERTY = 'w-[18rem]';
|
||||
export const SIDEBAR_WIDTH_ICON_PROPERTY = 'w-[4rem]';
|
||||
|
||||
@@ -32,8 +32,16 @@ const DialogContent: React.FC<
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
customClose?: React.JSX.Element;
|
||||
preventAutoFocus?: boolean;
|
||||
disableClose?: boolean;
|
||||
}
|
||||
> = ({ className, children, customClose, preventAutoFocus, ...props }) => (
|
||||
> = ({
|
||||
className,
|
||||
children,
|
||||
customClose,
|
||||
disableClose,
|
||||
preventAutoFocus,
|
||||
...props
|
||||
}) => (
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogPrimitive.Content
|
||||
@@ -47,16 +55,26 @@ const DialogContent: React.FC<
|
||||
onCloseAutoFocus={
|
||||
preventAutoFocus ? (e) => e.preventDefault() : props.onOpenAutoFocus
|
||||
}
|
||||
onInteractOutside={
|
||||
disableClose ? (e) => e.preventDefault() : props.onInteractOutside
|
||||
}
|
||||
onPointerDownOutside={
|
||||
disableClose ? (e) => e.preventDefault() : props.onPointerDownOutside
|
||||
}
|
||||
onEscapeKeyDown={
|
||||
disableClose ? (e) => e.preventDefault() : props.onEscapeKeyDown
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs transition-opacity hover:opacity-70 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
|
||||
{customClose || (
|
||||
<>
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</>
|
||||
)}
|
||||
{!disableClose &&
|
||||
(customClose || (
|
||||
<>
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</>
|
||||
))}
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</DialogPortal>
|
||||
@@ -114,13 +132,13 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Dialog,
|
||||
DialogPortal,
|
||||
DialogOverlay,
|
||||
DialogTrigger,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogFooter,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user