Skip to content

Commit

Permalink
feat: read productivity report from profile
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Nov 5, 2024
1 parent 7c19237 commit 0848cc1
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { render } from 'utils/testRenderer';
import { screen } from '@testing-library/react';
import { testServerRoute, testServerSetup } from 'utils/testServer';
import { ProductivityEmailSubscription } from './ProductivityEmailSubscription';
import ToastRenderer from '../../../common/ToastRenderer/ToastRenderer';

const server = testServerSetup();

const setupSubscribeApi = () => {
testServerRoute(
server,
'/api/admin/email-subscription/productivity-report',
{},
'put',
202,
);
};

const setupUnsubscribeApi = () => {
testServerRoute(
server,
'/api/admin/email-subscription/productivity-report',
{},
'delete',
202,
);
};

const setupErrorApi = () => {
testServerRoute(
server,
'/api/admin/email-subscription/productivity-report',
{ message: 'user error' },
'delete',
400,
);
};

test('unsubscribe', async () => {
setupUnsubscribeApi();
let changed = false;
render(
<>
<ProductivityEmailSubscription
status='subscribed'
onChange={() => {
changed = true;
}}
/>
<ToastRenderer />
</>,
);
const checkbox = screen.getByLabelText('Productivity Email Subscription');
expect(checkbox).toBeChecked();

checkbox.click();

await screen.findByText('Unsubscribed from productivity report');
expect(changed).toBe(true);
});

test('subscribe', async () => {
setupSubscribeApi();
let changed = false;
render(
<>
<ProductivityEmailSubscription
status='unsubscribed'
onChange={() => {
changed = true;
}}
/>
<ToastRenderer />
</>,
);
const checkbox = screen.getByLabelText('Productivity Email Subscription');
expect(checkbox).not.toBeChecked();

checkbox.click();

await screen.findByText('Subscribed to productivity report');
expect(changed).toBe(true);
});

test('handle error', async () => {
setupErrorApi();
let changed = false;
render(
<>
<ProductivityEmailSubscription
status='subscribed'
onChange={() => {
changed = true;
}}
/>
<ToastRenderer />
</>,
);
const checkbox = screen.getByLabelText('Productivity Email Subscription');

checkbox.click();

await screen.findByText('user error');
expect(changed).toBe(true);
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Box, Switch } from '@mui/material';
import { Box, FormControlLabel, Switch } from '@mui/material';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useState } from 'react';
import type { FC } from 'react';
import { useEmailSubscriptionApi } from 'hooks/api/actions/useEmailSubscriptionApi/useEmailSubscriptionApi';
import useToast from 'hooks/useToast';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';

export const ProductivityEmailSubscription = () => {
// TODO: read data from user profile when available
const [receiveProductivityReportEmail, setReceiveProductivityReportEmail] =
useState(false);
export const ProductivityEmailSubscription: FC<{
status: 'subscribed' | 'unsubscribed';
onChange: () => void;
}> = ({ status, onChange }) => {
const {
subscribe,
unsubscribe,
Expand All @@ -19,44 +19,46 @@ export const ProductivityEmailSubscription = () => {

return (
<Box>
Productivity Email Subscription
<Switch
onChange={async () => {
try {
if (receiveProductivityReportEmail) {
await unsubscribe('productivity-report');
setToastData({
title: 'Unsubscribed from productivity report',
type: 'success',
});
trackEvent('productivity-report', {
props: {
eventType: 'subscribe',
},
});
} else {
await subscribe('productivity-report');
setToastData({
title: 'Subscribed to productivity report',
type: 'success',
});
trackEvent('productivity-report', {
props: {
eventType: 'unsubscribe',
},
});
}
} catch (error) {
setToastApiError(formatUnknownError(error));
}
<FormControlLabel
label='Productivity Email Subscription'
control={
<Switch
onChange={async () => {
try {
if (status === 'subscribed') {
await unsubscribe('productivity-report');
setToastData({
title: 'Unsubscribed from productivity report',
type: 'success',
});
trackEvent('productivity-report', {
props: {
eventType: 'subscribe',
},
});
} else {
await subscribe('productivity-report');
setToastData({
title: 'Subscribed to productivity report',
type: 'success',
});
trackEvent('productivity-report', {
props: {
eventType: 'unsubscribe',
},
});
}
} catch (error) {
setToastApiError(formatUnknownError(error));
}

setReceiveProductivityReportEmail(
!receiveProductivityReportEmail,
);
}}
name='productivity-email'
checked={receiveProductivityReportEmail}
disabled={changingSubscriptionStatus}
onChange();
}}
name='productivity-email'
checked={status === 'subscribed'}
disabled={changingSubscriptionStatus}
/>
}
/>
</Box>
);
Expand Down
15 changes: 13 additions & 2 deletions frontend/src/component/user/Profile/ProfileTab/ProfileTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ interface IProfileTabProps {
}

export const ProfileTab = ({ user }: IProfileTabProps) => {
const { profile } = useProfile();
const { profile, refetchProfile } = useProfile();
const navigate = useNavigate();
const { locationSettings, setLocationSettings } = useLocationSettings();
const [currentLocale, setCurrentLocale] = useState<string>();
Expand Down Expand Up @@ -223,7 +223,18 @@ export const ProfileTab = ({ user }: IProfileTabProps) => {
<>
<StyledDivider />
<StyledSectionLabel>Email Settings</StyledSectionLabel>
<ProductivityEmailSubscription />
{profile?.subscriptions && (
<ProductivityEmailSubscription
status={
profile.subscriptions.includes(
'productivity-report',
)
? 'subscribed'
: 'unsubscribed'
}
onChange={refetchProfile}
/>
)}
</>
) : null}
</PageContent>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/api/getters/useProfile/useProfile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import useSWR from 'swr';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import type { IProfile } from 'interfaces/profile';
import type { ProfileSchema } from '../../../../openapi';

export interface IUseProfileOutput {
profile?: IProfile;
profile?: ProfileSchema;
refetchProfile: () => void;
loading: boolean;
error?: Error;
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/interfaces/profile.ts

This file was deleted.

0 comments on commit 0848cc1

Please sign in to comment.