mirror of
				https://github.com/VaalaCat/frp-panel.git
				synced 2025-10-31 02:36:44 +08:00 
			
		
		
		
	chore: frontend form style
This commit is contained in:
		| @@ -1,7 +1,7 @@ | |||||||
| "use client" | "use client" | ||||||
|  |  | ||||||
| import * as React from "react" | import * as React from "react" | ||||||
| import { Check, ChevronsUpDown } from "lucide-react" | import { Check } from "lucide-react" | ||||||
| import { cn } from "@/lib/utils" | import { cn } from "@/lib/utils" | ||||||
| import { Button } from "@/components/ui/button" | import { Button } from "@/components/ui/button" | ||||||
| import { useDebouncedCallback } from 'use-debounce' | import { useDebouncedCallback } from 'use-debounce' | ||||||
| @@ -19,6 +19,7 @@ import { | |||||||
|   PopoverContent, |   PopoverContent, | ||||||
|   PopoverTrigger, |   PopoverTrigger, | ||||||
| } from "@/components/ui/popover" | } from "@/components/ui/popover" | ||||||
|  | import { CaretSortIcon } from "@radix-ui/react-icons" | ||||||
|  |  | ||||||
| export interface ComboboxProps { | export interface ComboboxProps { | ||||||
|   value?: string |   value?: string | ||||||
| @@ -67,17 +68,17 @@ export function Combobox({ | |||||||
|           variant="outline" |           variant="outline" | ||||||
|           role="combobox" |           role="combobox" | ||||||
|           aria-expanded={open} |           aria-expanded={open} | ||||||
|           className={cn("w-full justify-between font-normal", className, |           className={cn("w-full justify-between font-normal px-3", className, | ||||||
|             !value && "text-muted-foreground" |             !value && "text-muted-foreground" | ||||||
|           )} |           )} | ||||||
|         > |         > | ||||||
|           {value |           {value | ||||||
|             ? (dataList.find((item) => item.value === value)?.label || value) |             ? (dataList.find((item) => item.value === value)?.label || value) | ||||||
|             : (placeholder || defaultPlaceholder)} |             : (placeholder || defaultPlaceholder)} | ||||||
|           <ChevronsUpDown className="opacity-50 h-[12px] w-[12px]" /> |           <CaretSortIcon className="h-4 w-4 opacity-50" /> | ||||||
|         </Button> |         </Button> | ||||||
|       </PopoverTrigger> |       </PopoverTrigger> | ||||||
|       <PopoverContent className="w-[200px] p-0" align="start"> |       <PopoverContent className="w-[--radix-popover-trigger-width] p-0" align="start"> | ||||||
|         <Command> |         <Command> | ||||||
|           <CommandInput  |           <CommandInput  | ||||||
|             value={keyword}  |             value={keyword}  | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ import { | |||||||
| } from '@/components/ui/dialog' | } from '@/components/ui/dialog' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { toast } from 'sonner' | import { toast } from 'sonner' | ||||||
|  | import { IsIDValid } from '@/lib/consts' | ||||||
|  |  | ||||||
| export const CreateClientDialog = ({refetchTrigger}: {refetchTrigger?: (randStr: string) => void}) => { | export const CreateClientDialog = ({refetchTrigger}: {refetchTrigger?: (randStr: string) => void}) => { | ||||||
|   const { t } = useTranslation() |   const { t } = useTranslation() | ||||||
| @@ -60,9 +61,11 @@ export const CreateClientDialog = ({refetchTrigger}: {refetchTrigger?: (randStr: | |||||||
|         </DialogHeader> |         </DialogHeader> | ||||||
|  |  | ||||||
|         <Label>{t('client.create.id')}</Label> |         <Label>{t('client.create.id')}</Label> | ||||||
|         <Input className="mt-2" value={clientID} onChange={(e) => setClientID(e.target.value)} /> |         <Input value={clientID} onChange={(e) => setClientID(e.target.value)} /> | ||||||
|         <DialogFooter> |         <DialogFooter> | ||||||
|           <Button onClick={handleNewClient}>{t('client.create.submit')}</Button> |           <Button onClick={handleNewClient} | ||||||
|  |           disabled={!IsIDValid(clientID)} | ||||||
|  |           className='w-full'>{t('client.create.submit')}</Button> | ||||||
|         </DialogFooter> |         </DialogFooter> | ||||||
|       </DialogContent> |       </DialogContent> | ||||||
|     </Dialog> |     </Dialog> | ||||||
|   | |||||||
| @@ -18,8 +18,9 @@ import { | |||||||
| } from '@/components/ui/dialog' | } from '@/components/ui/dialog' | ||||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||||
| import { toast } from 'sonner' | import { toast } from 'sonner' | ||||||
|  | import { IsIDValid } from '@/lib/consts' | ||||||
|  |  | ||||||
| export const CreateServerDialog = ({refetchTrigger}: {refetchTrigger?: (randStr: string) => void}) => { | export const CreateServerDialog = ({ refetchTrigger }: { refetchTrigger?: (randStr: string) => void }) => { | ||||||
|   const { t } = useTranslation() |   const { t } = useTranslation() | ||||||
|   const [serverID, setServerID] = useState<string | undefined>() |   const [serverID, setServerID] = useState<string | undefined>() | ||||||
|   const [serverIP, setServerIP] = useState<string | undefined>() |   const [serverIP, setServerIP] = useState<string | undefined>() | ||||||
| @@ -60,11 +61,13 @@ export const CreateServerDialog = ({refetchTrigger}: {refetchTrigger?: (randStr: | |||||||
|         </DialogHeader> |         </DialogHeader> | ||||||
|  |  | ||||||
|         <Label>{t('server.create.id')}</Label> |         <Label>{t('server.create.id')}</Label> | ||||||
|         <Input className="mt-2" value={serverID} onChange={(e) => setServerID(e.target.value)} /> |         <Input value={serverID} onChange={(e) => setServerID(e.target.value)} /> | ||||||
|         <Label>{t('server.create.ip')}</Label> |         <Label>{t('server.create.ip')}</Label> | ||||||
|         <Input className="mt-2" value={serverIP} onChange={(e) => setServerIP(e.target.value)} /> |         <Input value={serverIP} onChange={(e) => setServerIP(e.target.value)} /> | ||||||
|         <DialogFooter> |         <DialogFooter> | ||||||
|           <Button onClick={handleNewServer}>{t('server.create.submit')}</Button> |           <Button onClick={handleNewServer} | ||||||
|  |             disabled={!IsIDValid(serverID) || !serverIP} | ||||||
|  |             className='w-full'>{t('server.create.submit')}</Button> | ||||||
|         </DialogFooter> |         </DialogFooter> | ||||||
|       </DialogContent> |       </DialogContent> | ||||||
|     </Dialog> |     </Dialog> | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import { | |||||||
|   Dialog, |   Dialog, | ||||||
|   DialogContent, |   DialogContent, | ||||||
|   DialogDescription, |   DialogDescription, | ||||||
|   DialogFooter, |  | ||||||
|   DialogHeader, |   DialogHeader, | ||||||
|   DialogTitle, |   DialogTitle, | ||||||
|   DialogTrigger, |   DialogTrigger, | ||||||
| @@ -47,6 +46,12 @@ export const ProxyConfigMutateDialog = ({ ...props }: ProxyConfigMutateDialogPro | |||||||
|         </Button> |         </Button> | ||||||
|       </DialogTrigger> |       </DialogTrigger> | ||||||
|       <DialogContent className='max-h-screen overflow-auto'> |       <DialogContent className='max-h-screen overflow-auto'> | ||||||
|  |         <DialogHeader> | ||||||
|  |           <DialogTitle>{t('proxy.config.create_proxy')}</DialogTitle> | ||||||
|  |           <DialogDescription> | ||||||
|  |             {t('proxy.config.create_proxy_description')} | ||||||
|  |           </DialogDescription> | ||||||
|  |         </DialogHeader> | ||||||
|         <ProxyConfigMutateForm {...props} /> |         <ProxyConfigMutateForm {...props} /> | ||||||
|       </DialogContent> |       </DialogContent> | ||||||
|     </Dialog> |     </Dialog> | ||||||
| @@ -113,14 +118,14 @@ export const ProxyConfigMutateForm = ({ overwrite, defaultProxyConfig, defaultOr | |||||||
|         value={proxyType} |         value={proxyType} | ||||||
|         setValue={(value) => { setProxyType(value as ProxyType) }} |         setValue={(value) => { setProxyType(value as ProxyType) }} | ||||||
|       /> |       /> | ||||||
|       <div className='flex flex-row w-full overflow-auto'> |       {proxyConfigs && selectedServer && proxyConfigs.length > 0 && | ||||||
|         {proxyConfigs && selectedServer && proxyConfigs.length > 0 && |         proxyConfigs[0] && TypedProxyConfigValid(proxyConfigs[0]) && | ||||||
|           proxyConfigs[0] && TypedProxyConfigValid(proxyConfigs[0]) && |         <div className='flex flex-row w-full overflow-auto'> | ||||||
|           <div className='flex flex-col'> |           <div className='flex flex-col'> | ||||||
|             <VisitPreview server={selectedServer} typedProxyConfig={proxyConfigs[0]} /> |             <VisitPreview server={selectedServer} typedProxyConfig={proxyConfigs[0]} /> | ||||||
|           </div> |           </div> | ||||||
|         } |         </div> | ||||||
|       </div> |       } | ||||||
|       <Label>{t('proxy.config.proxy_name')} </Label> |       <Label>{t('proxy.config.proxy_name')} </Label> | ||||||
|       <Input className='text-sm' defaultValue={proxyName} onChange={(e) => setProxyName(e.target.value)} disabled={disableChangeProxyName} /> |       <Input className='text-sm' defaultValue={proxyName} onChange={(e) => setProxyName(e.target.value)} disabled={disableChangeProxyName} /> | ||||||
|       {proxyName && newClientID && newServerID && <TypedProxyForm |       {proxyName && newClientID && newServerID && <TypedProxyForm | ||||||
|   | |||||||
| @@ -353,6 +353,9 @@ | |||||||
|     }, |     }, | ||||||
|     "config": { |     "config": { | ||||||
|       "create": "Create", |       "create": "Create", | ||||||
|  |       "create_proxy": "Create Tunnel", | ||||||
|  |       "create_proxy_description": "Expose the client's services to the server. The same client tunnel name must be unique", | ||||||
|  |       "close": "Close", | ||||||
|       "create_success": "Create successful", |       "create_success": "Create successful", | ||||||
|       "create_failed": "Create failed", |       "create_failed": "Create failed", | ||||||
|       "select_server": "Select Server", |       "select_server": "Select Server", | ||||||
|   | |||||||
| @@ -353,6 +353,9 @@ | |||||||
|     }, |     }, | ||||||
|     "config": { |     "config": { | ||||||
|       "create": "创建", |       "create": "创建", | ||||||
|  |       "create_proxy": "创建隧道", | ||||||
|  |       "create_proxy_description": "将客户端的服务暴露在服务端,同一个客户端隧道名称必须唯一", | ||||||
|  |       "close": "关闭", | ||||||
|       "create_success": "创建成功", |       "create_success": "创建成功", | ||||||
|       "create_failed": "创建失败", |       "create_failed": "创建失败", | ||||||
|       "select_server": "选择服务器", |       "select_server": "选择服务器", | ||||||
|   | |||||||
| @@ -24,6 +24,14 @@ export const TypedProxyConfigValid = (typedProxyCfg: TypedProxyConfig | undefine | |||||||
|   return (typedProxyCfg?.localPort && typedProxyCfg.localIP && typedProxyCfg.name && typedProxyCfg.type) ? true : false |   return (typedProxyCfg?.localPort && typedProxyCfg.localIP && typedProxyCfg.name && typedProxyCfg.type) ? true : false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export const IsIDValid = (clientID: string|undefined): boolean => { | ||||||
|  |   if (clientID == undefined) { | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |   const regex = /^[a-zA-Z0-9-_]+$/; | ||||||
|  |   return clientID.length > 0 && regex.test(clientID); | ||||||
|  | } | ||||||
|  |  | ||||||
| export const ClientConfigured = (client: Client | undefined): boolean => { | export const ClientConfigured = (client: Client | undefined): boolean => { | ||||||
|   if (client == undefined) { |   if (client == undefined) { | ||||||
|     return false |     return false | ||||||
|   | |||||||
| @@ -104,3 +104,10 @@ | |||||||
|     @apply bg-background text-foreground; |     @apply bg-background text-foreground; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @layer utilities { | ||||||
|  |   .popover-content-width-full { | ||||||
|  |     width: var(--radix-popover-trigger-width); | ||||||
|  |     max-height: var(--radix-popover-content-available-height); | ||||||
|  |   } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 VaalaCat
					VaalaCat