refactor(web) add eslint (#222)

* fix(tsconfig.json): changed skipLibCheck to false.
refactor(eslint): moved configuration from package.json to .eslintrc.js and added a typescript plugin for future use

* feat: wip eslint and types

* feat: fix identation

* feat: get rid of last any types
This commit is contained in:
stacksmash76 2022-05-17 06:44:07 +02:00 committed by GitHub
parent 7f06a4c707
commit cb8f280e86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 6797 additions and 6541 deletions

View file

@ -8,27 +8,27 @@ interface CheckboxProps {
}
export const Checkbox = ({ label, description, value, setValue }: CheckboxProps) => (
<Switch.Group as="li" className="py-4 flex items-center justify-between">
<div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white" passive>
{label}
</Switch.Label>
{description === undefined ? null : (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-400">
{description}
</Switch.Description>
)}
</div>
<Switch
checked={value}
onChange={setValue}
className={
`${value ? 'bg-teal-500 dark:bg-blue-500' : 'bg-gray-200 dark:bg-gray-700'
} ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500`}
>
<span
className={`${value ? 'translate-x-5' : 'translate-x-0'} inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200`}
/>
</Switch>
</Switch.Group>
<Switch.Group as="li" className="py-4 flex items-center justify-between">
<div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white" passive>
{label}
</Switch.Label>
{description === undefined ? null : (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-400">
{description}
</Switch.Description>
)}
</div>
<Switch
checked={value}
onChange={setValue}
className={
`${value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-700"
} ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500`}
>
<span
className={`${value ? "translate-x-5" : "translate-x-0"} inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200`}
/>
</Switch>
</Switch.Group>
);

View file

@ -5,13 +5,13 @@ interface IconProps {
}
export const SortIcon = ({ className }: IconProps) => (
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41zm255-105L177 64c-9.4-9.4-24.6-9.4-33.9 0L24 183c-15.1 15.1-4.4 41 17 41h238c21.4 0 32.1-25.9 17-41z"></path></svg>
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41zm255-105L177 64c-9.4-9.4-24.6-9.4-33.9 0L24 183c-15.1 15.1-4.4 41 17 41h238c21.4 0 32.1-25.9 17-41z"></path></svg>
);
export const SortUpIcon = ({ className }: IconProps) => (
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M279 224H41c-21.4 0-32.1-25.9-17-41L143 64c9.4-9.4 24.6-9.4 33.9 0l119 119c15.2 15.1 4.5 41-16.9 41z"></path></svg>
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M279 224H41c-21.4 0-32.1-25.9-17-41L143 64c9.4-9.4 24.6-9.4 33.9 0l119 119c15.2 15.1 4.5 41-16.9 41z"></path></svg>
);
export const SortDownIcon = ({ className }: IconProps) => (
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41z"></path></svg>
<svg className={className} stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 320 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M41 288h238c21.4 0 32.1 25.9 17 41L177 448c-9.4 9.4-24.6 9.4-33.9 0L24 329c-15.1-15.1-4.4-41 17-41z"></path></svg>
);

View file

@ -51,7 +51,8 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
role="alert"
>
<div className="flex items-center">
<svg className="mr-2 w-5 h-5 text-red-700 dark:text-red-800" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<svg className="mr-2 w-5 h-5 text-red-700 dark:text-red-800" fill="currentColor" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
@ -77,11 +78,11 @@ export const ErrorPage = ({ error, resetErrorBoundary }: FallbackProps) => {
resetErrorBoundary();
}}
>
<RefreshIcon className="-ml-0.5 mr-2 h-5 w-5" />
<RefreshIcon className="-ml-0.5 mr-2 h-5 w-5"/>
Reset page state
</button>
</div>
</div>
</div>
);
}
};

View file

