diff --git a/pages/setup/Account.graphql b/pages/setup/Account.graphql
new file mode 100644
index 0000000000..32a601fd39
--- /dev/null
+++ b/pages/setup/Account.graphql
@@ -0,0 +1,8 @@
+query AccountListOptions {
+ accountLists(first: 50) {
+ nodes {
+ id
+ name
+ }
+ }
+}
diff --git a/pages/setup/account.page.test.tsx b/pages/setup/account.page.test.tsx
new file mode 100644
index 0000000000..9bbf9a3e9d
--- /dev/null
+++ b/pages/setup/account.page.test.tsx
@@ -0,0 +1,134 @@
+import { GetServerSidePropsContext } from 'next';
+import { ApolloClient, NormalizedCacheObject } from '@apollo/client';
+import { render, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { getSession } from 'next-auth/react';
+import { session } from '__tests__/fixtures/session';
+import TestRouter from '__tests__/util/TestRouter';
+import { GqlMockedProvider } from '__tests__/util/graphqlMocking';
+import makeSsrClient from 'src/lib/apollo/ssrClient';
+import AccountPage, { getServerSideProps } from './account.page';
+
+jest.mock('src/lib/apollo/ssrClient');
+
+const push = jest.fn();
+const router = {
+ push,
+};
+
+const context = {
+ req: {},
+} as unknown as GetServerSidePropsContext;
+
+describe('Setup account page', () => {
+ it('renders account options and saves default account renders and button saves and advances to the next page', async () => {
+ const accountListOptions = {
+ accountLists: {
+ nodes: [1, 2, 3].map((id) => ({
+ id: `account-list-${id}`,
+ name: `Account List ${id}`,
+ })),
+ },
+ };
+
+ const mutationSpy = jest.fn();
+ const { getByRole } = render(
+
+
+
+
+ ,
+ );
+
+ expect(
+ getByRole('heading', { name: 'Set default account' }),
+ ).toBeInTheDocument();
+
+ userEvent.click(getByRole('combobox', { name: 'Account' }));
+ userEvent.click(getByRole('option', { name: 'Account List 1' }));
+ const continueButton = getByRole('button', { name: 'Continue Tour' });
+ userEvent.click(continueButton);
+ expect(continueButton).toBeDisabled();
+
+ await waitFor(() =>
+ expect(mutationSpy).toHaveGraphqlOperation('UpdateUserDefaultAccount', {
+ input: { attributes: { defaultAccountList: 'account-list-1' } },
+ }),
+ );
+ expect(push).toHaveBeenCalledWith(
+ '/accountLists/account-list-1/settings/preferences',
+ );
+ });
+});
+
+describe('getServerSideProps', () => {
+ const query = jest.fn();
+ const mutate = jest.fn();
+
+ beforeEach(() => {
+ (makeSsrClient as jest.MockedFn).mockReturnValue({
+ query,
+ mutate,
+ } as unknown as ApolloClient);
+ });
+
+ it('redirects to the login page if the session is missing', async () => {
+ (getSession as jest.MockedFn).mockResolvedValueOnce(
+ null,
+ );
+
+ await expect(getServerSideProps(context)).resolves.toEqual({
+ redirect: {
+ destination: '/login',
+ permanent: false,
+ },
+ });
+ });
+
+ it('sets the single account list as the default', async () => {
+ query.mockResolvedValue({
+ data: {
+ accountLists: {
+ nodes: [{ id: 'account-list-1' }],
+ },
+ },
+ });
+
+ await expect(getServerSideProps(context)).resolves.toEqual({
+ redirect: {
+ destination: '/accountLists/account-list-1/settings/preferences',
+ permanent: false,
+ },
+ });
+ expect(mutate).toHaveBeenCalledWith(
+ expect.objectContaining({
+ variables: {
+ input: {
+ attributes: {
+ defaultAccountList: 'account-list-1',
+ },
+ },
+ },
+ }),
+ );
+ });
+
+ it('does not set an account list as the default when there are multiple', async () => {
+ const accountListOptions = {
+ accountLists: {
+ nodes: [{ id: 'account-list-1' }, { id: 'account-list-2' }],
+ },
+ };
+ query.mockResolvedValue({
+ data: accountListOptions,
+ });
+
+ await expect(getServerSideProps(context)).resolves.toEqual({
+ props: {
+ accountListOptions,
+ session,
+ },
+ });
+ expect(mutate).not.toHaveBeenCalled();
+ });
+});
diff --git a/pages/setup/account.page.tsx b/pages/setup/account.page.tsx
new file mode 100644
index 0000000000..9c3f269a75
--- /dev/null
+++ b/pages/setup/account.page.tsx
@@ -0,0 +1,146 @@
+import { GetServerSideProps } from 'next';
+import Head from 'next/head';
+import { useRouter } from 'next/router';
+import React, { useState } from 'react';
+import { Autocomplete, TextField } from '@mui/material';
+import { getSession } from 'next-auth/react';
+import { useTranslation } from 'react-i18next';
+import {
+ UpdateUserDefaultAccountDocument,
+ UpdateUserDefaultAccountMutation,
+ UpdateUserDefaultAccountMutationVariables,
+ useUpdateUserDefaultAccountMutation,
+} from 'src/components/Settings/preferences/accordions/DefaultAccountAccordion/UpdateDefaultAccount.generated';
+import { SetupPage } from 'src/components/Setup/SetupPage';
+import { LargeButton } from 'src/components/Setup/styledComponents';
+import useGetAppSettings from 'src/hooks/useGetAppSettings';
+import makeSsrClient from 'src/lib/apollo/ssrClient';
+import {
+ AccountListOptionsDocument,
+ AccountListOptionsQuery,
+} from './Account.generated';
+
+type AccountList = AccountListOptionsQuery['accountLists']['nodes'][number];
+
+interface PageProps {
+ accountListOptions: AccountListOptionsQuery;
+}
+
+// This is the third page page of the setup tour. It lets users choose their default account list. It will be shown if
+// the user has more than one account list and don't have a default chosen yet.
+const AccountPage: React.FC = ({ accountListOptions }) => {
+ const { t } = useTranslation();
+ const { appName } = useGetAppSettings();
+ const { push } = useRouter();
+ const [updateUserDefaultAccount, { loading: isSubmitting }] =
+ useUpdateUserDefaultAccountMutation();
+
+ const [defaultAccountList, setDefaultAccountList] =
+ useState(null);
+
+ const handleSave = async () => {
+ if (!defaultAccountList) {
+ return;
+ }
+
+ await updateUserDefaultAccount({
+ variables: {
+ input: {
+ attributes: {
+ defaultAccountList: defaultAccountList.id,
+ },
+ },
+ },
+ });
+ push(`/accountLists/${defaultAccountList.id}/settings/preferences`);
+ };
+
+ return (
+ <>
+
+
+ {appName} | {t('Setup - Default Account')}
+
+
+
+ {t(
+ 'Which account would you like to see by default when you open {{appName}}?',
+ { appName },
+ )}
+ setDefaultAccountList(value)}
+ options={accountListOptions.accountLists.nodes}
+ getOptionLabel={(accountList) => accountList.name ?? ''}
+ fullWidth
+ renderInput={(params) => (
+
+ )}
+ />
+
+ {t('Continue Tour')}
+
+
+ >
+ );
+};
+
+export const getServerSideProps: GetServerSideProps = async (
+ context,
+) => {
+ const session = await getSession(context);
+ const apiToken = session?.user?.apiToken;
+ if (!apiToken) {
+ return {
+ redirect: {
+ destination: '/login',
+ permanent: false,
+ },
+ };
+ }
+
+ const ssrClient = makeSsrClient(apiToken);
+ const { data: accountListOptions } =
+ await ssrClient.query({
+ query: AccountListOptionsDocument,
+ });
+
+ if (accountListOptions.accountLists.nodes.length === 1) {
+ // The user has exactly one account list, so set it as the default and go to preferences
+ const defaultAccountListId = accountListOptions.accountLists.nodes[0].id;
+ await ssrClient.mutate<
+ UpdateUserDefaultAccountMutation,
+ UpdateUserDefaultAccountMutationVariables
+ >({
+ mutation: UpdateUserDefaultAccountDocument,
+ variables: {
+ input: {
+ attributes: {
+ defaultAccountList: defaultAccountListId,
+ },
+ },
+ },
+ });
+ return {
+ redirect: {
+ destination: `/accountLists/${defaultAccountListId}/settings/preferences`,
+ permanent: false,
+ },
+ };
+ }
+
+ return {
+ props: {
+ session,
+ accountListOptions,
+ },
+ };
+};
+
+export default AccountPage;