💄 feat: Optimize UI components and input logic

This commit is contained in:
jarvis2f 2025-01-07 23:10:27 +08:00
parent 236bf0ac1f
commit 2220e33a1e
5 changed files with 56 additions and 22 deletions

View file

@ -30,11 +30,11 @@ export default function ProxyPing({ accountId }: { accountId: string }) {
>
<ChevronsLeftRightEllipsis className="h-4 w-4 text-gray-500" />
{isLoading && (
<div className="h-6 w-24 animate-pulse rounded bg-gray-200"></div>
<div className="h-6 w-16 animate-pulse rounded bg-gray-200"></div>
)}
{!isLoading && error && <span>Connection error</span>}
{!isLoading && data && (
<div className="flex items-center space-x-2">
<div className="flex h-6 items-center space-x-2">
<span className="text-sm">{(data.ping * 1000).toFixed(0)} ms</span>
</div>
)}

View file

@ -25,6 +25,7 @@ import useSWRMutation from "swr/mutation";
import { request } from "@/lib/api";
import { toast } from "@/hooks/use-toast";
import ProxyPing from "@/components/proxy-ping";
import { Label } from "./ui/label";
export interface ProxysProps {
enableSelect?: boolean;
@ -231,34 +232,46 @@ export default function Proxys({
<div className="space-y-4">
{/* Radio buttons for proxy type */}
<div>
<label className="mb-1 block text-lg text-gray-700">Type</label>
<Label className="mb-2 block">Type</Label>
<div className="flex space-x-4">
<label className="flex items-center space-x-2">
<label className="flex cursor-pointer items-center">
<input
type="radio"
name="type"
value="http"
checked={formState.type === "http"}
onChange={handleInputChange}
className="peer hidden"
/>
<span>HTTP</span>
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-full border-2 border-gray-300 peer-checked:border-primary peer-checked:bg-primary">
<div className="h-2.5 w-2.5 rounded-full bg-white"></div>
</div>
<span className="text-gray-700 peer-checked:text-primary">
HTTP
</span>
</label>
<label className="flex items-center space-x-2">
<label className="flex cursor-pointer items-center">
<input
type="radio"
name="type"
value="socks5"
checked={formState.type === "socks5"}
onChange={handleInputChange}
className="peer hidden"
/>
<span>SOCKS5</span>
<div className="mr-2 flex h-5 w-5 items-center justify-center rounded-full border-2 border-gray-300 peer-checked:border-primary peer-checked:bg-primary">
<div className="h-2.5 w-2.5 rounded-full bg-white"></div>
</div>
<span className="text-gray-400 peer-checked:text-primary">
SOCKS5
</span>
</label>
</div>
</div>
{/* Other input fields */}
<div>
<label className="mb-1 block text-lg text-gray-700">Name</label>
<Label className="mb-2 block">Name</Label>
<Input
name="name"
value={formState.name}
@ -267,12 +280,12 @@ export default function Proxys({
/>
</div>
<div>
<p className="mb-1 text-lg text-gray-700">
<Label className="mb-2 block">
Proxy server address and port number
</p>
</Label>
<div className="flex space-x-4">
<div className="flex-1">
<label className="block text-sm font-medium text-gray-500">
<label className="block text-xs font-medium text-gray-500">
Server
</label>
<Input
@ -283,7 +296,7 @@ export default function Proxys({
/>
</div>
<div className="w-24">
<label className="block text-sm font-medium text-gray-500">
<label className="block text-xs font-medium text-gray-500">
Port
</label>
<Input
@ -296,11 +309,9 @@ export default function Proxys({
</div>
</div>
</div>
<p className="mb-1 text-lg text-gray-700">
Authentication (optional)
</p>
<Label className="mb-2 block">Authentication (optional)</Label>
<div>
<label className="block text-sm font-medium text-gray-500">
<label className="block text-xs font-medium text-gray-500">
Username
</label>
<Input
@ -311,7 +322,7 @@ export default function Proxys({
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-500">
<label className="block text-xs font-medium text-gray-500">
Password
</label>
<Input

View file

@ -1,4 +1,4 @@
import { Bell } from "lucide-react";
import {Bell, Copy} from "lucide-react";
import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import {
@ -12,9 +12,13 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import React, { type FormEvent } from "react";
import { useSettings } from "@/hooks/use-settings";
import { useTelegramAccount } from "@/hooks/use-telegram-account";
import {useCopyToClipboard} from "@/hooks/use-copy-to-clipboard";
export default function SettingsForm() {
const { settings, setSetting, updateSettings } = useSettings();
const { account } = useTelegramAccount();
const [, copyToClipboard] = useCopyToClipboard();
const imageLoadSizeOptions = [
{ value: "s", label: "box 100x100" },
@ -34,7 +38,7 @@ export default function SettingsForm() {
{ value: "600", label: "10 minutes" },
{ value: "900", label: "15 minutes" },
{ value: "1800", label: "30 minutes" },
]
];
const handleSave = async (e: FormEvent) => {
e.preventDefault();
@ -51,6 +55,24 @@ export default function SettingsForm() {
<Bell className="mr-2 inline-block h-4 w-4" />
These settings will be applied to all accounts.
</p>
<div className="w-full rounded-md border p-4 shadow">
<p className="mb-1 text-xs text-muted-foreground">Your root path</p>
<div className="flex items-center justify-between space-x-1">
<p className="bg-gray-50 rounded-md p-2 text-xs text-muted-foreground">
{account?.rootPath}
</p>
<Button
variant="ghost"
size="sm"
onClick={(e) => {
e.preventDefault();
void copyToClipboard(account?.rootPath ?? "");
}}
>
<Copy className="h-4 w-4" />
</Button>
</div>
</div>
<div className="flex w-full flex-col space-y-4 rounded-md border p-4 shadow">
<div className="flex items-center space-x-2">
<Label htmlFor="unique-only">Unique Only</Label>

View file

@ -18,14 +18,14 @@ export function Toaster() {
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
<div className="grid gap-1" onClick={(e) => e.stopPropagation()}>
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</div>
{action}
<ToastClose />
<ToastClose onClick={(e) => e.stopPropagation()} />
</Toast>
);
})}

View file

@ -8,6 +8,7 @@ export type TelegramAccount = {
status: "active" | "inactive";
lastAuthorizationState?: TelegramObject;
proxy?: string;
rootPath: string;
};
export type TelegramChat = {