@ -1,34 +1,37 @@
import { classNames } from "../../utils"
import React from "react";
import { classNames } from "../../utils";
interface ButtonProps {
className?: string;
children: any;
[rest: string]: any;
children: React.ReactNode;
disabled?: boolean;
onClick?: () => void;
}
export const Button = ({ children, className, ...rest }: ButtonProps) => (
<button
type="button"
className={classNames(
className ?? "",
"relative inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-800 text-sm font-medium rounded-md text-gray-700 dark:text-gray-500 bg-white dark:bg-gray-800 hover:bg-gray-50"
)}
{...rest}
>
{children}
</button>
export const Button = ({ children, className, disabled, onClick }: ButtonProps) => (
<button
type="button"
className={classNames(
className ?? "",
"relative inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-800 text-sm font-medium rounded-md text-gray-700 dark:text-gray-500 bg-white dark:bg-gray-800 hover:bg-gray-50"
)}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);
export const PageButton = ({ children, className, ...rest }: ButtonProps) => (
<button
type="button"
className={classNames(
className ?? "",
"relative inline-flex items-center px-2 py-2 border border-gray-300 dark:border-gray-700 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-600"
)}
{...rest}
>
{children}
</button>
export const PageButton = ({ children, className, disabled, onClick }: ButtonProps) => (
<button
type="button"
className={classNames(
className ?? "",
"relative inline-flex items-center px-2 py-2 border border-gray-300 dark:border-gray-700 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-600"
)}
disabled={disabled}
onClick={onClick}
>
{children}
</button>
);

View file

@ -10,24 +10,22 @@ interface CellProps {
}
export const AgeCell = ({ value }: CellProps) => (
<div className="text-sm text-gray-500" title={value}>
{formatDistanceToNowStrict(new Date(value), { addSuffix: true })}
</div>
<div className="text-sm text-gray-500" title={value}>
{formatDistanceToNowStrict(new Date(value), { addSuffix: true })}
</div>
);
export const TitleCell = ({ value }: CellProps) => (
<div
className="text-sm font-medium box-content text-gray-900 dark:text-gray-300 max-w-[128px] sm:max-w-none overflow-auto py-4"
title={value}
>
{value}
</div>
<div
className="text-sm font-medium box-content text-gray-900 dark:text-gray-300 max-w-[128px] sm:max-w-none overflow-auto py-4"
title={value}
>
{value}
</div>
);
interface ReleaseStatusCellProps {
value: ReleaseActionStatus[];
column: any;
row: any;
}
interface StatusCellMapEntry {
@ -36,22 +34,22 @@ interface StatusCellMapEntry {
}
const StatusCellMap: Record<string, StatusCellMapEntry> = {
"PUSH_ERROR": {
colors: "bg-pink-100 text-pink-800 hover:bg-pink-300",
icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_REJECTED": {
colors: "bg-blue-200 dark:bg-blue-100 text-blue-400 dark:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-400",
icon: <BanIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_APPROVED": {
colors: "bg-green-100 text-green-800 hover:bg-green-300",
icon: <CheckIcon className="h-5 w-5" aria-hidden="true" />
},
"PENDING": {
colors: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
icon: <ClockIcon className="h-5 w-5" aria-hidden="true" />
}
"PUSH_ERROR": {
colors: "bg-pink-100 text-pink-800 hover:bg-pink-300",
icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_REJECTED": {
colors: "bg-blue-200 dark:bg-blue-100 text-blue-400 dark:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-400",
icon: <BanIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_APPROVED": {
colors: "bg-green-100 text-green-800 hover:bg-green-300",
icon: <CheckIcon className="h-5 w-5" aria-hidden="true" />
},
"PENDING": {
colors: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
icon: <ClockIcon className="h-5 w-5" aria-hidden="true" />
}
};
const GetReleaseStatusString = (releaseAction: ReleaseActionStatus) => {
@ -67,18 +65,18 @@ const GetReleaseStatusString = (releaseAction: ReleaseActionStatus) => {
};
export const ReleaseStatusCell = ({ value }: ReleaseStatusCellProps) => (
<div className="flex text-sm font-medium text-gray-900 dark:text-gray-300">
{value.map((v, idx) => (
<div
key={idx}
title={GetReleaseStatusString(v)}
className={classNames(
StatusCellMap[v.status].colors,
"mr-1 inline-flex items-center rounded text-xs font-semibold uppercase cursor-pointer"
)}
>
{StatusCellMap[v.status].icon}
</div>
))}
</div>
<div className="flex text-sm font-medium text-gray-900 dark:text-gray-300">
{value.map((v, idx) => (
<div
key={idx}
title={GetReleaseStatusString(v)}
className={classNames(
StatusCellMap[v.status].colors,
"mr-1 inline-flex items-center rounded text-xs font-semibold uppercase cursor-pointer"
)}
>
{StatusCellMap[v.status].icon}
</div>
))}
</div>
);

View file

@ -1,21 +1,19 @@
import { FC } from "react"
import { FC } from "react";
interface DebugProps {
values: unknown;
}
const DEBUG: FC<DebugProps> = ({ values }) => {
if (process.env.NODE_ENV !== "development") {
return null;
}
if (process.env.NODE_ENV !== "development") {
return null;
}
return (
<div className="w-full p-2 flex flex-col mt-6 bg-gray-100 dark:bg-gray-900">
<pre className="overflow-x-auto dark:text-gray-400">
{JSON.stringify(values, undefined, 2)}
</pre>
</div>
);
return (
<div className="w-full p-2 flex flex-col mt-12 mb-12 bg-gray-100 dark:bg-gray-900">
<pre className="dark:text-gray-400">{JSON.stringify(values, null, 2)}</pre>
</div>
);
};
export default DEBUG;

View file

@ -13,43 +13,43 @@ export const EmptySimple = ({
buttonText,
buttonAction
}: EmptySimpleProps) => (
<div className="text-center py-8">
<h3 className="mt-2 text-sm font-medium text-gray-900 dark:text-white">{title}</h3>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-200">{subtitle}</p>
{buttonText && buttonAction ? (
<div className="mt-6">
<button
type="button"
onClick={buttonAction}
className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
>
<PlusIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
{buttonText}
</button>
</div>
) : null}
</div>
)
<div className="text-center py-8">
<h3 className="mt-2 text-sm font-medium text-gray-900 dark:text-white">{title}</h3>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-200">{subtitle}</p>
{buttonText && buttonAction ? (
<div className="mt-6">
<button
type="button"
onClick={buttonAction}
className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
>
<PlusIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
{buttonText}
</button>
</div>
) : null}
</div>
);
interface EmptyListStateProps {
text: string;
buttonText?: string;
buttonOnClick?: any;
buttonOnClick?: () => void;
}
export function EmptyListState({ text, buttonText, buttonOnClick }: EmptyListStateProps) {
return (
<div className="px-4 py-12 flex flex-col items-center">
<p className="text-center text-gray-800 dark:text-white">{text}</p>
{buttonText && buttonOnClick && (
<button
type="button"
className="relative inline-flex items-center px-4 py-2 mt-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={buttonOnClick}
>
{buttonText}
</button>
)}
</div>
)
return (
<div className="px-4 py-12 flex flex-col items-center">
<p className="text-center text-gray-800 dark:text-white">{text}</p>
{buttonText && buttonOnClick && (
<button
type="button"
className="relative inline-flex items-center px-4 py-2 mt-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={buttonOnClick}
>
{buttonText}
</button>
)}
</div>
);
}

View file

@ -6,8 +6,8 @@ interface Props {
}
export const TitleSubtitle: FC<Props> = ({ title, subtitle }) => (
<div>
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">{title}</h2>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
</div>
)
<div>
<h2 className="text-lg leading-6 font-medium text-gray-900 dark:text-gray-100">{title}</h2>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{subtitle}</p>
</div>
);

View file

@ -1,17 +1,16 @@
import { Field } from "formik";
import { Field, FieldProps } from "formik";
interface ErrorFieldProps {
name: string;
classNames?: string;
subscribe?: any;
}
const ErrorField = ({ name, classNames }: ErrorFieldProps) => (
<Field name={name} subscribe={{ touched: true, error: true }}>
{({ meta: { touched, error } }: any) =>
touched && error ? <span className={classNames}>{error}</span> : null
}
</Field>
<Field name={name} subscribe={{ touched: true, error: true }}>
{({ meta: { touched, error } }: FieldProps) =>
touched && error ? <span className={classNames}>{error}</span> : null
}
</Field>
);
interface CheckboxFieldProps {
@ -21,26 +20,26 @@ interface CheckboxFieldProps {
}
const CheckboxField = ({
name,
label,
sublabel
name,
label,
sublabel
}: CheckboxFieldProps) => (
<div className="relative flex items-start">
<div className="flex items-center h-5">
<Field
id={name}
name={name}
type="checkbox"
className="focus:ring-bkue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"
/>
</div>
<div className="ml-3 text-sm">
<label htmlFor={name} className="font-medium text-gray-900 dark:text-gray-100">
{label}
</label>
<p className="text-gray-500">{sublabel}</p>
</div>
<div className="relative flex items-start">
<div className="flex items-center h-5">
<Field
id={name}
name={name}
type="checkbox"
className="focus:ring-bkue-500 h-4 w-4 text-blue-600 border-gray-300 rounded"
/>
</div>
)
<div className="ml-3 text-sm">
<label htmlFor={name} className="font-medium text-gray-900 dark:text-gray-100">
{label}
</label>
<p className="text-gray-500">{sublabel}</p>
</div>
</div>
);
export { ErrorField, CheckboxField }
export { ErrorField, CheckboxField };

View file

@ -2,6 +2,6 @@ export { ErrorField, CheckboxField } from "./common";
export { TextField, NumberField, PasswordField } from "./input";
export { NumberFieldWide, PasswordFieldWide, SwitchGroupWide, TextFieldWide } from "./input_wide";
export { RadioFieldsetWide } from "./radio";
export { MultiSelect, Select, SelectWide, DownloadClientSelect, IndexerMultiSelect} from "./select";
export { MultiSelect, Select, SelectWide, DownloadClientSelect, IndexerMultiSelect } from "./select";
export { SwitchGroup } from "./switch";

View file

@ -1,4 +1,4 @@
import { Field } from "formik";
import { Field, FieldProps } from "formik";
import { classNames } from "../../utils";
import { EyeIcon, EyeOffIcon } from "@heroicons/react/solid";
import { useToggle } from "../../hooks/hooks";
@ -16,49 +16,49 @@ interface TextFieldProps {
}
export const TextField = ({
name,
defaultValue,
label,
placeholder,
columns,
autoComplete,
hidden,
name,
defaultValue,
label,
placeholder,
columns,
autoComplete,
hidden
}: TextFieldProps) => (
<div
className={classNames(
hidden ? "hidden" : "",
columns ? `col-span-${columns}` : "col-span-12",
)}
>
{label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</label>
)}
<Field name={name}>
{({
field,
meta,
}: any) => (
<div>
<input
{...field}
id={name}
type="text"
defaultValue={defaultValue}
autoComplete={autoComplete}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md")}
placeholder={placeholder}
/>
<div
className={classNames(
hidden ? "hidden" : "",
columns ? `col-span-${columns}` : "col-span-12"
)}
>
{label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</label>
)}
<Field name={name}>
{({
field,
meta
}: FieldProps) => (
<div>
<input
{...field}
id={name}
type="text"
defaultValue={defaultValue}
autoComplete={autoComplete}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md")}
placeholder={placeholder}
/>
{meta.touched && meta.error && (
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
)}
</div>
)}
</Field>
</div>
)
{meta.touched && meta.error && (
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
)}
</div>
)}
</Field>
</div>
);
interface PasswordFieldProps {
name: string;
@ -72,60 +72,60 @@ interface PasswordFieldProps {
}
export const PasswordField = ({
name,
label,
placeholder,
defaultValue,
columns,
autoComplete,
help,
required
name,
label,
placeholder,
defaultValue,
columns,
autoComplete,
help,
required
}: PasswordFieldProps) => {
const [isVisible, toggleVisibility] = useToggle(false)
const [isVisible, toggleVisibility] = useToggle(false);
return (
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
return (
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
)}
>
{label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} {required && <span className="text-gray-500">*</span>}
</label>
)}
<Field name={name} defaultValue={defaultValue}>
{({
field,
meta
}: FieldProps) => (
<div className="sm:col-span-2 relative">
<input
{...field}
id={name}
type={isVisible ? "text" : "password"}
autoComplete={autoComplete}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md")}
placeholder={placeholder}
/>
<div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}>
{!isVisible ? <EyeIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" /> : <EyeOffIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" />}
</div>
{help && (
<p className="mt-2 text-sm text-gray-500" id="email-description">{help}</p>
)}
>
{label && (
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label} {required && <span className="text-gray-500">*</span>}
</label>
{meta.touched && meta.error && (
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
)}
<Field name={name} defaultValue={defaultValue}>
{({
field,
meta,
}: any) => (
<div className="sm:col-span-2 relative">
<input
{...field}
id={name}
type={isVisible ? "text" : "password"}
autoComplete={autoComplete}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md")}
placeholder={placeholder}
/>
<div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}>
{!isVisible ? <EyeIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" /> : <EyeOffIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" />}
</div>
{help && (
<p className="mt-2 text-sm text-gray-500" id="email-description">{help}</p>
)}
{meta.touched && meta.error && (
<p className="error text-sm text-red-600 mt-1">* {meta.error}</p>
)}
</div>
)}
</Field>
</div>
)
}
</div>
)}
</Field>
</div>
);
};
interface NumberFieldProps {
name: string;
@ -135,40 +135,40 @@ interface NumberFieldProps {
}
export const NumberField = ({
name,
label,
placeholder,
step,
name,
label,
placeholder,
step
}: NumberFieldProps) => (
<div className="col-span-12 sm:col-span-6">
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</label>
<Field name={name} type="number">
{({
field,
meta,
}: any) => (
<div className="sm:col-span-2">
<input
type="number"
step={step}
{...field}
className={classNames(
meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500"
: "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300",
"mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md"
)}
placeholder={placeholder}
/>
{meta.touched && meta.error && (
<div className="error">{meta.error}</div>
)}
</div>
<div className="col-span-12 sm:col-span-6">
<label htmlFor={name} className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</label>
<Field name={name} type="number">
{({
field,
meta
}: FieldProps) => (
<div className="sm:col-span-2">
<input
type="number"
step={step}
{...field}
className={classNames(
meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500"
: "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300",
"mt-2 block w-full dark:bg-gray-800 border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md"
)}
</Field>
</div>
placeholder={placeholder}
/>
{meta.touched && meta.error && (
<div className="error">{meta.error}</div>
)}
</div>
)}
</Field>
</div>
);

View file

@ -4,7 +4,7 @@ import { classNames } from "../../utils";
import { useToggle } from "../../hooks/hooks";
import { EyeIcon, EyeOffIcon } from "@heroicons/react/solid";
import { Switch } from "@headlessui/react";
import { ErrorField } from "./common"
import { ErrorField } from "./common";
interface TextFieldWideProps {
name: string;
@ -17,46 +17,46 @@ interface TextFieldWideProps {
}
export const TextFieldWide = ({
name,
label,
help,
placeholder,
defaultValue,
required,
hidden
name,
label,
help,
placeholder,
defaultValue,
required,
hidden
}: TextFieldWideProps) => (
<div hidden={hidden} className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
</label>
</div>
<div className="sm:col-span-2">
<Field
name={name}
value={defaultValue}
required={required}
>
{({ field, meta }: FieldProps) => (
<input
{...field}
id={name}
type="text"
value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md")}
placeholder={placeholder}
hidden={hidden}
/>
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
<div hidden={hidden} className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
</label>
</div>
)
<div className="sm:col-span-2">
<Field
name={name}
value={defaultValue}
required={required}
>
{({ field, meta }: FieldProps) => (
<input
{...field}
id={name}
type="text"
value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md")}
placeholder={placeholder}
hidden={hidden}
/>
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
</div>
);
interface PasswordFieldWideProps {
name: string;
@ -69,53 +69,53 @@ interface PasswordFieldWideProps {
}
export const PasswordFieldWide = ({
name,
label,
placeholder,
defaultValue,
help,
required,
defaultVisible
name,
label,
placeholder,
defaultValue,
help,
required,
defaultVisible
}: PasswordFieldWideProps) => {
const [isVisible, toggleVisibility] = useToggle(defaultVisible)
const [isVisible, toggleVisibility] = useToggle(defaultVisible);
return (
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
</label>
return (
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label htmlFor={name} className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2">
{label} {required && <span className="text-gray-500">*</span>}
</label>
</div>
<div className="sm:col-span-2">
<Field
name={name}
defaultValue={defaultValue}
>
{({ field, meta }: FieldProps) => (
<div className="relative">
<input
{...field}
id={name}
value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange}
type={isVisible ? "text" : "password"}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full pr-10 dark:bg-gray-800 shadow-sm dark:text-gray-100 sm:text-sm rounded-md")}
placeholder={placeholder}
/>
<div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}>
{!isVisible ? <EyeIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" /> : <EyeOffIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" />}
</div>
</div>
<div className="sm:col-span-2">
<Field
name={name}
defaultValue={defaultValue}
>
{({ field, meta }: FieldProps) => (
<div className="relative">
<input
{...field}
id={name}
value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange}
type={isVisible ? "text" : "password"}
className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700", "block w-full pr-10 dark:bg-gray-800 shadow-sm dark:text-gray-100 sm:text-sm rounded-md")}
placeholder={placeholder}
/>
<div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}>
{!isVisible ? <EyeIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" /> : <EyeOffIcon className="h-5 w-5 text-gray-400 hover:text-gray-500" aria-hidden="true" />}
</div>
</div>
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
</div>
)
}
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
</div>
);
};
interface NumberFieldWideProps {
name: string;
@ -127,50 +127,50 @@ interface NumberFieldWideProps {
}
export const NumberFieldWide = ({
name,
label,
placeholder,
help,
defaultValue,
required
name,
label,
placeholder,
help,
defaultValue,
required
}: NumberFieldWideProps) => (
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label
htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
>
{label} {required && <span className="text-gray-500">*</span>}
</label>
</div>
<div className="sm:col-span-2">
<Field
name={name}
defaultValue={defaultValue ?? 0}
>
{({ field, meta, form }: FieldProps) => (
<input
{...field}
id={name}
type="number"
value={field.value ? field.value : defaultValue ?? 0}
onChange={(e) => { form.setFieldValue(field.name, parseInt(e.target.value)) }}
className={classNames(
meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500"
: "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
"block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md"
)}
placeholder={placeholder}
/>
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500 dark:text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
<div className="px-4 space-y-1 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<div>
<label
htmlFor={name}
className="block text-sm font-medium text-gray-900 dark:text-white sm:mt-px sm:pt-2"
>
{label} {required && <span className="text-gray-500">*</span>}
</label>
</div>
<div className="sm:col-span-2">
<Field
name={name}
defaultValue={defaultValue ?? 0}
>
{({ field, meta, form }: FieldProps) => (
<input
{...field}
id={name}
type="number"
value={field.value ? field.value : defaultValue ?? 0}
onChange={(e) => { form.setFieldValue(field.name, parseInt(e.target.value)); }}
className={classNames(
meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500"
: "focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
"block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md"
)}
placeholder={placeholder}
/>
)}
</Field>
{help && (
<p className="mt-2 text-sm text-gray-500 dark:text-gray-500" id={`${name}-description`}>{help}</p>
)}
<ErrorField name={name} classNames="block text-red-500 mt-2" />
</div>
</div>
);
interface SwitchGroupWideProps {
@ -182,56 +182,56 @@ interface SwitchGroupWideProps {
}
export const SwitchGroupWide = ({
name,
label,
description,
defaultValue
name,
label,
description,
defaultValue
}: SwitchGroupWideProps) => (
<ul className="mt-2 divide-y divide-gray-200 dark:divide-gray-700">
<Switch.Group as="li" className="py-4 flex items-center justify-between">
<div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
passive>
{label}
</Switch.Label>
{description && (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
{description}
</Switch.Description>
)}
</div>
<ul className="mt-2 divide-y divide-gray-200 dark:divide-gray-700">
<Switch.Group as="li" className="py-4 flex items-center justify-between">
<div className="flex flex-col">
<Switch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-white"
passive>
{label}
</Switch.Label>
{description && (
<Switch.Description className="text-sm text-gray-500 dark:text-gray-700">
{description}
</Switch.Description>
)}
</div>
<Field
name={name}
defaultValue={defaultValue as boolean}
type="checkbox"
>
{({ field, form }: FieldProps) => (
<Switch
{...field}
type="button"
value={field.value}
checked={field.checked ?? false}
onChange={value => {
form.setFieldValue(field?.name ?? '', value)
}}
className={classNames(
field.value ? 'bg-teal-500 dark:bg-blue-500' : 'bg-gray-200 dark:bg-gray-500',
'ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
)}
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className={classNames(
field.value ? 'translate-x-5' : 'translate-x-0',
'inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
)}
/>
</Switch>
)}
</Field>
</Switch.Group>
</ul>
)
<Field
name={name}
defaultValue={defaultValue as boolean}
type="checkbox"
>
{({ field, form }: FieldProps) => (
<Switch
{...field}
type="button"
value={field.value}
checked={field.checked ?? false}
onChange={value => {
form.setFieldValue(field?.name ?? "", value);
}}
className={classNames(
field.value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-500",
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
)}
>
<span className="sr-only">Use setting</span>
<span
aria-hidden="true"
className={classNames(
field.value ? "translate-x-5" : "translate-x-0",
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
)}
/>
</Switch>
)}
</Field>
</Switch.Group>
</ul>
);

View file

@ -15,101 +15,106 @@ interface props {
options: radioFieldsetOption[];
}
interface anyObj {
[key: string]: string
}
function RadioFieldsetWide({ name, legend, options }: props) {
const {
values,
setFieldValue,
} = useFormikContext<any>();
const {
values,
setFieldValue
} = useFormikContext<anyObj>();
const onChange = (value: string) => {
setFieldValue(name, value)
}
return (
<fieldset>
<div className="space-y-2 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:px-6 sm:py-5">
<div>
<legend className="text-sm font-medium text-gray-900 dark:text-white">
{legend}
</legend>
</div>
<div className="space-y-5 sm:col-span-2">
<div className="space-y-5 sm:mt-0">
<Field name={name} type="radio">
{() => (
<RadioGroup value={values[name]} onChange={onChange}>
<RadioGroup.Label className="sr-only">
{legend}
</RadioGroup.Label>
<div className="bg-white dark:bg-gray-800 rounded-md -space-y-px">
{options.map((setting, settingIdx) => (
<RadioGroup.Option
key={setting.value}
value={setting.value}
className={({ checked }) =>
classNames(
settingIdx === 0
? "rounded-tl-md rounded-tr-md"
: "",
settingIdx === options.length - 1
? "rounded-bl-md rounded-br-md"
: "",
checked
? "bg-indigo-50 dark:bg-gray-700 border-indigo-200 dark:border-blue-600 z-10"
: "border-gray-200 dark:border-gray-700",
"relative border p-4 flex cursor-pointer focus:outline-none"
)
}
>
{({ active, checked }) => (
<Fragment>
<span
className={classNames(
checked
? "bg-indigo-600 dark:bg-blue-600 border-transparent"
: "bg-white border-gray-300 dark:border-gray-300",
active
? "ring-2 ring-offset-2 ring-indigo-500 dark:ring-blue-500"
: "",
"h-4 w-4 mt-0.5 cursor-pointer rounded-full border flex items-center justify-center"
)}
aria-hidden="true"
>
<span className="rounded-full bg-white w-1.5 h-1.5" />
</span>
<div className="ml-3 flex flex-col">
<RadioGroup.Label
as="span"
className={classNames(
checked ? "text-indigo-900 dark:text-blue-500" : "text-gray-900 dark:text-gray-300",
"block text-sm font-medium"
)}
>
{setting.label}
</RadioGroup.Label>
<RadioGroup.Description
as="span"
className={classNames(
checked ? "text-indigo-700 dark:text-blue-500" : "text-gray-500",
"block text-sm"
)}
>
{setting.description}
</RadioGroup.Description>
</div>
</Fragment>
)}
</RadioGroup.Option>
))}
</div>
</RadioGroup>
)}
</Field>
</div>
</div>
</div>
</fieldset>
);
const onChange = (value: string) => {
setFieldValue(name, value);
};
return (
<fieldset>
<div className="space-y-2 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:px-6 sm:py-5">
<div>
<legend className="text-sm font-medium text-gray-900 dark:text-white">
{legend}
</legend>
</div>
<div className="space-y-5 sm:col-span-2">
<div className="space-y-5 sm:mt-0">
<Field name={name} type="radio">
{() => (
<RadioGroup value={values[name]} onChange={onChange}>
<RadioGroup.Label className="sr-only">
{legend}
</RadioGroup.Label>
<div className="bg-white dark:bg-gray-800 rounded-md -space-y-px">
{options.map((setting, settingIdx) => (
<RadioGroup.Option
key={setting.value}
value={setting.value}
className={({ checked }) =>
classNames(
settingIdx === 0
? "rounded-tl-md rounded-tr-md"
: "",
settingIdx === options.length - 1
? "rounded-bl-md rounded-br-md"
: "",
checked
? "bg-indigo-50 dark:bg-gray-700 border-indigo-200 dark:border-blue-600 z-10"
: "border-gray-200 dark:border-gray-700",
"relative border p-4 flex cursor-pointer focus:outline-none"
)
}
>
{({ active, checked }) => (
<Fragment>
<span
className={classNames(
checked
? "bg-indigo-600 dark:bg-blue-600 border-transparent"
: "bg-white border-gray-300 dark:border-gray-300",
active
? "ring-2 ring-offset-2 ring-indigo-500 dark:ring-blue-500"
: "",
"h-4 w-4 mt-0.5 cursor-pointer rounded-full border flex items-center justify-center"
)}
aria-hidden="true"
>
<span className="rounded-full bg-white w-1.5 h-1.5" />
</span>
<div className="ml-3 flex flex-col">
<RadioGroup.Label
as="span"
className={classNames(
checked ? "text-indigo-900 dark:text-blue-500" : "text-gray-900 dark:text-gray-300",
"block text-sm font-medium"
)}
>
{setting.label}
</RadioGroup.Label>
<RadioGroup.Description
as="span"
className={classNames(
checked ? "text-indigo-700 dark:text-blue-500" : "text-gray-500",
"block text-sm"
)}
>
{setting.description}
</RadioGroup.Description>
</div>
</Fragment>
)}
</RadioGroup.Option>
))}
</div>
</RadioGroup>
)}
</Field>
</div>
</div>
</div>
</fieldset>
);
}
export { RadioFieldsetWide };

View file

@ -1,5 +1,5 @@
import { Fragment } from "react";
import { Field } from "formik";
import { Field, FieldProps } from "formik";
import { Transition, Listbox } from "@headlessui/react";
import { CheckIcon, SelectorIcon } from "@heroicons/react/solid";
import { MultiSelect as RMSC } from "react-multi-select-component";
@ -7,114 +7,125 @@ import { MultiSelect as RMSC } from "react-multi-select-component";
import { classNames, COL_WIDTHS } from "../../utils";
import { SettingsContext } from "../../utils/Context";
export interface MultiSelectOption {
value: string | number;
label: string;
key?: string;
disabled?: boolean;
}
interface MultiSelectProps {
name: string;
label?: string;
options?: [] | any;
options: MultiSelectOption[];
columns?: COL_WIDTHS;
creatable?: boolean;
}
export const MultiSelect = ({
name,
label,
options,
columns,
creatable,
name,
label,
options,
columns,
creatable
}: MultiSelectProps) => {
const settingsContext = SettingsContext.useValue();
const settingsContext = SettingsContext.useValue();
const handleNewField = (value: string) => ({
value: value.toUpperCase(),
label: value.toUpperCase(),
key: value,
});
const handleNewField = (value: string) => ({
value: value.toUpperCase(),
label: value.toUpperCase(),
key: value
});
return (
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
)}
>
<label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
htmlFor={label}
>
{label}
</label>
return (
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
)}
>
<label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
htmlFor={label}
>
{label}
</label>
<Field name={name} type="select" multiple={true}>
{({
field,
form: { setFieldValue },
}: any) => (
<RMSC
{...field}
type="select"
options={[...[...options, ...field.value.map((i: any) => ({ value: i.value ?? i, label: i.label ?? i}))].reduce((map, obj) => map.set(obj.value, obj), new Map()).values()]}
labelledBy={name}
isCreatable={creatable}
onCreateOption={handleNewField}
value={field.value && field.value.map((item: any) => ({
value: item.value ? item.value : item,
label: item.label ? item.label : item,
}))}
onChange={(values: any) => {
const am = values && values.map((i: any) => i.value);
<Field name={name} type="select" multiple={true}>
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<RMSC
{...field}
options={[...[...options, ...field.value.map((i: MultiSelectOption) => ({ value: i.value ?? i, label: i.label ?? i }))].reduce((map, obj) => map.set(obj.value, obj), new Map()).values()]}
labelledBy={name}
isCreatable={creatable}
onCreateOption={handleNewField}
value={field.value && field.value.map((item: MultiSelectOption) => ({
value: item.value ? item.value : item,
label: item.label ? item.label : item
}))}
onChange={(values: Array<MultiSelectOption>) => {
const am = values && values.map((i) => i.value);
setFieldValue(field.name, am);
}}
className={settingsContext.darkTheme ? "dark" : ""}
/>
)}
</Field>
</div>
);
setFieldValue(field.name, am);
}}
className={settingsContext.darkTheme ? "dark" : ""}
/>
)}
</Field>
</div>
);
};
interface IndexerMultiSelectOption {
id: number;
name: string;
}
export const IndexerMultiSelect = ({
name,
label,
options,
columns,
name,
label,
options,
columns
}: MultiSelectProps) => {
const settingsContext = SettingsContext.useValue();
return (
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
)}
<div
className={classNames(
columns ? `col-span-${columns}` : "col-span-12"
)}
>
<label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
htmlFor={label}
>
<label
className="block mb-2 text-xs font-bold tracking-wide text-gray-700 uppercase dark:text-gray-200"
htmlFor={label}
>
{label}
</label>
{label}
</label>
<Field name={name} type="select" multiple={true}>
{({
field,
form: { setFieldValue },
}: any) => (
<RMSC
{...field}
type="select"
options={options}
labelledBy={name}
value={field.value && field.value.map((item: any) => options.find((o: any) => o.value?.id === item.id))}
onChange={(values: any) => {
const am = values && values.map((i: any) => i.value);
setFieldValue(field.name, am);
}}
className={settingsContext.darkTheme ? "dark" : ""}
itemHeight={50}
/>
)}
</Field>
</div>
<Field name={name} type="select" multiple={true}>
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<RMSC
{...field}
options={options}
labelledBy={name}
value={field.value && field.value.map((item: IndexerMultiSelectOption) => ({
value: item.id, label: item.name
}))}
onChange={(values: MultiSelectOption[]) => {
const item = values && values.map((i) => ({ id: i.value, name: i.label }));
setFieldValue(field.name, item);
}}
className={settingsContext.darkTheme ? "dark" : ""}
/>
)}
</Field>
</div>
);
}
};
interface DownloadClientSelectProps {
name: string;
@ -123,102 +134,102 @@ interface DownloadClientSelectProps {
}
export function DownloadClientSelect({
name,
action,
clients
name,
action,
clients
}: DownloadClientSelectProps) {
return (
<div className="col-span-6 sm:col-span-6">
<Field name={name} type="select">
{({
field,
form: { setFieldValue },
}: any) => (
<Listbox
value={field.value}
onChange={(value: any) => setFieldValue(field?.name, value)}
>
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
return (
<div className="col-span-6 sm:col-span-6">
<Field name={name} type="select">
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<Listbox
value={field.value}
onChange={(value) => setFieldValue(field?.name, value)}
>
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
Client
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? clients.find((c) => c.id === field.value)!.name
: "Choose a client"}
</span>
{/*<span className="block truncate">Choose a client</span>*/}
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true" />
</span>
</Listbox.Button>
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? clients.find((c) => c.id === field.value)?.name
: "Choose a client"}
</span>
{/*<span className="block truncate">Choose a client</span>*/}
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true" />
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{clients
.filter((c) => c.type === action.type)
.map((client: any) => (
<Listbox.Option
key={client.id}
className={({ active }) => classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)}
value={client.id}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{client.name}
</span>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{clients
.filter((c) => c.type === action.type)
.map((client) => (
<Listbox.Option
key={client.id}
className={({ active }) => classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)}
value={client.id}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{client.name}
</span>
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)}
</Field>
</div>
);
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)}
</Field>
</div>
);
}
interface SelectFieldOption {
@ -234,209 +245,209 @@ interface SelectFieldProps {
}
export const Select = ({
name,
label,
optionDefaultText,
options
name,
label,
optionDefaultText,
options
}: SelectFieldProps) => {
return (
<div className="col-span-6">
<Field name={name} type="select">
{({
field,
form: { setFieldValue },
}: any) => (
<Listbox
value={field.value}
onChange={(value: any) => setFieldValue(field?.name, value)}
return (
<div className="col-span-6">
<Field name={name} type="select">
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<Listbox
value={field.value}
onChange={(value) => setFieldValue(field?.name, value)}
>
{({ open }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)?.label
: optionDefaultText
}
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{({ open }) => (
{options.map((opt) => (
<Listbox.Option
key={opt.value}
className={({ active }) =>
classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)
}
value={opt.value}
>
{({ selected, active }) => (
<>
<Listbox.Label className="block text-xs font-bold text-gray-700 dark:text-gray-200 uppercase tracking-wide">
{label}
</Listbox.Label>
<div className="mt-2 relative">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)!.label
: optionDefaultText
}
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{opt.label}
</span>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{options.map((opt) => (
<Listbox.Option
key={opt.value}
className={({ active }) =>
classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)
}
value={opt.value}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{opt.label}
</span>
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox>
)}
</Field>
</div>
);
}
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
)}
</Field>
</div>
);
};
export const SelectWide = ({
name,
label,
optionDefaultText,
options
name,
label,
optionDefaultText,
options
}: SelectFieldProps) => {
return (
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
return (
<div className="py-6 px-6 space-y-6 sm:py-0 sm:space-y-0 sm:divide-y sm:divide-gray-200">
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<Field name={name} type="select">
{({
field,
form: { setFieldValue },
}: any) => (
<Listbox
value={field.value}
onChange={(value: any) => setFieldValue(field?.name, value)}
<div className="space-y-1 px-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:py-5">
<Field name={name} type="select">
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<Listbox
value={field.value}
onChange={(value) => setFieldValue(field?.name, value)}
>
{({ open }) => (
<div className="py-4 flex items-center justify-between">
<Listbox.Label className="block text-sm font-medium text-gray-900 dark:text-white">
{label}
</Listbox.Label>
<div className="w-full">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)?.label
: optionDefaultText
}
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
{({ open }) => (
<div className="py-4 flex items-center justify-between">
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{options.map((opt) => (
<Listbox.Option
key={opt.value}
className={({ active }) =>
classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)
}
value={opt.value}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{opt.label}
</span>
<Listbox.Label className="block text-sm font-medium text-gray-900 dark:text-white">
{label}
</Listbox.Label>
<div className="w-full">
<Listbox.Button className="bg-white dark:bg-gray-800 relative w-full border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate">
{field.value
? options.find((c) => c.value === field.value)!.label
: optionDefaultText
}
</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<SelectorIcon
className="h-5 w-5 text-gray-400 dark:text-gray-300"
aria-hidden="true"
/>
</span>
</Listbox.Button>
<Transition
show={open}
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options
static
className="absolute z-10 mt-1 w-full bg-white dark:bg-gray-800 shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
{options.map((opt) => (
<Listbox.Option
key={opt.value}
className={({ active }) =>
classNames(
active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9"
)
}
value={opt.value}
>
{({ selected, active }) => (
<>
<span
className={classNames(
selected ? "font-semibold" : "font-normal",
"block truncate"
)}
>
{opt.label}
</span>
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</div>
)}
</Listbox>
)}
</Field>
</div>
</div>
);
}
{selected ? (
<span
className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4"
)}
>
<CheckIcon
className="h-5 w-5"
aria-hidden="true"
/>
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</Listbox.Options>
</Transition>
</div>
</div>
)}
</Listbox>
)}
</Field>
</div>
</div>
);
};

View file

@ -1,69 +1,73 @@
import React from "react";
import { Field } from "formik";
import type {
FieldInputProps,
FieldMetaProps,
FieldProps,
FormikProps,
FormikValues
FieldInputProps,
FieldMetaProps,
FieldProps,
FormikProps,
FormikValues
} from "formik";
import { Switch as HeadlessSwitch } from "@headlessui/react";
import { classNames } from "../../utils";
type SwitchProps<V = any> = {
label: string
type SwitchProps<V = unknown> = {
label?: string
checked: boolean
value: boolean
disabled?: boolean
onChange: (value: boolean) => void
field?: FieldInputProps<V>
form?: FormikProps<FormikValues>
meta?: FieldMetaProps<V>
}
children: React.ReactNode
className: string
};
export const Switch = ({
label,
checked: $checked,
disabled = false,
onChange: $onChange,
field,
form,
label,
checked: $checked,
disabled = false,
onChange: $onChange,
field,
form
}: SwitchProps) => {
const checked = field?.checked ?? $checked
const checked = field?.checked ?? $checked;
return (
<HeadlessSwitch.Group as="div" className="flex items-center space-x-4">
<HeadlessSwitch.Label>{label}</HeadlessSwitch.Label>
<HeadlessSwitch
as="button"
name={field?.name}
disabled={disabled}
checked={checked}
onChange={value => {
form?.setFieldValue(field?.name ?? '', value)
$onChange && $onChange(value)
}}
return (
<HeadlessSwitch.Group as="div" className="flex items-center space-x-4">
<HeadlessSwitch.Label>{label}</HeadlessSwitch.Label>
<HeadlessSwitch
as="button"
name={field?.name}
disabled={disabled}
checked={checked}
onChange={value => {
form?.setFieldValue(field?.name ?? "", value);
$onChange && $onChange(value);
}}
className={classNames(
checked ? 'bg-teal-500 dark:bg-blue-500' : 'bg-gray-200 dark:bg-gray-600',
'ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
)}
>
{({ checked }) => (
<span
aria-hidden="true"
className={classNames(
checked ? 'translate-x-5' : 'translate-x-0',
'inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
)}
/>
)}
</HeadlessSwitch>
</HeadlessSwitch.Group>
)
}
className={classNames(
checked ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
)}
>
{({ checked }) => (
<span
aria-hidden="true"
className={classNames(
checked ? "translate-x-5" : "translate-x-0",
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
)}
/>
)}
</HeadlessSwitch>
</HeadlessSwitch.Group>
);
};
export type SwitchFormikProps = SwitchProps & FieldProps & React.InputHTMLAttributes<HTMLInputElement>;
export const SwitchFormik = (props: SwitchProps) => <Switch {...props} />
export const SwitchFormik = (props: SwitchProps) => <Switch {...props} children={props.children}/>;
interface SwitchGroupProps {
name: string;
@ -73,54 +77,54 @@ interface SwitchGroupProps {
}
const SwitchGroup = ({
name,
label,
description
name,
label,
description
}: SwitchGroupProps) => (
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col">
<HeadlessSwitch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-gray-100"
passive>
{label}
</HeadlessSwitch.Label>
{description && (
<HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400">
{description}
</HeadlessSwitch.Description>
)}
</div>
}
<Field name={name} type="checkbox">
{({
field,
form: { setFieldValue },
}: any) => (
<Switch
{...field}
type="button"
value={field.value}
checked={field.checked}
onChange={value => {
setFieldValue(field?.name ?? '', value)
}}
className={classNames(
field.value ? 'bg-teal-500 dark:bg-blue-500' : 'bg-gray-200',
'ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500'
)}
>
<span
aria-hidden="true"
className={classNames(
field.value ? 'translate-x-5' : 'translate-x-0',
'inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200'
)}
/>
</Switch>
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col">
<HeadlessSwitch.Label as="p" className="text-sm font-medium text-gray-900 dark:text-gray-100"
passive>
{label}
</HeadlessSwitch.Label>
{description && (
<HeadlessSwitch.Description className="text-sm mt-1 text-gray-500 dark:text-gray-400">
{description}
</HeadlessSwitch.Description>
)}
</div>
}
<Field name={name} type="checkbox">
{({
field,
form: { setFieldValue }
}: FieldProps) => (
<Switch
{...field}
// type="button"
value={field.value}
checked={field.checked ?? false}
onChange={value => {
setFieldValue(field?.name ?? "", value);
}}
className={classNames(
field.value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200",
"ml-4 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
)}
>
<span
aria-hidden="true"
className={classNames(
field.value ? "translate-x-5" : "translate-x-0",
"inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
)}
</Field>
</HeadlessSwitch.Group>
/>
</Switch>
)}
</Field>
</HeadlessSwitch.Group>
);
export { SwitchGroup }
export { SwitchGroup };

View file

@ -1,92 +1,92 @@
import { Fragment, FC } from "react";
import React, { Fragment, FC } from "react";
import { Dialog, Transition } from "@headlessui/react";
import { ExclamationIcon } from "@heroicons/react/solid";
interface DeleteModalProps {
isOpen: boolean;
buttonRef: any;
toggle: any;
deleteAction: any;
buttonRef: React.MutableRefObject<HTMLElement | null> | undefined;
toggle: () => void;
deleteAction: () => void;
title: string;
text: string;
}
export const DeleteModal: FC<DeleteModalProps> = ({ isOpen, buttonRef, toggle, deleteAction, title, text }) => (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog
as="div"
static
className="fixed z-10 inset-0 overflow-y-auto"
initialFocus={buttonRef}
open={isOpen}
onClose={toggle}
<Transition.Root show={isOpen} as={Fragment}>
<Dialog
as="div"
static
className="fixed z-10 inset-0 overflow-y-auto"
initialFocus={buttonRef}
open={isOpen}
onClose={toggle}
>
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="fixed inset-0 bg-gray-700/60 dark:bg-black/60 transition-opacity" />
</Transition.Child>
<Dialog.Overlay className="fixed inset-0 bg-gray-700/60 dark:bg-black/60 transition-opacity" />
</Transition.Child>
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
&#8203;
</span>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div className="inline-block align-bottom rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div className="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<ExclamationIcon className="h-16 w-16 text-red-500 dark:text-red-500" aria-hidden="true" />
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title as="h3" className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
{title}
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500 dark:text-gray-300">
{text}
</p>
</div>
</div>
</div>
</div>
<div className="bg-gray-50 dark:bg-gray-800 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
type="button"
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => {
if (isOpen) {
deleteAction();
toggle();
}
}}
>
Remove
</button>
<button
type="button"
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-700 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
onClick={toggle}
ref={buttonRef}
>
Cancel
</button>
</div>
</div>
</Transition.Child>
</span>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div className="inline-block align-bottom rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div className="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div className="sm:flex sm:items-start">
<ExclamationIcon className="h-16 w-16 text-red-500 dark:text-red-500" aria-hidden="true" />
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<Dialog.Title as="h3" className="text-lg leading-6 font-medium text-gray-900 dark:text-white">
{title}
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500 dark:text-gray-300">
{text}
</p>
</div>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
)
<div className="bg-gray-50 dark:bg-gray-800 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
type="button"
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"
onClick={() => {
if (isOpen) {
deleteAction();
toggle();
}
}}
>
Remove
</button>
<button
type="button"
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-700 text-base font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
onClick={toggle}
// ref={buttonRef}
>
Cancel
</button>
</div>
</div>
</Transition.Child>
</div>
</Dialog>
</Transition.Root>
);

View file

@ -1,30 +1,30 @@
import { FC } from 'react'
import { XIcon, CheckCircleIcon, ExclamationIcon, ExclamationCircleIcon } from '@heroicons/react/solid'
import { toast } from 'react-hot-toast'
import { classNames } from '../../utils'
import { FC } from "react";
import { XIcon, CheckCircleIcon, ExclamationIcon, ExclamationCircleIcon } from "@heroicons/react/solid";
import { toast, Toast as Tooast } from "react-hot-toast";
import { classNames } from "../../utils";
type Props = {
type: 'error' | 'success' | 'warning'
type: "error" | "success" | "warning"
body?: string
t?: any;
}
t?: Tooast;
};
const Toast: FC<Props> = ({ type, body, t }) => (
<div className={classNames(
t.visible ? 'animate-enter' : 'animate-leave',
t?.visible ? "animate-enter" : "animate-leave",
"max-w-sm w-full bg-white dark:bg-gray-800 shadow-lg rounded-lg pointer-events-auto ring-1 ring-black ring-opacity-5 overflow-hidden transition-all")}>
<div className="p-4">
<div className="flex items-start">
<div className="flex-shrink-0">
{type === 'success' && <CheckCircleIcon className="h-6 w-6 text-green-400" aria-hidden="true" />}
{type === 'error' && <ExclamationCircleIcon className="h-6 w-6 text-red-400" aria-hidden="true" />}
{type === 'warning' && <ExclamationIcon className="h-6 w-6 text-yellow-400" aria-hidden="true" />}
{type === "success" && <CheckCircleIcon className="h-6 w-6 text-green-400" aria-hidden="true" />}
{type === "error" && <ExclamationCircleIcon className="h-6 w-6 text-red-400" aria-hidden="true" />}
{type === "warning" && <ExclamationIcon className="h-6 w-6 text-yellow-400" aria-hidden="true" />}
</div>
<div className="ml-3 w-0 flex-1 pt-0.5">
<p className="text-sm font-medium text-gray-900 dark:text-gray-200">
{type === 'success' && "Success"}
{type === 'error' && "Error"}
{type === 'warning' && "Warning"}
{type === "success" && "Success"}
{type === "error" && "Error"}
{type === "warning" && "Warning"}
</p>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">{body}</p>
</div>
@ -32,7 +32,7 @@ const Toast: FC<Props> = ({ type, body, t }) => (
<button
className="bg-white dark:bg-gray-700 rounded-md inline-flex text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={() => {
toast.dismiss(t.id)
toast.dismiss(t?.id);
}}
>
<span className="sr-only">Close</span>
@ -42,6 +42,6 @@ const Toast: FC<Props> = ({ type, body, t }) => (
</div>
</div>
</div>
)
);
export default Toast;

View file

@ -1,4 +1,4 @@
import { Fragment, useRef } from "react";
import React, { Fragment, useRef } from "react";
import { XIcon } from "@heroicons/react/solid";
import { Dialog, Transition } from "@headlessui/react";
import { Form, Formik } from "formik";
@ -10,7 +10,7 @@ import { classNames } from "../../utils";
interface SlideOverProps<DataType> {
title: string;
initialValues: DataType;
validate?: (values?: any) => void;
validate?: (values: DataType) => void;
onSubmit: (values?: DataType) => void;
isOpen: boolean;
toggle: () => void;
@ -30,117 +30,117 @@ function SlideOver<DataType>({
type,
children
}: SlideOverProps<DataType>): React.ReactElement {
const cancelModalButtonRef = useRef(null);
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
const cancelModalButtonRef = useRef<HTMLInputElement | null>(null);
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" static className="fixed inset-0 overflow-hidden" open={isOpen} onClose={toggle}>
{deleteAction && (
<DeleteModal
isOpen={deleteModalIsOpen}
toggle={toggleDeleteModal}
buttonRef={cancelModalButtonRef}
deleteAction={deleteAction}
title={`Remove ${title}`}
text={`Are you sure you want to remove this ${title}? This action cannot be undone.`}
/>
)}
return (
<Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" static className="fixed inset-0 overflow-hidden" open={isOpen} onClose={toggle}>
{deleteAction && (
<DeleteModal
isOpen={deleteModalIsOpen}
toggle={toggleDeleteModal}
buttonRef={cancelModalButtonRef}
deleteAction={deleteAction}
title={`Remove ${title}`}
text={`Are you sure you want to remove this ${title}? This action cannot be undone.`}
/>
)}
<div className="absolute inset-0 overflow-hidden">
<Dialog.Overlay className="absolute inset-0" />
<div className="absolute inset-0 overflow-hidden">
<Dialog.Overlay className="absolute inset-0" />
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<div className="w-screen max-w-2xl dark:border-gray-700 border-l">
<div className="fixed inset-y-0 right-0 pl-10 max-w-full flex sm:pl-16">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-700"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-700"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<div className="w-screen max-w-2xl dark:border-gray-700 border-l">
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validate={validate}
>
{({ handleSubmit, values }) => (
<Form className="h-full flex flex-col bg-white dark:bg-gray-800 shadow-xl overflow-y-scroll"
onSubmit={handleSubmit}>
<div className="flex-1">
<div className="px-4 py-6 bg-gray-50 dark:bg-gray-900 sm:px-6">
<div className="flex items-start justify-between space-x-3">
<div className="space-y-1">
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">{type === "CREATE" ? "Create" : "Update"} {title}</Dialog.Title>
<p className="text-sm text-gray-500 dark:text-gray-400">
{type === "CREATE" ? "Create" : "Update"} {title}.
</p>
</div>
<div className="h-7 flex items-center">
<button
type="button"
className="bg-white dark:bg-gray-900 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={toggle}
>
<span className="sr-only">Close panel</span>
<XIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
{!!values && children !== undefined ? (
children(values)
) : null}
</div>
<div className="flex-shrink-0 px-4 border-t border-gray-200 dark:border-gray-700 py-5 sm:px-6">
<div className={classNames(type === "CREATE" ? "justify-end" : "justify-between", "space-x-3 flex")}>
{type === "UPDATE" && (
<button
type="button"
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 dark:text-white bg-red-100 dark:bg-red-700 hover:bg-red-200 dark:hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm"
onClick={toggleDeleteModal}
>
Remove
</button>
)}
<div>
<button
type="button"
className="bg-white dark:bg-gray-700 py-2 px-4 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={toggle}
>
Cancel
</button>
<button
type="submit"
className="ml-4 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
{type === "CREATE" ? "Create" : "Save"}
</button>
</div>
</div>
</div>
<DEBUG values={values} />
</Form>
)}
</Formik>
<Formik
initialValues={initialValues}
onSubmit={onSubmit}
validate={validate}
>
{({ handleSubmit, values }) => (
<Form className="h-full flex flex-col bg-white dark:bg-gray-800 shadow-xl overflow-y-scroll"
onSubmit={handleSubmit}>
<div className="flex-1">
<div className="px-4 py-6 bg-gray-50 dark:bg-gray-900 sm:px-6">
<div className="flex items-start justify-between space-x-3">
<div className="space-y-1">
<Dialog.Title className="text-lg font-medium text-gray-900 dark:text-white">{type === "CREATE" ? "Create" : "Update"} {title}</Dialog.Title>
<p className="text-sm text-gray-500 dark:text-gray-400">
{type === "CREATE" ? "Create" : "Update"} {title}.
</p>
</div>
<div className="h-7 flex items-center">
<button
type="button"
className="bg-white dark:bg-gray-900 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={toggle}
>
<span className="sr-only">Close panel</span>
<XIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
)
{!!values && children !== undefined ? (
children(values)
) : null}
</div>
<div className="flex-shrink-0 px-4 border-t border-gray-200 dark:border-gray-700 py-5 sm:px-6">
<div className={classNames(type === "CREATE" ? "justify-end" : "justify-between", "space-x-3 flex")}>
{type === "UPDATE" && (
<button
type="button"
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 dark:text-white bg-red-100 dark:bg-red-700 hover:bg-red-200 dark:hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm"
onClick={toggleDeleteModal}
>
Remove
</button>
)}
<div>
<button
type="button"
className="bg-white dark:bg-gray-700 py-2 px-4 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500"
onClick={toggle}
>
Cancel
</button>
<button
type="submit"
className="ml-4 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 dark:bg-blue-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
{type === "CREATE" ? "Create" : "Save"}
</button>
</div>
</div>
</div>
<DEBUG values={values} />
</Form>
)}
</Formik>
</div>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
}
export { SlideOver };