diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 527ac4e..bd81341 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -1,16 +1,16 @@ "use client"; -import { useClientSettings } from "@/hooks/use-settings"; +import { useAccountSettings, useClientSettings } from "@/hooks/use-settings"; import { setClientSettings } from "@/lib/clientSettings"; import Settings from "./settings"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Card } from "@/components/ui/card"; import { CardContent } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; import { ACCOUNT_SCHEMA, CLIENT_SCHEMA } from "@/types/settings"; export default function SettingsPage() { const [settings, setSettings] = useClientSettings(); + const [accountSettings, setAccountSettings] = useAccountSettings(); return ( @@ -25,13 +25,8 @@ export default function SettingsPage() { settings={settings} setSettings={setSettings} schema={CLIENT_SCHEMA} + onSave={() => setClientSettings(settings)} /> - @@ -39,13 +34,11 @@ export default function SettingsPage() { {}} /> - diff --git a/src/app/settings/settings.tsx b/src/app/settings/settings.tsx index 2f3d938..01f2c59 100644 --- a/src/app/settings/settings.tsx +++ b/src/app/settings/settings.tsx @@ -1,6 +1,4 @@ -"use client"; - -import { Dispatch, SetStateAction } from "react"; +import { Dispatch, SetStateAction, useState, useRef } from "react"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { @@ -11,27 +9,65 @@ import { SelectValue, } from "@/components/ui/select"; import { StringMap } from "@/types/typeUtils"; +import { Button } from "@/components/ui/button"; + +export class RegexSetting { + placeholder: string; + pattern: RegExp; + + constructor(placeholder: string, pattern: RegExp) { + this.placeholder = placeholder; + this.pattern = pattern; + } +} export type SettingsSchema = StringMap< - "boolean" | "string" | "number" | string[] + "boolean" | "string" | "number" | string[] | RegexSetting >; export default function Settings({ settings, setSettings, schema, + onSave, }: { settings: T; setSettings: Dispatch>; schema: SettingsSchema; + onSave: () => void; }) { + const [feedback, setFeedback] = useState< + undefined | { kind: "error" | "info"; message: string } + >(); + + // Ref to store debounce timer + const debounceRef = useRef(undefined); + const handleChange = (key: string, value: any) => { setSettings((prev) => ({ ...prev, [key]: value })); }; + const handleRegexChange = ( + key: string, + regexSetting: RegexSetting, + value: string + ) => { + handleChange(key, value); + if (debounceRef.current) clearTimeout(debounceRef.current); + debounceRef.current = setTimeout(() => { + if (!regexSetting.pattern.test(value)) { + setFeedback({ + kind: "error", + message: `Value does not match the pattern!`, + }); + } else { + setFeedback(undefined); + } + }, 1500); + }; + const renderInput = (key: string, type: any) => { if (Array.isArray(type)) { - // Dropdown return ( ); } else if (type === "boolean") { - // Checkbox return ( ({ /> ); } else if (type === "string") { - // Text input return ( ({ /> ); } else if (type === "number") { - // Number input return ( ({ onChange={(e) => handleChange(key, Number(e.target.value))} /> ); + } else if (type instanceof RegexSetting) { + return ( + handleRegexChange(key, type, e.target.value)} + /> + ); } }; @@ -86,6 +128,27 @@ export default function Settings({ {renderInput(key, type)} ))} + + {feedback && ( +
+ {feedback.message} +
+ )} + + ); } diff --git a/src/hooks/use-settings.ts b/src/hooks/use-settings.ts index a4e51f5..eb17b47 100644 --- a/src/hooks/use-settings.ts +++ b/src/hooks/use-settings.ts @@ -1,7 +1,7 @@ "use client"; import { defaultSettings, getClientSettings } from "@/lib/clientSettings"; -import { ClientSettings } from "@/types/settings"; +import { AccountSettings, ClientSettings } from "@/types/settings"; import { Dispatch, SetStateAction, useEffect, useState } from "react"; export function useClientSettings(): [ @@ -16,3 +16,14 @@ export function useClientSettings(): [ return [settings, setSettings]; } + +export function useAccountSettings(): [ + AccountSettings, + Dispatch> +] { + const [settings, setSettings] = useState({ + node_ip: "node0.voxa.org", + }); + + return [settings, setSettings]; +} diff --git a/src/types/settings.ts b/src/types/settings.ts index 22a5b15..973f305 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -1,15 +1,20 @@ -import { SettingsSchema } from "@/app/settings/settings"; +import { RegexSetting, SettingsSchema } from "@/app/settings/settings"; export interface ClientSettings { theme: "light" | "dark"; } -export interface AccountSettings {} +export interface AccountSettings { + node_ip: string; +} export const CLIENT_SCHEMA: SettingsSchema = { theme: ["light", "dark"], }; export const ACCOUNT_SCHEMA: SettingsSchema = { - node_ip: "string", + node_ip: new RegexSetting( + "node2.voxa.org", + /^(?:(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(?:\d{1,3}\.){3}\d{1,3})$/ + ), };