-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui, webserver): add general settings (#1405)
* feat(ui): init general settings [autofix.ci] apply automated fixes feat: useFieldArray fix: format fix: text * fix: Separator * [autofix.ci] apply automated fixes * update * fix: domain list keydown * update * update schema.graphql * connect network form * connect security form * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * [autofix.ci] apply automated fixes (attempt 3/3) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Meng Zhang <[email protected]>
- Loading branch information
1 parent
4c193d4
commit 23661f7
Showing
12 changed files
with
552 additions
and
20 deletions.
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
ee/tabby-ui/app/(dashboard)/settings/general/components/form-section.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react' | ||
|
||
import { cn } from '@/lib/utils' | ||
|
||
interface GeneralFormSectionProps | ||
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> { | ||
title: string | ||
} | ||
|
||
export const GeneralFormSection: React.FC<GeneralFormSectionProps> = ({ | ||
title, | ||
className, | ||
children, | ||
...props | ||
}) => { | ||
return ( | ||
<div className={cn('lg:flex', className)} {...props}> | ||
<div className="text-left lg:w-1/5"> | ||
<h1 className="text-2xl font-bold">{title}</h1> | ||
</div> | ||
<div className="flex-1 lg:px-4"> | ||
<div className="mb-7 mt-4 lg:mt-0">{children}</div> | ||
</div> | ||
</div> | ||
) | ||
} |
40 changes: 40 additions & 0 deletions
40
ee/tabby-ui/app/(dashboard)/settings/general/components/general.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
'use client' | ||
|
||
import React from 'react' | ||
|
||
import { Separator } from '@/components/ui/separator' | ||
import { ListSkeleton } from '@/components/skeleton' | ||
|
||
import { GeneralFormSection } from './form-section' | ||
import { GeneralNetworkForm } from './network-form' | ||
import { GeneralSecurityForm } from './security-form' | ||
|
||
export default function General() { | ||
// todo usequery | ||
|
||
const [initialized, setInitialized] = React.useState(false) | ||
|
||
React.useEffect(() => { | ||
setTimeout(() => { | ||
// get data from query and then setInitialized | ||
setInitialized(true) | ||
}, 500) | ||
}, []) | ||
|
||
// makes it convenient to set the defaultValues of forms | ||
if (!initialized) return <ListSkeleton /> | ||
|
||
return ( | ||
<div className="flex flex-col gap-4"> | ||
<GeneralFormSection title="Network"> | ||
{/* todo pass defualtValues from useQuery */} | ||
<GeneralNetworkForm /> | ||
</GeneralFormSection> | ||
<Separator className="mb-8" /> | ||
<GeneralFormSection title="Security"> | ||
{/* todo pass defualtValues from useQuery */} | ||
<GeneralSecurityForm /> | ||
</GeneralFormSection> | ||
</div> | ||
) | ||
} |
135 changes: 135 additions & 0 deletions
135
ee/tabby-ui/app/(dashboard)/settings/general/components/network-form.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
'use client' | ||
|
||
import React from 'react' | ||
import { zodResolver } from '@hookform/resolvers/zod' | ||
import { isEmpty } from 'lodash-es' | ||
import { useForm } from 'react-hook-form' | ||
import { toast } from 'sonner' | ||
import { useQuery } from 'urql' | ||
import * as z from 'zod' | ||
|
||
import { graphql } from '@/lib/gql/generates' | ||
import { useMutation } from '@/lib/tabby/gql' | ||
import { Button } from '@/components/ui/button' | ||
import { | ||
Form, | ||
FormControl, | ||
FormDescription, | ||
FormField, | ||
FormItem, | ||
FormLabel, | ||
FormMessage | ||
} from '@/components/ui/form' | ||
import { Input } from '@/components/ui/input' | ||
|
||
const updateNetworkSettingMutation = graphql(/* GraphQL */ ` | ||
mutation updateNetworkSettingMutation($input: NetworkSettingInput!) { | ||
updateNetworkSetting(input: $input) | ||
} | ||
`) | ||
|
||
export const networkSetting = graphql(/* GraphQL */ ` | ||
query NetworkSetting { | ||
networkSetting { | ||
externalUrl | ||
} | ||
} | ||
`) | ||
|
||
const formSchema = z.object({ | ||
externalUrl: z.string() | ||
}) | ||
|
||
type NetworkFormValues = z.infer<typeof formSchema> | ||
|
||
interface NetworkFormProps { | ||
defaultValues?: Partial<NetworkFormValues> | ||
onSuccess?: () => void | ||
} | ||
|
||
const NetworkForm: React.FC<NetworkFormProps> = ({ | ||
onSuccess, | ||
defaultValues: propsDefaultValues | ||
}) => { | ||
const defaultValues = React.useMemo(() => { | ||
return { | ||
...(propsDefaultValues || {}) | ||
} | ||
}, [propsDefaultValues]) | ||
|
||
const form = useForm<z.infer<typeof formSchema>>({ | ||
resolver: zodResolver(formSchema), | ||
defaultValues | ||
}) | ||
|
||
const isDirty = !isEmpty(form.formState.dirtyFields) | ||
|
||
const updateNetworkSetting = useMutation(updateNetworkSettingMutation, { | ||
form, | ||
onCompleted(values) { | ||
if (values?.updateNetworkSetting) { | ||
onSuccess?.() | ||
form.reset(form.getValues()) | ||
} | ||
} | ||
}) | ||
|
||
const onSubmit = async () => { | ||
await updateNetworkSetting({ | ||
input: form.getValues() | ||
}) | ||
} | ||
|
||
return ( | ||
<Form {...form}> | ||
<div className="flex flex-col gap-4"> | ||
<form | ||
className="flex flex-col gap-8" | ||
onSubmit={form.handleSubmit(onSubmit)} | ||
> | ||
<FormField | ||
control={form.control} | ||
name="externalUrl" | ||
render={({ field }) => ( | ||
<FormItem> | ||
<FormLabel>External URL</FormLabel> | ||
<FormDescription> | ||
The external URL where user visits Tabby, must start with | ||
http:// or https://. | ||
</FormDescription> | ||
<FormControl> | ||
<Input | ||
placeholder="http://localhost:8080" | ||
autoCapitalize="none" | ||
autoComplete="off" | ||
autoCorrect="off" | ||
{...field} | ||
/> | ||
</FormControl> | ||
<FormMessage /> | ||
</FormItem> | ||
)} | ||
/> | ||
<div className="mt-2 flex justify-end"> | ||
<Button type="submit" disabled={!isDirty}> | ||
Update | ||
</Button> | ||
</div> | ||
</form> | ||
<FormMessage className="text-center" /> | ||
</div> | ||
</Form> | ||
) | ||
} | ||
|
||
export const GeneralNetworkForm = () => { | ||
const [{ data: data }] = useQuery({ query: networkSetting }) | ||
const onSuccess = () => { | ||
toast.success('Network configuration is updated') | ||
} | ||
return ( | ||
data && ( | ||
<NetworkForm defaultValues={data.networkSetting} onSuccess={onSuccess} /> | ||
) | ||
) | ||
} |
Oops, something went wrong.