Browse Source

validation

pull/266/head
Hunter Thornsberry 2 years ago
parent
commit
1eedb6d97b
  1. 5
      src/components/Form/DynamicForm.tsx
  2. 12
      src/components/Form/FormPasswordGenerator.tsx
  3. 5
      src/components/Form/FormWrapper.tsx
  4. 54
      src/components/PageComponents/Channel.tsx
  5. 67
      src/components/UI/Generator.tsx
  6. 31
      src/components/UI/Input.tsx

5
src/components/Form/DynamicForm.tsx

@ -26,6 +26,7 @@ export interface BaseFormBuilderProps<T> {
disabledBy?: DisabledBy<T>[];
label: string;
description?: string;
validationText?: string;
properties?: Record<string, unknown>;
}
@ -44,6 +45,8 @@ export interface DynamicFormProps<T extends FieldValues> {
fieldGroups: {
label: string;
description: string;
valid?: boolean;
validationText?: string;
fields: FieldProps<T>[];
}[];
}
@ -98,6 +101,8 @@ export function DynamicForm<T extends FieldValues>({
key={field.label}
label={field.label}
description={field.description}
valid={field.validationText == undefined || field.validationText == ""}
validationText={field.validationText}
>
<DynamicFormField
field={field}

12
src/components/Form/FormPasswordGenerator.tsx

@ -3,11 +3,15 @@ import type {
GenericFormElementProps,
} from "@components/Form/DynamicForm.js";
import { Generator } from "@components/UI/Generator.js";
import { ChangeEventHandler, MouseEventHandler } from "react";
import { Controller, type FieldValues } from "react-hook-form";
export interface PasswordGeneratorProps<T> extends BaseFormBuilderProps<T> {
type: "passwordGenerator";
devicePSKBitCount: number;
inputChange: ChangeEventHandler;
selectChange: (event: string) => void;
buttonClick: MouseEventHandler;
}
export function PasswordGenerator<T extends FieldValues>({
@ -18,12 +22,14 @@ export function PasswordGenerator<T extends FieldValues>({
<Controller
name={field.name}
control={control}
render={({ field: { value, onChange, ...rest } }) => (
render={({ field: { value,...rest } }) => (
<Generator
devicePSKBitCount={field.devicePSKBitCount}
changeEvent={onChange}
inputChange={field.inputChange}
selectChange={field.selectChange}
buttonClick={field.buttonClick}
value={value}
variant={"success"}
variant={field.validationText ? "invalid" : "default"}
buttonText="Generate"
{...field.properties}
{...rest}

5
src/components/Form/FormWrapper.tsx

@ -5,12 +5,16 @@ export interface FieldWrapperProps {
description?: string;
disabled?: boolean;
children?: React.ReactNode;
valid?: boolean;
validationText?: string;
}
export const FieldWrapper = ({
label,
description,
children,
valid,
validationText,
}: FieldWrapperProps): JSX.Element => (
<div className="pt-6 sm:pt-5">
<div role="group" aria-labelledby="label-notifications">
@ -19,6 +23,7 @@ export const FieldWrapper = ({
<div className="sm:col-span-2">
<div className="max-w-lg">
<p className="text-sm text-gray-500">{description}</p>
<p hidden={valid ?? true} className="text-sm text-red-500">{validationText}</p>
<div className="mt-4 space-y-4">
<div className="flex items-center">{children}</div>
</div>

54
src/components/PageComponents/Channel.tsx

@ -21,6 +21,7 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
const [bitCount, setBits] = useState<number>(
channel?.settings?.psk.length ?? 16,
);
const [validationText, setValidationText] = useState<string>();
const onSubmit = (data: ChannelValidation) => {
const channel = new Protobuf.Channel.Channel({
@ -54,8 +55,53 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
}),
),
);
setValidationText(undefined);
};
const validatePass = (input: string, count: number) => {
if (count == 32) {
if (input.length != 44) {
setValidationText("Please enter a valid 256 bit PSK.");
}
else {
setValidationText(undefined);
}
}
else if (count == 16)
{
if (input.length != 24) {
setValidationText("Please enter a valid 128 bit PSK.");
}
else {
setValidationText(undefined);
}
}
else if (count == 1)
{
if (input.length != 4) {
setValidationText("Please enter a valid 1 bit PSK");
}
else {
setValidationText(undefined);
}
}
else {
setValidationText("Unkown PSK length.");
}
}
const inputChangeEvent = (e) => {
let psk = e.currentTarget?.value;
setPass(psk);
validatePass(psk, bitCount);
};
const selectChangeEvent = (e: string) => {
let count = Number.parseInt(e);
setBits(count);
validatePass(pass, count);
}
return (
<DynamicForm<ChannelValidation>
onSubmit={onSubmit}
@ -100,11 +146,13 @@ export const Channel = ({ channel }: SettingsPanelProps): JSX.Element => {
name: "settings.psk",
label: "pre-Shared Key",
description: "256, 128, or 8 bit PSKs allowed",
validationText: validationText,
devicePSKBitCount: bitCount ?? 0,
inputChange: inputChangeEvent,
selectChange: selectChangeEvent,
buttonClick: clickEvent,
properties: {
value: pass,
onClick: clickEvent,
changeEvent: (e: string) => setBits(Number.parseInt(e)),
value: pass
},
},
{

67
src/components/UI/Generator.tsx

@ -1,4 +1,3 @@
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { Button } from "@components/UI/Button.js";
@ -11,67 +10,35 @@ import {
SelectValue,
} from "@components/UI/Select.js";
const generatorVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus:outline-none focus:ring-2",
{
variants: {
variant: {
default: "",
destructive:
"bg-red-500 text-white hover:bg-red-600 dark:hover:bg-red-600",
success:
"bg-green-500 text-white hover:bg-green-600 dark:hover:bg-green-600",
outline:
"bg-transparent border border-slate-200 hover:bg-slate-100 dark:border-slate-700 dark:text-slate-100",
subtle:
"bg-slate-100 text-slate-900 hover:bg-slate-200 dark:bg-slate-700 dark:text-slate-100",
ghost:
"bg-transparent hover:bg-slate-100 dark:hover:bg-slate-800 dark:text-slate-100 dark:hover:text-slate-100 data-[state=open]:bg-transparent dark:data-[state=open]:bg-transparent",
link: "bg-transparent underline-offset-4 hover:underline text-slate-900 dark:text-slate-100 hover:bg-transparent dark:hover:bg-transparent",
},
size: {
default: "h-10 py-2 px-4",
sm: "h-9 px-2 rounded-md",
lg: "h-11 px-8 rounded-md",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface GeneratorProps
extends React.BaseHTMLAttributes<HTMLElement>,
VariantProps<typeof generatorVariants> {
extends React.BaseHTMLAttributes<HTMLElement> {
devicePSKBitCount?: number;
value: string;
variant: "default" | "invalid";
buttonText?: string;
changeEvent: (event: string) => void;
selectChange: (event: string) => void;
inputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
buttonClick: React.MouseEventHandler<HTMLButtonElement>;
}
const getBitString = (bitcount?: number) => {
if (bitcount === 32) {
return "32";
}
if (bitcount === 1) {
return "1";
}
return "16";
};
const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
(
{ devicePSKBitCount, value, buttonText, variant, changeEvent, ...props },
{ devicePSKBitCount, variant, value, buttonText, selectChange, inputChange, buttonClick, ...props },
ref,
) => {
return (
<>
<Input type="text" id="pskInput" value={value} />
<Input
type="text"
id="pskInput"
variant={variant}
value={value}
onChange={inputChange}
/>
<Select
value={getBitString(devicePSKBitCount)}
onValueChange={(e) => changeEvent(e)}
value={devicePSKBitCount?.toString()}
onValueChange={(e) => selectChange(e)}
>
<SelectTrigger>
<SelectValue />
@ -88,7 +55,7 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
</SelectItem>
</SelectContent>
</Select>
<Button type="button" variant="success" {...props}>
<Button type="button" variant="success" onClick={buttonClick} {...props}>
{buttonText}
</Button>
</>
@ -97,4 +64,4 @@ const Generator = React.forwardRef<HTMLInputElement, GeneratorProps>(
);
Generator.displayName = "Button";
export { Generator, generatorVariants };
export { Generator };

31
src/components/UI/Input.tsx

@ -2,9 +2,28 @@ import * as React from "react";
import { cn } from "@core/utils/cn.js";
import type { LucideIcon } from "lucide-react";
import { cva, VariantProps } from "class-variance-authority";
const inputVariants = cva(
"flex h-10 w-full rounded-md border bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900",
{
variants: {
variant: {
default:
"border-slate-300 dark:border-slate-700",
invalid:
"border-red-500 dark:border-red-500",
},
},
defaultVariants: {
variant: "default",
},
},
);
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
extends React.InputHTMLAttributes<HTMLInputElement>,
VariantProps<typeof inputVariants> {
prefix?: string;
suffix?: string;
action?: {
@ -14,7 +33,7 @@ export interface InputProps
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, prefix, suffix, action, ...props }, ref) => {
({ className, variant, prefix, suffix, action, ...props }, ref) => {
return (
<div className="relative w-full">
{prefix && (
@ -23,11 +42,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
</span>
)}
<input
className={cn(
"flex h-10 w-full rounded-md border border-slate-300 bg-transparent py-2 px-3 text-sm placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-700 dark:text-slate-50 dark:focus:ring-slate-400 dark:focus:ring-offset-slate-900",
action && "pr-8",
className,
)}
className={cn(action && "pr-8", className, inputVariants({ variant }))}
ref={ref}
{...props}
/>
@ -51,4 +66,4 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
);
Input.displayName = "Input";
export { Input };
export { Input, inputVariants };

Loading…
Cancel
Save