@@ -4,10 +4,12 @@ import { zodResolver } from '@hookform/resolvers/zod';
44import { useMutation } from '@tanstack/react-query' ;
55import { useCallback , useEffect , useMemo , useState } from 'react' ;
66import {
7+ type Control ,
78 type SubmitHandler ,
89 type UseControllerProps ,
910 useController ,
1011 useForm ,
12+ useWatch ,
1113} from 'react-hook-form' ;
1214import { z } from 'zod' ;
1315import { shallow } from 'zustand/shallow' ;
@@ -39,6 +41,7 @@ import {
3941 type TrayIconTheme ,
4042} from '../../../../clientAPI/types' ;
4143import { useClientStore } from '../../../../hooks/useClientStore' ;
44+ import type { DefguardInstance } from '../../../../types' ;
4245
4346type FormFields = AppConfig ;
4447
@@ -81,6 +84,8 @@ export const GlobalSettingsTab = () => {
8184 required_error : LL . form . errors . required ( ) ,
8285 } )
8386 . gte ( 120 , LL . form . errors . minValue ( { min : 120 } ) ) ,
87+ default_instance : z . number ( ) . nullable ( ) ,
88+ auto_connect_mfa : z . boolean ( ) ,
8489 } ) ,
8590 [ LL . form . errors ] ,
8691 ) ;
@@ -139,6 +144,16 @@ export const GlobalSettingsTab = () => {
139144 </ header >
140145 < FormInput controller = { { control, name : 'peer_alive_period' } } type = "number" />
141146 </ section >
147+ < section >
148+ < header >
149+ < h2 > { localLL . defaultInstance . title ( ) } </ h2 >
150+ < Helper initialPlacement = "right" >
151+ < p > { localLL . defaultInstance . helper ( ) } </ p >
152+ </ Helper >
153+ </ header >
154+ < DefaultInstanceSelect controller = { { control, name : 'default_instance' } } />
155+ < AutoConnectMfaOption control = { control } controller = { { control, name : 'auto_connect_mfa' } } />
156+ </ section >
142157 </ form >
143158 ) ;
144159} ;
@@ -330,3 +345,69 @@ const CheckForUpdatesOption = ({ controller }: FormMemberProps) => {
330345 />
331346 ) ;
332347} ;
348+
349+ const AutoConnectMfaOption = ( {
350+ controller,
351+ control,
352+ } : FormMemberProps & { control : Control < FormFields > } ) => {
353+ const { LL } = useI18nContext ( ) ;
354+ const localLL = LL . pages . client . pages . settingsPage . tabs . global ;
355+ const defaultInstance = useWatch ( { control, name : 'default_instance' } ) ;
356+
357+ return (
358+ < FormCheckBox
359+ labelPlacement = "right"
360+ label = { localLL . autoConnectMfa . title ( ) }
361+ controller = { controller }
362+ disabled = { defaultInstance === null }
363+ />
364+ ) ;
365+ } ;
366+
367+ const DefaultInstanceSelect = ( { controller } : FormMemberProps ) => {
368+ const { LL } = useI18nContext ( ) ;
369+ const localLL = LL . pages . client . pages . settingsPage . tabs . global . defaultInstance ;
370+ const instances = useClientStore ( ( state ) => state . instances ) ;
371+
372+ const options = useMemo ( ( ) : SelectOption < number | null > [ ] => {
373+ const noneOption : SelectOption < number | null > = {
374+ key : - 1 ,
375+ label : localLL . options . none ( ) ,
376+ value : null ,
377+ } ;
378+ const instanceOptions : SelectOption < number | null > [ ] = instances . map (
379+ ( instance : DefguardInstance ) => ( {
380+ key : instance . id ,
381+ label : instance . name ,
382+ value : instance . id ,
383+ } ) ,
384+ ) ;
385+ return [ noneOption , ...instanceOptions ] ;
386+ } , [ instances , localLL . options ] ) ;
387+
388+ const renderSelected = useCallback (
389+ ( value : number | null ) : SelectSelectedValue => {
390+ const option = options . find ( ( o ) => o . value === value ) ;
391+ if ( option ) {
392+ return {
393+ key : option . key ,
394+ displayValue : option . label ,
395+ } ;
396+ }
397+ return {
398+ key : - 1 ,
399+ displayValue : localLL . options . none ( ) ,
400+ } ;
401+ } ,
402+ [ options , localLL . options ] ,
403+ ) ;
404+
405+ return (
406+ < FormSelect
407+ sizeVariant = { SelectSizeVariant . STANDARD }
408+ options = { options }
409+ renderSelected = { renderSelected }
410+ controller = { controller }
411+ />
412+ ) ;
413+ } ;
0 commit comments