From 0414cf8be9b03e9cd9f3aaeb08331ef987f01669 Mon Sep 17 00:00:00 2001 From: Filip Slezak Date: Wed, 20 Sep 2023 12:48:09 +0200 Subject: [PATCH 1/2] up --- src/i18n/en/index.ts | 27 ++- src/i18n/i18n-types.ts | 120 ++++++++++- src/pages/client/clientAPI/clientApi.ts | 17 ++ src/pages/client/clientAPI/types.ts | 0 .../AddInstanceModal/AddInstanceModal.tsx | 188 +++--------------- .../hooks/useAddInstanceModal.tsx | 8 +- .../steps/AddInstanceDeviceStep.tsx | 116 +++++++++++ .../steps/AddInstanceInitStep.tsx | 162 +++++++++++++++ src/pages/client/types.ts | 14 +- .../EnrollmentSideBar/EnrollmentSideBar.tsx | 3 +- .../components/DesktopSetup/DesktopSetup.tsx | 24 ++- 11 files changed, 489 insertions(+), 190 deletions(-) create mode 100644 src/pages/client/clientAPI/clientApi.ts create mode 100644 src/pages/client/clientAPI/types.ts create mode 100644 src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceDeviceStep.tsx create mode 100644 src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceInitStep.tsx diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index 55a1f459..b2e04dcc 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -39,6 +39,9 @@ const en = { close: 'Close', reset: 'Reset', }, + messages: { + error: 'Unexpected error occurred!', + }, }, components: { adminInfo: { @@ -50,7 +53,7 @@ const en = { title: 'Locations', sideBar: { instances: 'Instances', - addInstance: 'Add Instance', + addInstance: 'Enter token', copyright: { copyright: `Copyright © 2023`, appVersion: 'Application version: {version:string}', @@ -90,7 +93,10 @@ const en = { }, modals: { addInstanceModal: { - title: 'Add instance', + titles: { + addInstance: 'Enter token', + addDevice: 'Add device', + }, messages: { success: { add: 'Instance added', @@ -99,6 +105,10 @@ const en = { error: 'Operation failed, check url and token.', }, form: { + submit: { + submitToken: 'Submit token', + addDevice: 'Add device', + }, fields: { token: { label: 'Token', @@ -106,6 +116,9 @@ const en = { url: { label: 'Proxy URL', }, + name: { + label: 'Name', + }, }, }, }, @@ -179,6 +192,16 @@ If you have any questions, please consult your assigned admin.All necessary info }, }, deviceSetup: { + desktopSetup: { + title: 'Configure this device', + controls: { + create: 'Configure device', + success: 'Device is configured', + }, + messages: { + deviceConfigured: 'Device is configured', + }, + }, optionalMessage: `* This step is OPTIONAL. You can skip it if you wish. This can be configured later in defguard.`, cards: { device: { diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 03557f62..d100b282 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -114,6 +114,12 @@ type RootTranslation = { */ reset: string } + messages: { + /** + * U​n​e​x​p​e​c​t​e​d​ ​e​r​r​o​r​ ​o​c​c​u​r​r​e​d​! + */ + error: string + } } components: { adminInfo: { @@ -135,7 +141,7 @@ type RootTranslation = { */ instances: string /** - * A​d​d​ ​I​n​s​t​a​n​c​e + * E​n​t​e​r​ ​t​o​k​e​n */ addInstance: string copyright: { @@ -228,10 +234,16 @@ type RootTranslation = { } modals: { addInstanceModal: { - /** - * A​d​d​ ​i​n​s​t​a​n​c​e - */ - title: string + titles: { + /** + * E​n​t​e​r​ ​t​o​k​e​n + */ + addInstance: string + /** + * A​d​d​ ​d​e​v​i​c​e + */ + addDevice: string + } messages: { success: { /** @@ -249,6 +261,16 @@ type RootTranslation = { error: string } form: { + submit: { + /** + * S​u​b​m​i​t​ ​t​o​k​e​n + */ + submitToken: string + /** + * A​d​d​ ​d​e​v​i​c​e + */ + addDevice: string + } fields: { token: { /** @@ -262,6 +284,12 @@ type RootTranslation = { */ label: string } + name: { + /** + * N​a​m​e + */ + label: string + } } } } @@ -402,6 +430,28 @@ type RootTranslation = { } } deviceSetup: { + desktopSetup: { + /** + * C​o​n​f​i​g​u​r​e​ ​t​h​i​s​ ​d​e​v​i​c​e + */ + title: string + controls: { + /** + * C​o​n​f​i​g​u​r​e​ ​d​e​v​i​c​e + */ + create: string + /** + * D​e​v​i​c​e​ ​i​s​ ​c​o​n​f​i​g​u​r​e​d + */ + success: string + } + messages: { + /** + * D​e​v​i​c​e​ ​i​s​ ​c​o​n​f​i​g​u​r​e​d + */ + deviceConfigured: string + } + } /** * *​ ​T​h​i​s​ ​s​t​e​p​ ​i​s​ ​O​P​T​I​O​N​A​L​.​ ​Y​o​u​ ​c​a​n​ ​s​k​i​p​ ​i​t​ ​i​f​ ​y​o​u​ ​w​i​s​h​.​ ​T​h​i​s​ ​c​a​n​ ​b​e​ ​c​o​n​f​i​g​u​r​e​d​ ​l​a​t​e​r​ ​i​n​ ​d​e​f​g​u​a​r​d​. */ @@ -707,6 +757,12 @@ export type TranslationFunctions = { */ reset: () => LocalizedString } + messages: { + /** + * Unexpected error occurred! + */ + error: () => LocalizedString + } } components: { adminInfo: { @@ -728,7 +784,7 @@ export type TranslationFunctions = { */ instances: () => LocalizedString /** - * Add Instance + * Enter token */ addInstance: () => LocalizedString copyright: { @@ -820,10 +876,16 @@ export type TranslationFunctions = { } modals: { addInstanceModal: { - /** - * Add instance - */ - title: () => LocalizedString + titles: { + /** + * Enter token + */ + addInstance: () => LocalizedString + /** + * Add device + */ + addDevice: () => LocalizedString + } messages: { success: { /** @@ -841,6 +903,16 @@ export type TranslationFunctions = { error: () => LocalizedString } form: { + submit: { + /** + * Submit token + */ + submitToken: () => LocalizedString + /** + * Add device + */ + addDevice: () => LocalizedString + } fields: { token: { /** @@ -854,6 +926,12 @@ export type TranslationFunctions = { */ label: () => LocalizedString } + name: { + /** + * Name + */ + label: () => LocalizedString + } } } } @@ -992,6 +1070,28 @@ export type TranslationFunctions = { } } deviceSetup: { + desktopSetup: { + /** + * Configure this device + */ + title: () => LocalizedString + controls: { + /** + * Configure device + */ + create: () => LocalizedString + /** + * Device is configured + */ + success: () => LocalizedString + } + messages: { + /** + * Device is configured + */ + deviceConfigured: () => LocalizedString + } + } /** * * This step is OPTIONAL. You can skip it if you wish. This can be configured later in defguard. */ diff --git a/src/pages/client/clientAPI/clientApi.ts b/src/pages/client/clientAPI/clientApi.ts new file mode 100644 index 00000000..90124579 --- /dev/null +++ b/src/pages/client/clientAPI/clientApi.ts @@ -0,0 +1,17 @@ +import { invoke } from '@tauri-apps/api'; + +import { DefguardInstance, DefguardLocation } from '../types'; + +const getInstances = async (): Promise => invoke('all_instances'); + +type GetLocationsRequest = { + instance_id: number; +}; + +const getLocations = async (data: GetLocationsRequest): Promise => + invoke('all_locations', data); + +export const clientApi = () => ({ + getInstances, + getLocations, +}); diff --git a/src/pages/client/clientAPI/types.ts b/src/pages/client/clientAPI/types.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/pages/client/components/modals/AddInstanceModal/AddInstanceModal.tsx b/src/pages/client/components/modals/AddInstanceModal/AddInstanceModal.tsx index 57a5057d..6708a411 100644 --- a/src/pages/client/components/modals/AddInstanceModal/AddInstanceModal.tsx +++ b/src/pages/client/components/modals/AddInstanceModal/AddInstanceModal.tsx @@ -1,180 +1,48 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import dayjs from 'dayjs'; -import { useMemo, useState } from 'react'; -import { SubmitHandler, useForm } from 'react-hook-form'; -import { useNavigate, useSearchParams } from 'react-router-dom'; -import { z } from 'zod'; +import { ReactNode, useMemo } from 'react'; +import { shallow } from 'zustand/shallow'; import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; import { ModalWithTitle } from '../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { useToaster } from '../../../../../shared/defguard-ui/hooks/toasts/useToaster'; -import { EnrollmentStartResponse } from '../../../../../shared/hooks/api/types'; -import { routes } from '../../../../../shared/routes'; -import { useEnrollmentStore } from '../../../../enrollment/hooks/store/useEnrollmentStore'; import { useAddInstanceModal } from './hooks/useAddInstanceModal'; +import { AddInstanceModalInitStep } from './steps/AddInstanceInitStep'; export const AddInstanceModal = () => { const { LL } = useI18nContext(); - const [isOpen, loading, reset, close] = useAddInstanceModal((state) => [ + const componentLL = LL.pages.client.modals.addInstanceModal; + const [isOpen, loading, currentStep] = useAddInstanceModal((state) => [ state.isOpen, state.loading, - state.reset, - state.close, + state.step, ]); + const [reset, close] = useAddInstanceModal( + (state) => [state.reset, state.close], + shallow, + ); + + const getTitle = useMemo(() => { + switch (currentStep) { + case 0: + return componentLL.titles.addInstance(); + case 1: + return componentLL.titles.addDevice(); + default: + return ''; + } + }, [currentStep, componentLL]); + return ( 0} + steps={steps} + currentStep={currentStep} backdrop - > - - + /> ); }; -type FormFields = { - url: string; - token: string; -}; - -const defaultValues: FormFields = { - url: '', - token: '', -}; - -const ModalContent = () => { - const toaster = useToaster(); - const { LL } = useI18nContext(); - const navigate = useNavigate(); - const initEnrollment = useEnrollmentStore((state) => state.init); - const isLoading = useAddInstanceModal((state) => state.loading); - const closeModal = useAddInstanceModal((state) => state.close); - const setModalState = useAddInstanceModal((state) => state.setState); - const schema = useMemo( - () => - z.object({ - url: z - .string() - .trim() - .nonempty(LL.form.errors.required()) - .url(LL.form.errors.invalid()), - token: z.string().trim().nonempty(LL.form.errors.required()), - }), - [LL.form.errors], - ); - const { handleSubmit, control } = useForm({ - resolver: zodResolver(schema), - defaultValues, - mode: 'all', - }); - - const handleValidSubmit: SubmitHandler = async (values) => { - const url = () => { - const endpoint = '/api/v1/enrollment/start'; - if (import.meta.env.DEV) { - return endpoint; - } - let base: string; - if (values.url[values.url.length - 1] === '/') { - base = values.url.slice(0, -1); - } else { - base = values.url; - } - return base + endpoint; - }; - - const endpointUrl = url(); - - const headers = { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin': '*', - }; - - const data = JSON.stringify({ - token: values.token, - }); - - setModalState({ loading: true }); - - fetch(endpointUrl, { - method: 'POST', - headers, - body: data, - mode: 'cors', - }) - .then((res) => { - if (!res.ok) { - toaster.error(LL.pages.client.modals.addInstanceModal.messages.error()); - setModalState({ loading: false }); - return; - } - res.json().then((r: EnrollmentStartResponse) => { - setModalState({ loading: false }); - if (r.user.is_active) { - } else { - let proxy_url = import.meta.env.DEV ? '/api/v1' : values.url; - if (proxy_url[proxy_url.length - 1] === '/') { - proxy_url = proxy_url.slice(0, -1); - } - const sessionEnd = dayjs.unix(r.deadline_timestamp).utc().local().format(); - const sessionStart = dayjs().local().format(); - initEnrollment({ - userInfo: r.user, - adminInfo: r.admin, - endContent: r.final_page_content, - proxy_url: proxy_url, - sessionEnd, - sessionStart, - }); - closeModal(); - navigate(routes.enrollment, { replace: true }); - } - }); - }) - .catch((e) => { - toaster.error(LL.pages.client.modals.addInstanceModal.messages.error()); - setModalState({ loading: false }); - console.error(e); - }); - }; - - return ( -
- - -
-
- - ); -}; +const steps: ReactNode[] = []; diff --git a/src/pages/client/components/modals/AddInstanceModal/hooks/useAddInstanceModal.tsx b/src/pages/client/components/modals/AddInstanceModal/hooks/useAddInstanceModal.tsx index b7af9c78..4fa0966e 100644 --- a/src/pages/client/components/modals/AddInstanceModal/hooks/useAddInstanceModal.tsx +++ b/src/pages/client/components/modals/AddInstanceModal/hooks/useAddInstanceModal.tsx @@ -3,12 +3,15 @@ import { createWithEqualityFn } from 'zustand/traditional'; const defaultValues: StoreValues = { isOpen: false, loading: false, + step: 0, + proxyUrl: undefined, }; export const useAddInstanceModal = createWithEqualityFn( - (set) => ({ + (set, get) => ({ ...defaultValues, open: (initial) => set({ ...defaultValues, ...initial, isOpen: true }), + next: (values) => set({ ...values, step: get().step + 1 }), close: () => set({ isOpen: false }), reset: () => set(defaultValues), setState: (values) => set(values), @@ -21,10 +24,13 @@ type Store = StoreValues & StoreMethods; type StoreValues = { isOpen: boolean; loading: boolean; + step: number; + proxyUrl?: string; }; type StoreMethods = { open: (init?: Partial) => void; + next: (values?: Partial) => void; close: () => void; reset: () => void; setState: (values: Partial) => void; diff --git a/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceDeviceStep.tsx b/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceDeviceStep.tsx new file mode 100644 index 00000000..0d5c6563 --- /dev/null +++ b/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceDeviceStep.tsx @@ -0,0 +1,116 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { invoke } from '@tauri-apps/api'; +import { useMemo, useState } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { z } from 'zod'; +import { shallow } from 'zustand/shallow'; + +import { useI18nContext } from '../../../../../../i18n/i18n-react'; +import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; +import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { + ButtonSize, + ButtonStyleVariant, +} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; +import { useToaster } from '../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { + CreateDeviceRequest, + CreateDeviceResponse, +} from '../../../../../../shared/hooks/api/types'; +import { generateWGKeys } from '../../../../../../shared/utils/generateWGKeys'; +import { useAddInstanceModal } from '../hooks/useAddInstanceModal'; + +type FormFields = { + name: string; +}; + +const defaultValues: FormFields = { + name: '', +}; + +export const AddInstanceDeviceStep = () => { + const { LL } = useI18nContext(); + const componentLL = LL.pages.client.modals.addInstanceModal; + const toaster = useToaster(); + const close = useAddInstanceModal((state) => state.close); + const [isLoading, setIsLoading] = useState(false); + + const [proxyUrl] = useAddInstanceModal((state) => [state.proxyUrl], shallow); + + const schema = useMemo( + () => z.object({ name: z.string().trim().nonempty(LL.form.errors.required()) }), + [LL.form.errors], + ); + + const { control, handleSubmit } = useForm({ + defaultValues: defaultValues, + resolver: zodResolver(schema), + mode: 'all', + }); + + const handleValidSubmit: SubmitHandler = async (values) => { + if (!proxyUrl) return; + setIsLoading(true); + const { publicKey, privateKey } = generateWGKeys(); + const data: CreateDeviceRequest = { + name: values.name, + pubkey: publicKey, + }; + const headers = { + 'Content-Type': 'application/json', + }; + try { + await fetch(`${proxyUrl}/enrollment/create_device`, { + headers, + body: JSON.stringify(data), + method: 'POST', + }).then((r) => { + if (!r.ok) { + setIsLoading(false); + toaster.error(LL.common.messages.error()); + throw Error('Failed to create device'); + } + r.json().then((deviceResp: CreateDeviceResponse) => { + invoke('save_device_config', { + privateKey: privateKey, + response: deviceResp, + }) + .then(() => { + setIsLoading(false); + toaster.success(componentLL.messages.success.add()); + close(); + }) + .catch((e) => { + toaster.error(LL.common.messages.error()); + setIsLoading(false); + close(); + console.error(e); + }); + }); + }); + } catch (e) { + setIsLoading(false); + toaster.error(LL.common.messages.error()); + close(); + console.error(e); + } + }; + + return ( +
+ +
+
+ + ); +}; diff --git a/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceInitStep.tsx b/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceInitStep.tsx new file mode 100644 index 00000000..fa19fb83 --- /dev/null +++ b/src/pages/client/components/modals/AddInstanceModal/steps/AddInstanceInitStep.tsx @@ -0,0 +1,162 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import dayjs from 'dayjs'; +import { useMemo } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; +import { useNavigate } from 'react-router-dom'; +import { z } from 'zod'; + +import { useI18nContext } from '../../../../../../i18n/i18n-react'; +import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; +import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; +import { + ButtonSize, + ButtonStyleVariant, +} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; +import { useToaster } from '../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; +import { EnrollmentStartResponse } from '../../../../../../shared/hooks/api/types'; +import { routes } from '../../../../../../shared/routes'; +import { useEnrollmentStore } from '../../../../../enrollment/hooks/store/useEnrollmentStore'; +import { useAddInstanceModal } from '../hooks/useAddInstanceModal'; + +type FormFields = { + url: string; + token: string; +}; + +const defaultValues: FormFields = { + url: '', + token: '', +}; + +export const AddInstanceModalInitStep = () => { + const toaster = useToaster(); + const { LL } = useI18nContext(); + const navigate = useNavigate(); + const initEnrollment = useEnrollmentStore((state) => state.init); + const isLoading = useAddInstanceModal((state) => state.loading); + const closeModal = useAddInstanceModal((state) => state.close); + const nextStep = useAddInstanceModal((state) => state.next); + const setModalState = useAddInstanceModal((state) => state.setState); + const schema = useMemo( + () => + z.object({ + url: z + .string() + .trim() + .nonempty(LL.form.errors.required()) + .url(LL.form.errors.invalid()), + token: z.string().trim().nonempty(LL.form.errors.required()), + }), + [LL.form.errors], + ); + const { handleSubmit, control } = useForm({ + resolver: zodResolver(schema), + defaultValues, + mode: 'all', + }); + + const handleValidSubmit: SubmitHandler = async (values) => { + const url = () => { + const endpoint = '/api/v1/enrollment/start'; + if (import.meta.env.DEV) { + return endpoint; + } + let base: string; + if (values.url[values.url.length - 1] === '/') { + base = values.url.slice(0, -1); + } else { + base = values.url; + } + return base + endpoint; + }; + + const endpointUrl = url(); + + const headers = { + 'Content-Type': 'application/json', + }; + + const data = JSON.stringify({ + token: values.token, + }); + + setModalState({ loading: true }); + + fetch(endpointUrl, { + method: 'POST', + headers, + body: data, + }) + .then((res) => { + if (!res.ok) { + toaster.error(LL.pages.client.modals.addInstanceModal.messages.error()); + setModalState({ loading: false }); + return; + } + res.json().then((r: EnrollmentStartResponse) => { + setModalState({ loading: false }); + let proxy_api_url = import.meta.env.DEV ? '' : values.url; + if (proxy_api_url[proxy_api_url.length - 1] === '/') { + proxy_api_url = proxy_api_url.slice(0, -1); + } + proxy_api_url = proxy_api_url + '/api/v1'; + // is user in need of full enrollment ? + if (r.user.is_active) { + //no, only create new device for desktop client + nextStep({ + proxyUrl: proxy_api_url, + }); + } else { + // yes, enroll the user + const sessionEnd = dayjs.unix(r.deadline_timestamp).utc().local().format(); + const sessionStart = dayjs().local().format(); + initEnrollment({ + userInfo: r.user, + adminInfo: r.admin, + endContent: r.final_page_content, + proxy_url: proxy_api_url, + sessionEnd, + sessionStart, + }); + closeModal(); + navigate(routes.enrollment, { replace: true }); + } + }); + }) + .catch((e) => { + toaster.error(LL.pages.client.modals.addInstanceModal.messages.error()); + setModalState({ loading: false }); + console.error(e); + }); + }; + + return ( +
+ + +
+
+ + ); +}; diff --git a/src/pages/client/types.ts b/src/pages/client/types.ts index fb33359f..6a999144 100644 --- a/src/pages/client/types.ts +++ b/src/pages/client/types.ts @@ -1,13 +1,17 @@ export type DefguardInstance = { - id: string; + id: number; + uuid: string; name: string; url: string; - locations: DefguardLocation[]; }; export type DefguardLocation = { - id: string; - ip: string; + id: number; + instance_id: number; + network_id: number; name: string; - connected: boolean; + address: string; + pubkey: string; + endpoint: string; + allowed_ips: string; }; diff --git a/src/pages/enrollment/components/EnrollmentSideBar/EnrollmentSideBar.tsx b/src/pages/enrollment/components/EnrollmentSideBar/EnrollmentSideBar.tsx index 2c7814df..11e9f5ba 100644 --- a/src/pages/enrollment/components/EnrollmentSideBar/EnrollmentSideBar.tsx +++ b/src/pages/enrollment/components/EnrollmentSideBar/EnrollmentSideBar.tsx @@ -1,13 +1,12 @@ import './style.scss'; -import { getTauriVersion, getVersion } from '@tauri-apps/api/app'; +import { getVersion } from '@tauri-apps/api/app'; import classNames from 'classnames'; import { useEffect, useMemo, useState } from 'react'; import { LocalizedString } from 'typesafe-i18n'; import { useI18nContext } from '../../../../i18n/i18n-react'; import { Divider } from '../../../../shared/defguard-ui/components/Layout/Divider/Divider.tsx'; -import { useApi } from '../../../../shared/hooks/api/useApi.tsx'; import { useEnrollmentStore } from '../../hooks/store/useEnrollmentStore'; import { AdminInfo } from '../AdminInfo/AdminInfo'; import { TimeLeft } from '../TimeLeft/TimeLeft'; diff --git a/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx b/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx index 5b3f792e..bd83c720 100644 --- a/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx +++ b/src/pages/enrollment/steps/DeviceStep/components/DesktopSetup/DesktopSetup.tsx @@ -16,6 +16,7 @@ import { ButtonStyleVariant, } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; import { Card } from '../../../../../../shared/defguard-ui/components/Layout/Card/Card'; +import { useToaster } from '../../../../../../shared/defguard-ui/hooks/toasts/useToaster'; import { CreateDeviceResponse } from '../../../../../../shared/hooks/api/types'; import { useApi } from '../../../../../../shared/hooks/api/useApi'; import { generateWGKeys } from '../../../../../../shared/utils/generateWGKeys'; @@ -36,6 +37,8 @@ const saveConfig = async ( export const DekstopSetup = () => { const { LL } = useI18nContext(); + const toaster = useToaster(); + const stepLL = LL.pages.enrollment.steps.deviceSetup; const { enrollment: { createDevice, activateUser }, } = useApi(); @@ -45,12 +48,14 @@ export const DekstopSetup = () => { state.userPassword, ]); const setEnrollmentStore = useEnrollmentStore((state) => state.setState); + const next = useEnrollmentStore((state) => state.nextStep); const [isLoading, setIsLoading] = useState(false); const { isLoading: loadingUserActivation, mutateAsync: mutateUserActivation } = useMutation({ mutationFn: activateUser, onError: (e) => { + toaster.error(LL.common.messages.error()); console.error(e); }, }); @@ -88,20 +93,15 @@ export const DekstopSetup = () => { password: userPassword, phone_number: userInfo.phone_number, }).then(() => { - console.log('User activated'); setIsLoading(true); saveConfig(privateKey, deviceResponse) .then(() => { - console.log('config saved'); setIsLoading(false); setEnrollmentStore({ deviceName: values.name }); + toaster.success(stepLL.desktopSetup.messages.deviceConfigured()); + next(); }) .catch((e) => { - console.log('Failed to save config'); - console.log({ - privateKey, - deviceResponse, - }); setIsLoading(false); console.error(e); }); @@ -110,7 +110,7 @@ export const DekstopSetup = () => { return ( -

{LL.pages.enrollment.steps.deviceSetup.cards.device.title()}

+

{stepLL.desktopSetup.title()}

{