From f8186e68e4cd6661bc83e834f880fc7dbae3787a Mon Sep 17 00:00:00 2001 From: Manuel Date: Sun, 30 Jul 2023 23:29:41 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20procedure=20for=20updating=20?= =?UTF-8?q?user=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Accessibility/AccessibilitySettings.tsx | 61 +------ src/pages/user/preferences.tsx | 149 +++++++++++++----- src/server/api/routers/user.ts | 27 +++- src/validations/user.ts | 6 + 4 files changed, 142 insertions(+), 101 deletions(-) diff --git a/src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx b/src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx index fc4e92eb5..c0e121b1c 100644 --- a/src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx +++ b/src/components/Settings/Customization/Accessibility/AccessibilitySettings.tsx @@ -1,76 +1,25 @@ import { Alert, Stack, Switch } from '@mantine/core'; import { IconInfoCircle } from '@tabler/icons-react'; -import { BaseSyntheticEvent } from 'react'; import { useTranslation } from 'react-i18next'; - -import { useConfigContext } from '../../../../config/provider'; -import { useConfigStore } from '../../../../config/store'; +import { useFormContext } from '~/pages/user/preferences'; export const AccessibilitySettings = () => { const { t } = useTranslation('settings/customization/accessibility'); - const { updateConfig } = useConfigStore(); - const { config, name: configName } = useConfigContext(); + + const form = useFormContext(); return ( { - if (!configName) { - return; - } - - updateConfig( - configName, - (previousConfig) => ({ - ...previousConfig, - settings: { - ...previousConfig.settings, - customization: { - ...previousConfig.settings.customization, - accessibility: { - ...previousConfig.settings.customization.accessibility, - disablePingPulse: value.target.checked, - }, - }, - }, - }), - false, - true - ); - }} + {...form.getInputProps('disablePingPulse')} /> { - if (!configName) { - return; - } - - updateConfig( - configName, - (previousConfig) => ({ - ...previousConfig, - settings: { - ...previousConfig.settings, - customization: { - ...previousConfig.settings.customization, - accessibility: { - ...previousConfig.settings.customization.accessibility, - replacePingDotsWithIcons: value.target.checked, - }, - }, - }, - }), - false, - true - ); - }} + {...form.getInputProps('replaceDotsWithIcons')} /> } color="blue"> diff --git a/src/pages/user/preferences.tsx b/src/pages/user/preferences.tsx index 2a504809b..341283f7a 100644 --- a/src/pages/user/preferences.tsx +++ b/src/pages/user/preferences.tsx @@ -1,19 +1,21 @@ -import { Group, Select, Stack, Text, Title } from '@mantine/core'; -import Head from 'next/head'; +import { Button, Group, Select, Stack, Text, Title } from '@mantine/core'; +import { createFormContext } from '@mantine/form'; +import type { InferGetServerSidePropsType } from 'next'; +import { GetServerSidePropsContext } from 'next'; import { forwardRef } from 'react'; +import { z } from 'zod'; import { AccessibilitySettings } from '~/components/Settings/Customization/Accessibility/AccessibilitySettings'; import { MainLayout } from '~/components/layout/admin/main-admin.layout'; import { CommonHeader } from '~/components/layout/common-header'; import { languages } from '~/tools/language'; +import { getServerSideTranslations } from '~/tools/server/getServerSideTranslations'; +import { RouterOutputs, api } from '~/utils/api'; +import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; +import { updateSettingsValidationSchema } from '~/validations/user'; + +const PreferencesPage = ({ locale }: InferGetServerSidePropsType) => { + const { data } = api.user.getWithSettings.useQuery(); -const PreferencesPage = () => { - const data = languages.map((language) => ({ - image: 'https://img.icons8.com/clouds/256/000000/futurama-bender.png', - label: language.originalName, - description: language.translatedName, - value: language.shortName, - country: language.country, - })); return ( @@ -21,43 +23,94 @@ const PreferencesPage = () => { Preferences - - - Localization - - - - - - Accessibility - - - - + {data && } ); }; +export const [FormProvider, useFormContext, useForm] = + createFormContext>(); + +const SettingsComponent = ({ + settings, +}: { + settings: RouterOutputs['user']['getWithSettings']['settings']; +}) => { + const languagesData = languages.map((language) => ({ + image: 'https://img.icons8.com/clouds/256/000000/futurama-bender.png', + label: language.originalName, + description: language.translatedName, + value: language.shortName, + country: language.country, + })); + + const { i18nZodResolver } = useI18nZodResolver(); + + const form = useForm({ + initialValues: { + disablePingPulse: settings.disablePingPulse, + replaceDotsWithIcons: settings.replacePingWithIcons, + language: settings.language, + }, + validate: i18nZodResolver(updateSettingsValidationSchema), + validateInputOnBlur: true, + validateInputOnChange: true, + }); + + const { mutate } = api.user.updateSettings.useMutation(); + + const handleSubmit = () => { + mutate(form.values); + }; + + return ( + +
+ + + Localization + + + + + + Accessibility + + + + + + +
+
+ ); +}; + interface ItemProps extends React.ComponentPropsWithoutRef<'div'> { image: string; label: string; @@ -82,4 +135,14 @@ const SelectItem = forwardRef( ) ); +export async function getServerSideProps({ req, res, locale }: GetServerSidePropsContext) { + const translations = await getServerSideTranslations([], locale, undefined, undefined); + return { + props: { + ...translations, + locale: locale, + }, + }; +} + export default PreferencesPage; diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts index 48abab753..5fe949e18 100644 --- a/src/server/api/routers/user.ts +++ b/src/server/api/routers/user.ts @@ -2,7 +2,11 @@ import { TRPCError } from '@trpc/server'; import bcrypt from 'bcrypt'; import { z } from 'zod'; import { hashPassword } from '~/utils/security'; -import { colorSchemeParser, signUpFormSchema } from '~/validations/user'; +import { + colorSchemeParser, + signUpFormSchema, + updateSettingsValidationSchema, +} from '~/validations/user'; import { COOKIE_COLOR_SCHEME_KEY, COOKIE_LOCALE_KEY } from '../../../../data/constants'; import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc'; @@ -134,6 +138,25 @@ export const userRouter = createTRPCRouter({ }; }), + updateSettings: protectedProcedure + .input(updateSettingsValidationSchema) + .mutation(async ({ ctx, input }) => { + await ctx.prisma.user.update({ + where: { + id: ctx.session.user.id, + }, + data: { + settings: { + update: { + disablePingPulse: input.disablePingPulse, + replacePingWithIcons: input.replaceDotsWithIcons, + language: input.language, + }, + }, + }, + }); + }), + getAll: publicProcedure .input( z.object({ @@ -183,7 +206,7 @@ export const userRouter = createTRPCRouter({ password: hashedPassword, salt: salt, settings: { - create: {} + create: {}, }, }, }); diff --git a/src/validations/user.ts b/src/validations/user.ts index 5f05755aa..d886fdf79 100644 --- a/src/validations/user.ts +++ b/src/validations/user.ts @@ -23,3 +23,9 @@ export const colorSchemeParser = z .enum(['light', 'dark', 'environment']) .default('environment') .catch('environment'); + +export const updateSettingsValidationSchema = z.object({ + disablePingPulse: z.boolean(), + replaceDotsWithIcons: z.boolean(), + language: z.string(), +});