feat(web): better tooltips and uniformity (#474)

* - fix: changed all {text,bg}-{teal,indigo}-* color properties to {text,bg}-{blue}-* so there is only one dominant primary color in the UI -- blue
- enhancement: added `cursor: pointer` to the PageButton component (used in pagination)
- enhancement: modified TitleCell to use the new Tooltip component and modified the width selectors to a more sane default value, now instead of scrolling one just has to tap the cell to see it's untruncated value
- enhancement: modified the Tooltip component to use react-popper-tooltip (which in turn uses popper.js) which is a much better alternative, since it uses auto-positioning in case there's not enough space and some things aren't as broken as in the previous version (e.g. there was a problem with forcing the previous tooltip to use a specific width)
- enhancement: added a useMedia hook selector from the react-use repository, which might come in handy in the future for better/easier decoupling of Desktop and Mobile UI/UX patterns via JS (versus CSS-only)
- enhancement: made the mobile navbar more visible and clear. also fixed previous path === "/" bug which was fixed on desktop.
- fix: fixed table headers/footers so they're rounded
- enhancement: made pagination components more compact (buttons and show N result selector)

* changed {ring, border}-indigo-* to ring-blue-*

* build: add yarn.lock

* fix: formatting warnings

* fix: formatting warnings

* fix: more formatting warnings

* fix: more formatting
This commit is contained in:
stacksmash76 2022-10-14 21:54:21 +02:00 committed by GitHub
parent 71d0424b61
commit ac988f28f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 531 additions and 318 deletions

View file

@ -20,6 +20,7 @@
"react-hook-form": "^7.32.1", "react-hook-form": "^7.32.1",
"react-hot-toast": "^2.2.0", "react-hot-toast": "^2.2.0",
"react-multi-select-component": "^4.2.9", "react-multi-select-component": "^4.2.9",
"react-popper-tooltip": "^4.4.2",
"react-query": "^3.39.1", "react-query": "^3.39.1",
"react-ridge-state": "4.2.2", "react-ridge-state": "4.2.2",
"react-router-dom": "^6.3.0", "react-router-dom": "^6.3.0",

View file

@ -23,7 +23,7 @@ export const Checkbox = ({ label, description, value, setValue }: CheckboxProps)
checked={value} checked={value}
onChange={setValue} onChange={setValue}
className={ className={
`${value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-700" `${value ? "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`} } 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 <span

View file

@ -27,7 +27,7 @@ export const PageButton = ({ children, className, disabled, onClick }: ButtonPro
type="button" type="button"
className={classNames( className={classNames(
className ?? "", 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" "cursor-pointer inline-flex items-center p-1.5 border border-gray-300 dark:border-gray-700 text-sm font-medium text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600"
)} )}
disabled={disabled} disabled={disabled}
onClick={onClick} onClick={onClick}

View file

@ -18,10 +18,19 @@ export const AgeCell = ({ value }: CellProps) => (
export const TitleCell = ({ value }: CellProps) => ( export const TitleCell = ({ value }: CellProps) => (
<div <div
className="text-sm font-medium box-content text-gray-900 dark:text-gray-300 max-w-[128px] sm:max-w-[256px] md:max-w-[360px] lg:max-w-[640px] xl:max-w-[840px] overflow-auto py-4" className={classNames(
title={value} "py-3 text-sm font-medium box-content text-gray-900 dark:text-gray-300",
"max-w-[96px] sm:max-w-[216px] md:max-w-[360px] lg:max-w-[640px] xl:max-w-[840px]"
)}
> >
{value} <Tooltip
label={value}
maxWidth="max-w-[90vw]"
>
<span className="whitespace-pre-wrap break-word">
{value}
</span>
</Tooltip>
</div> </div>
); );
@ -32,27 +41,87 @@ interface ReleaseStatusCellProps {
interface StatusCellMapEntry { interface StatusCellMapEntry {
colors: string; colors: string;
icon: React.ReactElement; icon: React.ReactElement;
textFormatter: (text: string) => React.ReactElement;
} }
const StatusCellMap: Record<string, StatusCellMapEntry> = { const StatusCellMap: Record<string, StatusCellMapEntry> = {
"PUSH_ERROR": { "PUSH_ERROR": {
colors: "bg-pink-100 text-pink-800 hover:bg-pink-300", colors: "bg-pink-100 text-pink-800 hover:bg-pink-300",
icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" /> icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />,
textFormatter: (text: string) => (
<>
Action
{" "}
<span className="font-bold underline underline-offset-2 decoration-2 decoration-red-500">
error
</span>
{": "}
{text}
</>
)
}, },
"PUSH_REJECTED": { "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", colors: "bg-blue-100 dark:bg-blue-100 text-blue-400 dark:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-400",
icon: <NoSymbolIcon className="h-5 w-5" aria-hidden="true" /> icon: <NoSymbolIcon className="h-5 w-5" aria-hidden="true" />,
textFormatter: (text: string) => (
<>
Action
{" "}
<span
className="font-bold underline underline-offset-2 decoration-2 decoration-sky-500"
>
rejected
</span>
{": "}
{text}
</>
)
}, },
"PUSH_APPROVED": { "PUSH_APPROVED": {
colors: "bg-green-100 text-green-800 hover:bg-green-300", colors: "bg-green-100 text-green-800 hover:bg-green-300",
icon: <CheckIcon className="h-5 w-5" aria-hidden="true" /> icon: <CheckIcon className="h-5 w-5" aria-hidden="true" />,
textFormatter: (text: string) => (
<>
Action
{" "}
<span className="font-bold underline underline-offset-2 decoration-2 decoration-green-500">
approved
</span>
{": "}
{text}
</>
)
}, },
"PENDING": { "PENDING": {
colors: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200", colors: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
icon: <ClockIcon className="h-5 w-5" aria-hidden="true" /> icon: <ClockIcon className="h-5 w-5" aria-hidden="true" />,
textFormatter: (text: string) => (
<>
Action
{" "}
<span className="font-bold underline underline-offset-2 decoration-2 decoration-yellow-500">
pending
</span>
{": "}
{text}
</>
)
} }
}; };
const CellLine = ({ title, children }: { title: string; children?: string; }) => {
if (!children)
return null;
return (
<div className="mt-1">
<span className="font-bold">{title}</span>
{": "}
<span className="whitespace-pre-wrap break-word leading-4">{children}</span>
</div>
);
};
export const ReleaseStatusCell = ({ value }: ReleaseStatusCellProps) => ( export const ReleaseStatusCell = ({ value }: ReleaseStatusCellProps) => (
<div className="flex text-sm font-medium text-gray-900 dark:text-gray-300"> <div className="flex text-sm font-medium text-gray-900 dark:text-gray-300">
{value.map((v, idx) => ( {value.map((v, idx) => (
@ -60,27 +129,24 @@ export const ReleaseStatusCell = ({ value }: ReleaseStatusCellProps) => (
key={idx} key={idx}
className={classNames( className={classNames(
StatusCellMap[v.status].colors, StatusCellMap[v.status].colors,
"mr-1 inline-flex items-center rounded text-xs font-semibold cursor-pointer" "mr-1 inline-flex items-center rounded text-xs cursor-pointer"
)} )}
> >
<Tooltip button={StatusCellMap[v.status].icon}> <Tooltip
<ol className="flex flex-col max-w-sm"> label={StatusCellMap[v.status].icon}
<li className="py-1">Status: {v.status}</li> title={StatusCellMap[v.status].textFormatter(v.action)}
<li className="py-1">Action: {v.action}</li> >
<li className="py-1">Type: {v.type}</li> <div className="mb-1">
{v.client && <li className="py-1">Client: {v.client}</li>} <CellLine title="Type">{v.type}</CellLine>
{v.filter && <li className="py-1">Filter: {v.filter}</li>} <CellLine title="Client">{v.client}</CellLine>
<li className="py-1">Time: {simplifyDate(v.timestamp)}</li> <CellLine title="Filter">{v.filter}</CellLine>
<CellLine title="Time">{simplifyDate(v.timestamp)}</CellLine>
{v.rejections.length ? ( {v.rejections.length ? (
<li className="py-1"> <CellLine title="Filter">
Rejections: {v.rejections.toString()}
{" "} </CellLine>
<p className="whitespace-pre-wrap break-all">
{v.rejections.toString()}
</p>
</li>
) : null} ) : null}
</ol> </div>
</Tooltip> </Tooltip>
</div> </div>
))} ))}

View file

@ -21,7 +21,7 @@ export const EmptySimple = ({
<button <button
type="button" type="button"
onClick={buttonAction} 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" className="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
<PlusIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" /> <PlusIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
{buttonText} {buttonText}
@ -44,7 +44,7 @@ export function EmptyListState({ text, buttonText, buttonOnClick }: EmptyListSta
{buttonText && buttonOnClick && ( {buttonText && buttonOnClick && (
<button <button
type="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" 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-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={buttonOnClick} onClick={buttonOnClick}
> >
{buttonText} {buttonText}

View file

@ -43,7 +43,7 @@ export const KeyField = ({ value }: KeyFieldProps) => {
type={isVisible ? "text" : "password"} type={isVisible ? "text" : "password"}
value={value} value={value}
readOnly={true} readOnly={true}
className="focus:outline-none dark:focus:border-blue-500 focus:border-indigo-500 dark:focus:ring-blue-500 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100" className="focus:outline-none dark:focus:border-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
/> />
</div> </div>
<button <button

View file

@ -50,7 +50,7 @@ export const TextField = ({
defaultValue={defaultValue} defaultValue={defaultValue}
autoComplete={autoComplete} autoComplete={autoComplete}
className={classNames( 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", meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800", disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
"mt-2 block w-full dark:text-gray-100 rounded-md" "mt-2 block w-full dark:text-gray-100 rounded-md"
)} )}
@ -114,7 +114,7 @@ export const TextArea = ({
defaultValue={defaultValue} defaultValue={defaultValue}
autoComplete={autoComplete} autoComplete={autoComplete}
className={classNames( 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", meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800", disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800",
"mt-2 block w-full dark:text-gray-100 rounded-md" "mt-2 block w-full dark:text-gray-100 rounded-md"
)} )}
@ -176,7 +176,7 @@ export const PasswordField = ({
id={name} id={name}
type={isVisible ? "text" : "password"} type={isVisible ? "text" : "password"}
autoComplete={autoComplete} 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")} className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-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} placeholder={placeholder}
/> />
@ -231,7 +231,7 @@ export const NumberField = ({
className={classNames( className={classNames(
meta.touched && meta.error meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500" ? "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", : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300",
"mt-2 block w-full border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md", "mt-2 block w-full border border-gray-300 dark:border-gray-700 dark:text-gray-100 rounded-md",
disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800" disabled ? "bg-gray-100 dark:bg-gray-700 cursor-not-allowed" : "dark:bg-gray-800"
)} )}

View file

@ -44,7 +44,7 @@ export const TextFieldWide = ({
type="text" type="text"
value={field.value ? field.value : defaultValue ?? ""} value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange} 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")} className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-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} placeholder={placeholder}
hidden={hidden} hidden={hidden}
/> />
@ -99,7 +99,7 @@ export const PasswordFieldWide = ({
value={field.value ? field.value : defaultValue ?? ""} value={field.value ? field.value : defaultValue ?? ""}
onChange={field.onChange} onChange={field.onChange}
type={isVisible ? "text" : "password"} 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")} className={classNames(meta.touched && meta.error ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-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} placeholder={placeholder}
/> />
<div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}> <div className="absolute inset-y-0 right-0 px-3 flex items-center" onClick={toggleVisibility}>
@ -158,7 +158,7 @@ export const NumberFieldWide = ({
className={classNames( className={classNames(
meta.touched && meta.error meta.touched && meta.error
? "focus:ring-red-500 focus:border-red-500 border-red-500" ? "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", : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-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" "block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white rounded-md"
)} )}
placeholder={placeholder} placeholder={placeholder}
@ -216,7 +216,7 @@ export const SwitchGroupWide = ({
form.setFieldValue(field?.name ?? "", value); form.setFieldValue(field?.name ?? "", value);
}} }}
className={classNames( className={classNames(
field.value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-500", field.value ? "bg-blue-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" "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"
)} )}
> >

View file

@ -58,7 +58,7 @@ function RadioFieldsetWide({ name, legend, options }: props) {
? "rounded-bl-md rounded-br-md" ? "rounded-bl-md rounded-br-md"
: "", : "",
checked checked
? "border-1 bg-indigo-100 dark:bg-blue-900 border-indigo-400 dark:border-blue-600 z-10" ? "border-1 bg-blue-100 dark:bg-blue-900 border-blue-400 dark:border-blue-600 z-10"
: "border-gray-200 dark:border-gray-700", : "border-gray-200 dark:border-gray-700",
"relative border p-4 flex cursor-pointer focus:outline-none" "relative border p-4 flex cursor-pointer focus:outline-none"
) )
@ -69,7 +69,7 @@ function RadioFieldsetWide({ name, legend, options }: props) {
<span <span
className={classNames( className={classNames(
checked checked
? "bg-indigo-600 dark:bg-blue-500 border-transparent" ? "bg-blue-600 dark:bg-blue-500 border-transparent"
: "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-300", : "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-300",
"h-6 w-6 mt-1 cursor-pointer rounded-full border flex items-center justify-center" "h-6 w-6 mt-1 cursor-pointer rounded-full border flex items-center justify-center"
)} )}

View file

@ -155,7 +155,7 @@ export function DownloadClientSelect({
Client Client
</Listbox.Label> </Listbox.Label>
<div className="mt-2 relative"> <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"> <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-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate"> <span className="block truncate">
{field.value {field.value
? clients.find((c) => c.id === field.value)?.name ? clients.find((c) => c.id === field.value)?.name
@ -186,7 +186,7 @@ export function DownloadClientSelect({
key={client.id} key={client.id}
className={({ active }) => classNames( className={({ active }) => classNames(
active active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800" ? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300", : "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9" "cursor-default select-none relative py-2 pl-3 pr-9"
)} )}
@ -206,7 +206,7 @@ export function DownloadClientSelect({
{selected ? ( {selected ? (
<span <span
className={classNames( className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700", active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4" "absolute inset-y-0 right-0 flex items-center pr-4"
)} )}
> >
@ -266,7 +266,7 @@ export const Select = ({
{label} {label}
</Listbox.Label> </Listbox.Label>
<div className="mt-2 relative"> <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.5 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"> <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.5 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate"> <span className="block truncate">
{field.value {field.value
? options.find((c) => c.value === field.value)?.label ? options.find((c) => c.value === field.value)?.label
@ -298,7 +298,7 @@ export const Select = ({
className={({ active }) => className={({ active }) =>
classNames( classNames(
active active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800" ? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300", : "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9" "cursor-default select-none relative py-2 pl-3 pr-9"
) )
@ -319,7 +319,7 @@ export const Select = ({
{selected ? ( {selected ? (
<span <span
className={classNames( className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700", active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4" "absolute inset-y-0 right-0 flex items-center pr-4"
)} )}
> >
@ -371,7 +371,7 @@ export const SelectWide = ({
{label} {label}
</Listbox.Label> </Listbox.Label>
<div className="w-full"> <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"> <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-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 dark:text-gray-200 sm:text-sm">
<span className="block truncate"> <span className="block truncate">
{field.value {field.value
? options.find((c) => c.value === field.value)?.label ? options.find((c) => c.value === field.value)?.label
@ -403,7 +403,7 @@ export const SelectWide = ({
className={({ active }) => className={({ active }) =>
classNames( classNames(
active active
? "text-white dark:text-gray-100 bg-indigo-600 dark:bg-gray-800" ? "text-white dark:text-gray-100 bg-blue-600 dark:bg-gray-800"
: "text-gray-900 dark:text-gray-300", : "text-gray-900 dark:text-gray-300",
"cursor-default select-none relative py-2 pl-3 pr-9" "cursor-default select-none relative py-2 pl-3 pr-9"
) )
@ -424,7 +424,7 @@ export const SelectWide = ({
{selected ? ( {selected ? (
<span <span
className={classNames( className={classNames(
active ? "text-white dark:text-gray-100" : "text-indigo-600 dark:text-gray-700", active ? "text-white dark:text-gray-100" : "text-blue-600 dark:text-gray-700",
"absolute inset-y-0 right-0 flex items-center pr-4" "absolute inset-y-0 right-0 flex items-center pr-4"
)} )}
> >

View file

@ -1,12 +1,6 @@
import React from "react"; import React from "react";
import type { FieldInputProps, FieldMetaProps, FieldProps, FormikProps, FormikValues } from "formik";
import { Field } from "formik"; import { Field } from "formik";
import type {
FieldInputProps,
FieldMetaProps,
FieldProps,
FormikProps,
FormikValues
} from "formik";
import { Switch as HeadlessSwitch } from "@headlessui/react"; import { Switch as HeadlessSwitch } from "@headlessui/react";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
@ -47,7 +41,7 @@ export const Switch = ({
}} }}
className={classNames( className={classNames(
checked ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", checked ? "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" "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"
)} )}
> >
@ -81,7 +75,7 @@ const SwitchGroup = ({
name, name,
label, label,
description, description,
heading, heading
}: SwitchGroupProps) => ( }: SwitchGroupProps) => (
<HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between"> <HeadlessSwitch.Group as="ol" className="py-4 flex items-center justify-between">
{label && <div className="flex flex-col"> {label && <div className="flex flex-col">
@ -111,7 +105,7 @@ const SwitchGroup = ({
setFieldValue(field?.name ?? "", value); setFieldValue(field?.name ?? "", value);
}} }}
className={classNames( className={classNames(
field.value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200", field.value ? "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" "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"
)} )}
> >

View file

@ -117,7 +117,7 @@ export const TextInput = <TFormValues extends Record<string, unknown>>({
aria-invalid={hasError} aria-invalid={hasError}
className={classNames( className={classNames(
"mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md",
hasError ? "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" hasError ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700"
)} )}
{...props} {...props}
{...(register && register(name, rules))} {...(register && register(name, rules))}
@ -169,7 +169,7 @@ export const PasswordInput = <TFormValues extends Record<string, unknown>>({
type={isVisible ? "text" : "password"} type={isVisible ? "text" : "password"}
className={classNames( className={classNames(
"mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md", "mt-2 block w-full dark:bg-gray-800 dark:text-gray-100 rounded-md",
hasError ? "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" hasError ? "focus:ring-red-500 focus:border-red-500 border-red-500" : "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700"
)} )}
{...props} {...props}
{...(register && register(name, rules))} {...(register && register(name, rules))}

View file

@ -77,7 +77,7 @@ export const DeleteModal: FC<DeleteModalProps> = ({ isOpen, buttonRef, toggle, d
</button> </button>
<button <button
type="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" 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-blue-500 dark:focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
onClick={toggle} onClick={toggle}
// ref={buttonRef} // ref={buttonRef}
> >

View file

@ -30,7 +30,7 @@ const Toast: FC<Props> = ({ type, body, t }) => (
</div> </div>
<div className="ml-4 flex-shrink-0 flex"> <div className="ml-4 flex-shrink-0 flex">
<button <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" 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-blue-500 dark:focus:ring-blue-500"
onClick={() => { onClick={() => {
toast.dismiss(t?.id); toast.dismiss(t?.id);
}} }}

View file

@ -98,7 +98,7 @@ function SlideOver<DataType>({
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="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" className="bg-white dark:bg-gray-900 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -135,7 +135,7 @@ function SlideOver<DataType>({
? "text-red-500 border-red-500 bg-red-50" ? "text-red-500 border-red-500 bg-red-50"
: "border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-400 bg-white dark:bg-gray-700 hover:bg-gray-50 focus:border-rose-700 active:bg-rose-700", : "border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-400 bg-white dark:bg-gray-700 hover:bg-gray-50 focus:border-rose-700 active:bg-rose-700",
isTesting ? "cursor-not-allowed" : "", isTesting ? "cursor-not-allowed" : "",
"mr-2 inline-flex items-center px-4 py-2 border font-medium rounded-md shadow-sm text-sm transition ease-in-out duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" "mr-2 inline-flex items-center px-4 py-2 border font-medium rounded-md shadow-sm text-sm transition ease-in-out duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
)} )}
disabled={isTesting} disabled={isTesting}
onClick={() => test(values)} onClick={() => test(values)}
@ -173,14 +173,14 @@ function SlideOver<DataType>({
<button <button
type="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" 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-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" 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" 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-blue-600 dark:bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
{type === "CREATE" ? "Create" : "Save"} {type === "CREATE" ? "Create" : "Save"}
</button> </button>

View file

@ -1,16 +1,62 @@
import { ReactNode } from "react"; import * as React from "react";
import type { ReactNode } from "react";
import { usePopperTooltip } from "react-popper-tooltip";
import { classNames } from "../../utils";
interface TooltipProps {
label: ReactNode;
title?: ReactNode;
maxWidth?: string;
children: ReactNode;
}
export const Tooltip = ({
label,
title,
children,
maxWidth = "max-w-sm"
}: TooltipProps) => {
const {
// TODO?: getArrowProps,
getTooltipProps,
setTooltipRef,
setTriggerRef,
visible
} = usePopperTooltip({
trigger: ["click"],
interactive: false
});
export const Tooltip = ({ children, button } : {
message?: string, children: ReactNode, button: ReactNode
}) => {
return ( return (
<div className="relative flex flex-col items-center group"> <>
{button} <div ref={setTriggerRef} className="truncate">
<div className="absolute bottom-0 flex-col items-center hidden mb-6 group-hover:flex"> {label}
<span className="z-40 p-2 text-xs leading-none text-white whitespace-no-wrap bg-gray-600 shadow-lg rounded-md">
{children}
</span>
</div> </div>
</div> {visible && (
<div
ref={setTooltipRef}
{...getTooltipProps({
className: classNames(
maxWidth,
"rounded-md border border-gray-300 text-black text-xs shadow-lg dark:text-white dark:border-gray-700 dark:shadow-2xl"
)
})}
>
{title ? (
<div className="p-2 border-b border-gray-300 bg-gray-100 dark:border-gray-700 dark:bg-gray-800 rounded-t-md">
{title}
</div>
) : null}
<div
className={classNames(
title ? "" : "rounded-t-md",
"py-1 px-2 rounded-b-md bg-white dark:bg-gray-900"
)}
>
{children}
</div>
</div>
)}
</>
); );
}; };

View file

@ -83,7 +83,7 @@ function FilterAddForm({ isOpen, toggle }: filterAddFormProps) {
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="light:bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="light:bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -115,7 +115,7 @@ function FilterAddForm({ isOpen, toggle }: filterAddFormProps) {
{...field} {...field}
id="name" id="name"
type="text" type="text"
className="block w-full shadow-sm dark:bg-gray-800 border-gray-300 dark:border-gray-700 sm:text-sm dark:text-white focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 rounded-md" className="block w-full shadow-sm dark:bg-gray-800 border-gray-300 dark:border-gray-700 sm:text-sm dark:text-white focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 rounded-md"
/> />
{meta.touched && meta.error && {meta.touched && meta.error &&
@ -133,14 +133,14 @@ function FilterAddForm({ isOpen, toggle }: filterAddFormProps) {
<div className="space-x-3 flex justify-end"> <div className="space-x-3 flex justify-end">
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-800 py-2 px-4 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="bg-white dark:bg-gray-800 py-2 px-4 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="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 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Create Create
</button> </button>

View file

@ -79,7 +79,7 @@ function APIKeyAddForm({ isOpen, toggle }: apiKeyAddFormProps) {
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="light:bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="light:bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -111,7 +111,7 @@ function APIKeyAddForm({ isOpen, toggle }: apiKeyAddFormProps) {
{...field} {...field}
id="name" id="name"
type="text" type="text"
className="block w-full shadow-sm dark:bg-gray-800 border-gray-300 dark:border-gray-700 sm:text-sm dark:text-white focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 rounded-md" className="block w-full shadow-sm dark:bg-gray-800 border-gray-300 dark:border-gray-700 sm:text-sm dark:text-white focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 rounded-md"
/> />
{meta.touched && meta.error && {meta.touched && meta.error &&
@ -129,14 +129,14 @@ function APIKeyAddForm({ isOpen, toggle }: apiKeyAddFormProps) {
<div className="space-x-3 flex justify-end"> <div className="space-x-3 flex justify-end">
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-800 py-2 px-4 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="bg-white dark:bg-gray-800 py-2 px-4 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="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 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Create Create
</button> </button>

View file

@ -336,7 +336,7 @@ function DownloadClientFormButtons({
? "text-red-500 border-red-500 bg-red-50" ? "text-red-500 border-red-500 bg-red-50"
: "border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-400 bg-white dark:bg-gray-700 hover:bg-gray-50 focus:border-rose-700 active:bg-rose-700", : "border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-400 bg-white dark:bg-gray-700 hover:bg-gray-50 focus:border-rose-700 active:bg-rose-700",
isTesting ? "cursor-not-allowed" : "", isTesting ? "cursor-not-allowed" : "",
"mr-2 inline-flex items-center px-4 py-2 border font-medium rounded-md shadow-sm text-sm transition ease-in-out duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" "mr-2 inline-flex items-center px-4 py-2 border font-medium rounded-md shadow-sm text-sm transition ease-in-out duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
)} )}
disabled={isTesting} disabled={isTesting}
// onClick={() => testClient(values)} // onClick={() => testClient(values)}
@ -374,14 +374,14 @@ function DownloadClientFormButtons({
<button <button
type="button" type="button"
className="mr-4 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-400 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" className="mr-4 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-400 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={cancelFn} onClick={cancelFn}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="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 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
{type === "CREATE" ? "Create" : "Save"} {type === "CREATE" ? "Create" : "Save"}
</button> </button>
@ -514,7 +514,7 @@ export function DownloadClientAddForm({ isOpen, toggle }: formProps) {
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-800 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="bg-white dark:bg-gray-800 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -713,7 +713,7 @@ export function DownloadClientUpdateForm({ client, isOpen, toggle }: updateFormP
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500" className="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>

View file

@ -379,7 +379,7 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500" className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -468,14 +468,14 @@ export function IndexerAddForm({ isOpen, toggle }: AddProps) {
<div className="space-x-3 flex justify-end"> <div className="space-x-3 flex justify-end">
<button <button
type="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" 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-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="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 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Save Save
</button> </button>
@ -593,7 +593,7 @@ export function IndexerUpdateForm({ isOpen, toggle, indexer }: UpdateProps) {
<input <input
type="text" type="text"
{...field} {...field}
className="block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white focus:ring-indigo-500 focus:border-indigo-500 border-gray-300 dark:border-gray-700 rounded-md" className="block w-full shadow-sm dark:bg-gray-800 sm:text-sm dark:text-white focus:ring-blue-500 focus:border-blue-500 border-gray-300 dark:border-gray-700 rounded-md"
/> />
{meta.touched && meta.error && <span>{meta.error}</span>} {meta.touched && meta.error && <span>{meta.error}</span>}
</div> </div>

View file

@ -1,16 +1,16 @@
import {useMutation} from "react-query"; import { useMutation } from "react-query";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import {XMarkIcon} from "@heroicons/react/24/solid"; import { XMarkIcon } from "@heroicons/react/24/solid";
import type {FieldProps} from "formik"; import type { FieldProps } from "formik";
import {Field, FieldArray, FormikErrors, FormikValues} from "formik"; import { Field, FieldArray, FormikErrors, FormikValues } from "formik";
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {NumberFieldWide, PasswordFieldWide, SwitchGroupWide, TextFieldWide} from "../../components/inputs"; import { NumberFieldWide, PasswordFieldWide, SwitchGroupWide, TextFieldWide } from "../../components/inputs";
import {SlideOver} from "../../components/panels"; import { SlideOver } from "../../components/panels";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {ExclamationTriangleIcon} from "@heroicons/react/24/outline"; import { ExclamationTriangleIcon } from "@heroicons/react/24/outline";
interface ChannelsFieldArrayProps { interface ChannelsFieldArrayProps {
channels: IrcChannel[]; channels: IrcChannel[];
@ -33,7 +33,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
value={field.value ?? ""} value={field.value ?? ""}
onChange={field.onChange} onChange={field.onChange}
placeholder="#Channel" placeholder="#Channel"
className="mr-4 dark:bg-gray-700 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-600 block w-full shadow-sm sm:text-sm dark:text-white rounded-md" className="mr-4 dark:bg-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-600 block w-full shadow-sm sm:text-sm dark:text-white rounded-md"
/> />
)} )}
</Field> </Field>
@ -46,7 +46,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
value={field.value ?? ""} value={field.value ?? ""}
onChange={field.onChange} onChange={field.onChange}
placeholder="Password" placeholder="Password"
className="mr-4 dark:bg-gray-700 focus:ring-indigo-500 dark:focus:ring-blue-500 focus:border-indigo-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-600 block w-full shadow-sm sm:text-sm dark:text-white rounded-md" className="mr-4 dark:bg-gray-700 focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-600 block w-full shadow-sm sm:text-sm dark:text-white rounded-md"
/> />
)} )}
</Field> </Field>
@ -54,7 +54,7 @@ const ChannelsFieldArray = ({ channels }: ChannelsFieldArrayProps) => (
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={() => remove(index)} onClick={() => remove(index)}
> >
<span className="sr-only">Remove</span> <span className="sr-only">Remove</span>

View file

@ -1,19 +1,19 @@
import {Dialog, Transition} from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
import {Fragment} from "react"; import { Fragment } from "react";
import type {FieldProps} from "formik"; import type { FieldProps } from "formik";
import {Field, Form, Formik, FormikErrors, FormikValues} from "formik"; import { Field, Form, Formik, FormikErrors, FormikValues } from "formik";
import {XMarkIcon} from "@heroicons/react/24/solid"; import { XMarkIcon } from "@heroicons/react/24/solid";
import Select, {components, ControlProps, InputProps, MenuProps, OptionProps} from "react-select"; import Select, { components, ControlProps, InputProps, MenuProps, OptionProps } from "react-select";
import {PasswordFieldWide, SwitchGroupWide, TextFieldWide} from "../../components/inputs"; import { PasswordFieldWide, SwitchGroupWide, TextFieldWide } from "../../components/inputs";
import DEBUG from "../../components/debug"; import DEBUG from "../../components/debug";
import {EventOptions, NotificationTypeOptions, SelectOption} from "../../domain/constants"; import { EventOptions, NotificationTypeOptions, SelectOption } from "../../domain/constants";
import {useMutation} from "react-query"; import { useMutation } from "react-query";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {SlideOver} from "../../components/panels"; import { SlideOver } from "../../components/panels";
import {componentMapType} from "./DownloadClientForms"; import { componentMapType } from "./DownloadClientForms";
const Input = (props: InputProps) => { const Input = (props: InputProps) => {
return ( return (
@ -226,7 +226,7 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
<div className="h-7 flex items-center"> <div className="h-7 flex items-center">
<button <button
type="button" type="button"
className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500" className="bg-white dark:bg-gray-700 rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
<span className="sr-only">Close panel</span> <span className="sr-only">Close panel</span>
@ -325,21 +325,21 @@ export function NotificationAddForm({ isOpen, toggle }: AddProps) {
<div className="space-x-3 flex justify-end"> <div className="space-x-3 flex justify-end">
<button <button
type="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" 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-blue-500 dark:focus:ring-blue-500"
onClick={() => testNotification(values)} onClick={() => testNotification(values)}
> >
Test Test
</button> </button>
<button <button
type="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" 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-blue-500 dark:focus:ring-blue-500"
onClick={toggle} onClick={toggle}
> >
Cancel Cancel
</button> </button>
<button <button
type="submit" type="submit"
className="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 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-blue-500" className="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Save Save
</button> </button>

View file

@ -1,4 +1,4 @@
import { useState } from "react"; import { useEffect, useState } from "react";
export function useToggle(initialValue = false): [boolean, () => void] { export function useToggle(initialValue = false): [boolean, () => void] {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
@ -6,3 +6,50 @@ export function useToggle(initialValue = false): [boolean, () => void] {
return [value, toggle]; return [value, toggle];
} }
const isBrowser = typeof window !== "undefined";
const getInitialState = (query: string, defaultState?: boolean) => {
// Prevent a React hydration mismatch when a default value is provided by not defaulting to window.matchMedia(query).matches.
if (defaultState !== undefined) {
return defaultState;
}
if (isBrowser) {
return window.matchMedia(query).matches;
}
// A default value has not been provided, and you are rendering on the server, warn of a possible hydration mismatch when defaulting to false.
if (process.env.NODE_ENV !== "production") {
console.warn(
"`useMedia` When server side rendering, defaultState should be defined to prevent a hydration mismatches."
);
}
return false;
};
export const useMedia = (query: string, defaultState?: boolean) => {
const [state, setState] = useState(getInitialState(query, defaultState));
useEffect(() => {
let mounted = true;
const mql = window.matchMedia(query);
const onChange = () => {
if (!mounted) {
return;
}
setState(!!mql.matches);
};
mql.addListener(onChange);
setState(mql.matches);
return () => {
mounted = false;
mql.removeListener(onChange);
};
}, [query]);
return state;
};

View file

@ -77,8 +77,7 @@ export default function Base() {
)} )}
> >
Docs Docs
<ArrowTopRightOnSquareIcon className="inline ml-1 h-5 w-5" <ArrowTopRightOnSquareIcon className="inline ml-1 h-5 w-5" aria-hidden="true" />
aria-hidden="true"/>
</a> </a>
</div> </div>
</div> </div>
@ -125,7 +124,7 @@ export default function Base() {
to="/settings" to="/settings"
className={classNames( className={classNames(
active ? "bg-gray-100 dark:bg-gray-600" : "", active ? "bg-gray-100 dark:bg-gray-600" : "",
"block px-4 py-2 text-sm text-gray-700 dark:text-gray-200" "block px-4 py-2 text-sm text-gray-900 dark:text-gray-200"
)} )}
> >
Settings Settings
@ -138,7 +137,7 @@ export default function Base() {
to="/logout" to="/logout"
className={classNames( className={classNames(
active ? "bg-gray-100 dark:bg-gray-600" : "", active ? "bg-gray-100 dark:bg-gray-600" : "",
"block px-4 py-2 text-sm text-gray-700 dark:text-gray-200" "block px-4 py-2 text-sm text-gray-900 dark:text-gray-200"
)} )}
> >
Logout Logout
@ -175,17 +174,17 @@ export default function Base() {
key={item.path} key={item.path}
to={item.path} to={item.path}
className={({ isActive }) => classNames( className={({ isActive }) => classNames(
// TODO: Double check whether this is correct "shadow-sm border bg-gray-100 border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white block px-3 py-2 rounded-md text-base",
"dark:bg-gray-900 dark:text-white block px-3 py-2 rounded-md text-base", isActive ? "underline underline-offset-2 decoration-2 decoration-sky-500 font-bold text-black" : "font-medium"
isActive ? "font-bold bg-gray-300 text-black" : "font-medium"
)} )}
end={item.path === "/"}
> >
{item.name} {item.name}
</NavLink> </NavLink>
)} )}
<Link <Link
to="/logout" to="/logout"
className="dark:bg-gray-900 dark:text-white block px-3 py-2 rounded-md text-base font-medium" className="shadow-sm border bg-gray-100 border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white block px-3 py-2 rounded-md text-base font-medium"
> >
Logout Logout
</Link> </Link>

View file

@ -1,12 +1,11 @@
import {useEffect, useRef, useState} from "react"; import { useEffect, useRef, useState } from "react";
import {ExclamationTriangleIcon} from "@heroicons/react/24/solid"; import { ExclamationTriangleIcon } from "@heroicons/react/24/solid";
import format from "date-fns/format"; import format from "date-fns/format";
import {DebounceInput} from "react-debounce-input"; import { DebounceInput } from "react-debounce-input";
import { APIClient } from "../api/APIClient";
import {APIClient} from "../api/APIClient"; import { Checkbox } from "../components/Checkbox";
import {Checkbox} from "../components/Checkbox"; import { classNames } from "../utils";
import {classNames} from "../utils"; import { SettingsContext } from "../utils/Context";
import {SettingsContext} from "../utils/Context";
type LogEvent = { type LogEvent = {
time: string; time: string;
@ -75,15 +74,15 @@ export const Logs = () => {
return ( return (
<main> <main>
<header className="py-10"> <header className="pt-10 pb-5">
<div className="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 className="text-3xl font-bold text-black dark:text-white">Logs</h1> <h1 className="text-3xl font-bold text-black dark:text-white">Logs</h1>
<div className="flex justify-center"> <div className="flex justify-center mt-1">
<ExclamationTriangleIcon <ExclamationTriangleIcon
className="h-5 w-5 text-yellow-400" className="h-5 w-5 text-yellow-400"
aria-hidden="true" aria-hidden="true"
/> />
<p className="ml-2 text-sm text-gray-800 dark:text-gray-400">This only shows new logs, no history.</p> <p className="ml-2 text-sm text-black dark:text-gray-400">This page shows only new logs, i.e. no history.</p>
</div> </div>
</div> </div>
</header> </header>
@ -99,7 +98,7 @@ export const Logs = () => {
type="text" type="text"
autoComplete="off" autoComplete="off"
className={classNames( className={classNames(
"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", "focus:ring-blue-500 dark:focus:ring-blue-500 focus:border-blue-500 dark:focus:border-blue-500 border-gray-300 dark:border-gray-700",
"block w-full dark:bg-gray-800 shadow-sm dark:text-gray-100 sm:text-sm rounded-md" "block w-full dark:bg-gray-800 shadow-sm dark:text-gray-100 sm:text-sm rounded-md"
)} )}
placeholder="Enter a string to filter logs by..." placeholder="Enter a string to filter logs by..."

View file

@ -1,4 +1,4 @@
import {NavLink, Outlet, useLocation} from "react-router-dom"; import { NavLink, Outlet, useLocation } from "react-router-dom";
import { import {
BellIcon, BellIcon,
ChatBubbleLeftRightIcon, ChatBubbleLeftRightIcon,
@ -9,7 +9,7 @@ import {
RssIcon RssIcon
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import {classNames} from "../utils"; import { classNames } from "../utils";
interface NavTabType { interface NavTabType {
name: string; name: string;
@ -45,9 +45,9 @@ function SubNavLink({ item }: NavLinkProps) {
to={item.href} to={item.href}
end end
className={({ isActive }) => classNames( className={({ isActive }) => classNames(
"border-transparent text-gray-900 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:text-gray-900 dark:hover:text-gray-300 group border-l-4 px-3 py-2 flex items-center text-sm font-medium", "border-transparent text-gray-900 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-600 hover:text-gray-900 dark:hover:text-gray-300 group border-l-4 px-3 py-2 flex items-center text-sm font-medium",
isActive ? isActive ?
"font-bold bg-teal-50 dark:bg-gray-700 border-teal-500 dark:border-blue-500 text-teal-700 dark:text-white hover:bg-teal-50 dark:hover:bg-gray-500 hover:text-teal-700 dark:hover:text-gray-200" : "" "font-bold bg-blue-50 dark:bg-gray-700 border-sky-500 dark:border-blue-500 text-sky-700 dark:text-white hover:bg-blue-100 dark:hover:bg-gray-500 hover:text-sky-700 dark:hover:text-gray-200" : ""
)} )}
aria-current={splitLocation[2] === item.href ? "page" : undefined} aria-current={splitLocation[2] === item.href ? "page" : undefined}
> >

View file

@ -81,7 +81,7 @@ export const Login = () => {
<div className="mt-6"> <div className="mt-6">
<button <button
type="submit" type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium 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" className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Sign in Sign in
</button> </button>

View file

@ -66,9 +66,9 @@ export const Onboarding = () => {
<div className="mt-6"> <div className="mt-6">
<button <button
type="submit" type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium 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" className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Create an account! Create account!
</button> </button>
</div> </div>
</Form> </Form>

View file

@ -34,7 +34,7 @@ function SelectColumnFilter({
<label className="flex items-baseline gap-x-2"> <label className="flex items-baseline gap-x-2">
<span className="text-gray-700"><>{render("Header")}:</></span> <span className="text-gray-700"><>{render("Header")}:</></span>
<select <select
className="border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" className="border-gray-300 rounded-md shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
name={id} name={id}
id={id} id={id}
value={filterValue} value={filterValue}
@ -80,7 +80,7 @@ function Table({ columns, data }: TableProps) {
// Render the UI for your table // Render the UI for your table
return ( return (
<div className="inline-block min-w-full mt-4 mb-2 align-middle"> <div className="inline-block min-w-full mt-4 mb-2 align-middle">
<div className="bg-white shadow dark:bg-gray-800 rounded-lg"> <div className="bg-white shadow-lg dark:bg-gray-800 rounded-md rounded-md overflow-auto">
<table {...getTableProps()} className="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <table {...getTableProps()} className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-800"> <thead className="bg-gray-50 dark:bg-gray-800">
{headerGroups.map((headerGroup) => { {headerGroups.map((headerGroup) => {
@ -95,7 +95,7 @@ function Table({ columns, data }: TableProps) {
<th <th
key={`${rowKey}-${columnKey}`} key={`${rowKey}-${columnKey}`}
scope="col" scope="col"
className="first:pl-5 pl-3 pr-3 py-3 text-xs font-medium tracking-wider text-left text-gray-500 uppercase group" className="first:pl-5 first:rounded-tl-md last:rounded-tr-md pl-3 pr-3 py-3 text-xs font-medium tracking-wider text-left text-gray-500 uppercase group"
{...columnRest} {...columnRest}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">

View file

@ -72,7 +72,7 @@ export function FilterActions({ filter, values }: FilterActionsProps) {
<div className="ml-4 mt-4 flex-shrink-0"> <div className="ml-4 mt-4 flex-shrink-0">
<button <button
type="button" type="button"
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={() => push(newAction)} onClick={() => push(newAction)}
> >
Add new Add new
@ -436,7 +436,7 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
setFieldValue(field?.name ?? "", value); setFieldValue(field?.name ?? "", value);
}} }}
className={classNames( className={classNames(
field.value ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", field.value ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"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" "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"
)} )}
> >
@ -456,7 +456,7 @@ function FilterActionsItem({ action, clients, idx, remove }: FilterActionsItemPr
<div className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between"> <div className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
<div className="truncate"> <div className="truncate">
<div className="flex text-sm"> <div className="flex text-sm">
<p className="ml-4 font-medium text-indigo-600 dark:text-gray-100 truncate"> <p className="ml-4 font-medium text-blue-600 dark:text-gray-100 truncate">
{action.name} {action.name}
</p> </p>
</div> </div>

View file

@ -1,9 +1,9 @@
import React, {useRef} from "react"; import React, { useRef } from "react";
import {useMutation, useQuery} from "react-query"; import { useMutation, useQuery } from "react-query";
import {NavLink, Route, Routes, useLocation, useNavigate, useParams} from "react-router-dom"; import { NavLink, Route, Routes, useLocation, useNavigate, useParams } from "react-router-dom";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import {Form, Formik, FormikValues, useFormikContext} from "formik"; import { Form, Formik, FormikValues, useFormikContext } from "formik";
import {ChevronDownIcon, ChevronRightIcon} from "@heroicons/react/24/solid"; import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/24/solid";
import { import {
CODECS_OPTIONS, CODECS_OPTIONS,
@ -19,10 +19,10 @@ import {
SOURCES_MUSIC_OPTIONS, SOURCES_MUSIC_OPTIONS,
SOURCES_OPTIONS SOURCES_OPTIONS
} from "../../domain/constants"; } from "../../domain/constants";
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {useToggle} from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import {classNames} from "../../utils"; import { classNames } from "../../utils";
import { import {
CheckboxField, CheckboxField,
@ -35,10 +35,10 @@ import {
} from "../../components/inputs"; } from "../../components/inputs";
import DEBUG from "../../components/debug"; import DEBUG from "../../components/debug";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {DeleteModal} from "../../components/modals"; import { DeleteModal } from "../../components/modals";
import {TitleSubtitle} from "../../components/headings"; import { TitleSubtitle } from "../../components/headings";
import {TextArea} from "../../components/inputs/input"; import { TextArea } from "../../components/inputs/input";
import {FilterActions} from "./action"; import { FilterActions } from "./action";
interface tabType { interface tabType {
name: string; name: string;
@ -122,7 +122,7 @@ const FormButtonsGroup = ({ values, deleteAction, reset }: FormButtonsGroupProps
</button> </button>
<button <button
type="submit" type="submit"
className="ml-4 relative 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" className="ml-4 relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
Save Save
</button> </button>

View file

@ -1,8 +1,9 @@
import {Dispatch, FC, Fragment, MouseEventHandler, useReducer, useRef, useState} from "react"; import { Dispatch, FC, Fragment, MouseEventHandler, useReducer, useRef, useState } from "react";
import {Link} from "react-router-dom"; import { Link } from "react-router-dom";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import {Listbox, Menu, Switch, Transition} from "@headlessui/react"; import { Listbox, Menu, Switch, Transition } from "@headlessui/react";
import {useMutation, useQuery, useQueryClient} from "react-query"; import { useMutation, useQuery, useQueryClient } from "react-query";
import { import {
ArrowsRightLeftIcon, ArrowsRightLeftIcon,
CheckIcon, CheckIcon,
@ -13,14 +14,14 @@ import {
TrashIcon TrashIcon
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {classNames} from "../../utils"; import { classNames } from "../../utils";
import {FilterAddForm} from "../../forms"; import { FilterAddForm } from "../../forms";
import {useToggle} from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {EmptyListState} from "../../components/emptystates"; import { EmptyListState } from "../../components/emptystates";
import {DeleteModal} from "../../components/modals"; import { DeleteModal } from "../../components/modals";
type FilterListState = { type FilterListState = {
indexerFilter: string[], indexerFilter: string[],
@ -85,7 +86,7 @@ export default function Filters() {
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<button <button
type="button" type="button"
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
onClick={toggleCreateFilter} onClick={toggleCreateFilter}
> >
Add new Add new
@ -139,8 +140,8 @@ function FilterList({ toggleCreateFilter }: any) {
return ( return (
<div className="max-w-screen-xl mx-auto pb-12 px-4 sm:px-6 lg:px-8 relative"> <div className="max-w-screen-xl mx-auto pb-12 px-4 sm:px-6 lg:px-8 relative">
<div className="align-middle min-w-full rounded-t-md rounded-b-lg shadow-lg bg-gray-50 dark:bg-gray-800"> <div className="align-middle min-w-full rounded-t-lg rounded-b-lg shadow-lg bg-gray-50 dark:bg-gray-800">
<div className="flex justify-between px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"> <div className="rounded-t-lg flex justify-between px-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
<div className="flex gap-4"> <div className="flex gap-4">
<StatusButton data={filtered.all} label="All" value="" currentValue={status} dispatch={dispatchFilter} /> <StatusButton data={filtered.all} label="All" value="" currentValue={status} dispatch={dispatchFilter} />
<StatusButton data={filtered.enabled} label="Enabled" value="enabled" currentValue={status} dispatch={dispatchFilter} /> <StatusButton data={filtered.enabled} label="Enabled" value="enabled" currentValue={status} dispatch={dispatchFilter} />
@ -389,7 +390,7 @@ function FilterListItem({ filter, idx }: FilterListItemProps) {
<li <li
key={filter.id} key={filter.id}
className={classNames( className={classNames(
"flex items-center hover:bg-gray-100 dark:hover:bg-[#222225]", "flex items-center hover:bg-gray-100 dark:hover:bg-[#222225] rounded-b-lg",
idx % 2 === 0 ? idx % 2 === 0 ?
"bg-white dark:bg-[#2e2e31]" : "bg-white dark:bg-[#2e2e31]" :
"bg-gray-50 dark:bg-gray-800" "bg-gray-50 dark:bg-gray-800"
@ -402,7 +403,7 @@ function FilterListItem({ filter, idx }: FilterListItemProps) {
checked={enabled} checked={enabled}
onChange={toggleActive} onChange={toggleActive}
className={classNames( className={classNames(
enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-700", enabled ? "bg-blue-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-700",
"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" "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"
)} )}
> >
@ -514,37 +515,35 @@ const ListboxFilter = ({
onChange, onChange,
children children
}: ListboxFilterProps) => ( }: ListboxFilterProps) => (
<div className=""> <Listbox
<Listbox refName={id}
refName={id} value={currentValue}
value={currentValue} onChange={onChange}
onChange={onChange} >
> <div className="relative">
<div className="relative"> <Listbox.Button className="relative w-full py-2 pr-5 text-left dark:text-gray-400 sm:text-sm">
<Listbox.Button className="relative w-full py-2 pr-5 text-left cursor-default dark:text-gray-400 sm:text-sm"> <span className="block truncate">{label}</span>
<span className="block truncate">{label}</span> <span className="absolute inset-y-0 right-0 flex items-center pointer-events-none">
<span className="absolute inset-y-0 right-0 flex items-center pointer-events-none"> <ChevronDownIcon
<ChevronDownIcon className="w-3 h-3 text-gray-600 hover:text-gray-600"
className="w-3 h-3 text-gray-600 hover:text-gray-600" aria-hidden="true"
aria-hidden="true" />
/> </span>
</span> </Listbox.Button>
</Listbox.Button> <Transition
<Transition as={Fragment}
as={Fragment} leave="transition ease-in duration-100"
leave="transition ease-in duration-100" leaveFrom="opacity-100"
leaveFrom="opacity-100" leaveTo="opacity-0"
leaveTo="opacity-0" >
<Listbox.Options
className="w-48 absolute z-10 w-full mt-1 right-0 overflow-auto text-base bg-white dark:bg-gray-800 rounded-md shadow-lg max-h-60 border border-opacity-5 border-black dark:border-gray-700 dark:border-opacity-40 focus:outline-none sm:text-sm"
> >
<Listbox.Options {children}
className="w-48 absolute z-10 w-full mt-1 right-0 overflow-auto text-base bg-white dark:bg-gray-800 rounded-md shadow-lg max-h-60 border border-opacity-5 border-black dark:border-gray-700 dark:border-opacity-40 focus:outline-none sm:text-sm" </Listbox.Options>
> </Transition>
{children} </div>
</Listbox.Options> </Listbox>
</Transition>
</div>
</Listbox>
</div>
); );
// a unique option from a list // a unique option from a list

View file

@ -1,13 +1,13 @@
import * as React from "react"; import * as React from "react";
import {useQuery} from "react-query"; import { useQuery } from "react-query";
import {Listbox, Transition} from "@headlessui/react"; import { Listbox, Transition } from "@headlessui/react";
import {CheckIcon, ChevronDownIcon} from "@heroicons/react/24/solid"; import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/solid";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {classNames} from "../../utils"; import { classNames } from "../../utils";
import {PushStatusOptions} from "../../domain/constants"; import { PushStatusOptions } from "../../domain/constants";
import {FilterProps} from "react-table"; import { FilterProps } from "react-table";
import {DebounceInput} from "react-debounce-input"; import { DebounceInput } from "react-debounce-input";
interface ListboxFilterProps { interface ListboxFilterProps {
id: string; id: string;
@ -31,7 +31,7 @@ const ListboxFilter = ({
onChange={onChange} onChange={onChange}
> >
<div className="relative mt-1"> <div className="relative mt-1">
<Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white dark:bg-gray-800 rounded-lg shadow-md cursor-default dark:text-gray-400 sm:text-sm"> <Listbox.Button className="relative w-full py-2 pl-3 pr-10 text-left bg-white dark:bg-gray-800 rounded-lg shadow-md cursor-pointer dark:text-gray-400 sm:text-sm">
<span className="block truncate">{label}</span> <span className="block truncate">{label}</span>
<span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none"> <span className="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<ChevronDownIcon <ChevronDownIcon
@ -63,7 +63,7 @@ export const IndexerSelectColumnFilter = ({
column: { filterValue, setFilter, id } column: { filterValue, setFilter, id }
}: FilterProps<object>) => { }: FilterProps<object>) => {
const { data, isSuccess } = useQuery( const { data, isSuccess } = useQuery(
"release_indexers", "indexer_options",
() => APIClient.release.indexerOptions(), () => APIClient.release.indexerOptions(),
{ {
keepPreviousData: true, keepPreviousData: true,

View file

@ -1,6 +1,6 @@
import * as React from "react"; import * as React from "react";
import {useQuery} from "react-query"; import { useQuery } from "react-query";
import {Column, useFilters, usePagination, useSortBy, useTable} from "react-table"; import { Column, useFilters, usePagination, useSortBy, useTable } from "react-table";
import { import {
ChevronDoubleLeftIcon, ChevronDoubleLeftIcon,
ChevronDoubleRightIcon, ChevronDoubleRightIcon,
@ -8,13 +8,13 @@ import {
ChevronRightIcon ChevronRightIcon
} from "@heroicons/react/24/solid"; } from "@heroicons/react/24/solid";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {EmptyListState} from "../../components/emptystates"; import { EmptyListState } from "../../components/emptystates";
import * as Icons from "../../components/Icons"; import * as Icons from "../../components/Icons";
import * as DataTable from "../../components/data-table"; import * as DataTable from "../../components/data-table";
import {IndexerSelectColumnFilter, PushStatusSelectColumnFilter, SearchColumnFilter} from "./Filters"; import { IndexerSelectColumnFilter, PushStatusSelectColumnFilter, SearchColumnFilter } from "./Filters";
type TableState = { type TableState = {
queryPageIndex: number; queryPageIndex: number;
@ -182,7 +182,7 @@ export const ReleaseTable = () => {
)) ))
)} )}
</div> </div>
<div className="bg-white shadow-lg dark:bg-gray-800 rounded-lg"> <div className="bg-white shadow-lg dark:bg-gray-800 rounded-md overflow-auto">
<table {...getTableProps()} className="min-w-full divide-y divide-gray-200 dark:divide-gray-700"> <table {...getTableProps()} className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead className="bg-gray-50 dark:bg-gray-800"> <thead className="bg-gray-50 dark:bg-gray-800">
{headerGroups.map((headerGroup) => { {headerGroups.map((headerGroup) => {
@ -197,7 +197,7 @@ export const ReleaseTable = () => {
<th <th
key={`${rowKey}-${columnKey}`} key={`${rowKey}-${columnKey}`}
scope="col" scope="col"
className="first:pl-5 pl-3 pr-3 py-3 text-xs font-medium tracking-wider text-left text-gray-500 uppercase group" className="first:pl-5 pl-3 pr-3 py-3 first:rounded-tl-md last:rounded-tr-md text-xs font-medium tracking-wider text-left text-gray-500 uppercase group"
{...columnRest} {...columnRest}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -259,13 +259,13 @@ export const ReleaseTable = () => {
</div> </div>
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> <div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div className="flex items-baseline gap-x-2"> <div className="flex items-baseline gap-x-2">
<span className="text-sm text-gray-700"> <span className="text-sm text-gray-700 dark:text-gray-500">
Page <span className="font-medium">{pageIndex + 1}</span> of <span className="font-medium">{pageOptions.length}</span> Page <span className="font-medium">{pageIndex + 1}</span> of <span className="font-medium">{pageOptions.length}</span>
</span> </span>
<label> <label>
<span className="sr-only bg-gray-700">Items Per Page</span> <span className="sr-only bg-gray-700">Items Per Page</span>
<select <select
className="block w-full border-gray-300 rounded-md shadow-sm cursor-pointer dark:bg-gray-800 dark:border-gray-800 dark:text-gray-600 dark:hover:text-gray-500 focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50" className="py-1 pl-2 pr-8 text-sm block w-full border-gray-300 rounded-md shadow-sm cursor-pointer dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 dark:hover:text-gray-500 focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
value={pageSize} value={pageSize}
onChange={e => { onChange={e => {
setPageSize(Number(e.target.value)); setPageSize(Number(e.target.value));
@ -280,7 +280,7 @@ export const ReleaseTable = () => {
</label> </label>
</div> </div>
<div> <div>
<nav className="relative z-0 inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination"> <nav className="inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
<DataTable.PageButton <DataTable.PageButton
className="rounded-l-md" className="rounded-l-md"
onClick={() => gotoPage(0)} onClick={() => gotoPage(0)}

View file

@ -12,7 +12,7 @@ function ActionSettings() {
<div className="ml-4 mt-4 flex-shrink-0"> <div className="ml-4 mt-4 flex-shrink-0">
<button <button
type="button" type="button"
className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
Add new Add new
</button> </button>

View file

@ -1,21 +1,21 @@
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {useRef} from "react"; import { useRef } from "react";
import {useMutation, useQuery} from "react-query"; import { useMutation, useQuery } from "react-query";
import {KeyField} from "../../components/fields/text"; import { KeyField } from "../../components/fields/text";
import {DeleteModal} from "../../components/modals"; import { DeleteModal } from "../../components/modals";
import APIKeyAddForm from "../../forms/settings/APIKeyAddForm"; import APIKeyAddForm from "../../forms/settings/APIKeyAddForm";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {useToggle} from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import {classNames} from "../../utils"; import { classNames } from "../../utils";
import {TrashIcon} from "@heroicons/react/24/outline"; import { TrashIcon } from "@heroicons/react/24/outline";
import {EmptySimple} from "../../components/emptystates"; import { EmptySimple } from "../../components/emptystates";
function APISettings() { function APISettings() {
const [addFormIsOpen, toggleAddForm] = useToggle(false); const [addFormIsOpen, toggleAddForm] = useToggle(false);
const { isLoading, data } = useQuery( const { data } = useQuery(
["apikeys"], ["apikeys"],
() => APIClient.apikeys.getAll(), () => APIClient.apikeys.getAll(),
{ {
@ -40,7 +40,7 @@ function APISettings() {
<div className="ml-4 mt-4 flex-shrink-0"> <div className="ml-4 mt-4 flex-shrink-0">
<button <button
type="button" type="button"
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
onClick={toggleAddForm} onClick={toggleAddForm}
> >
Add new Add new

View file

@ -47,7 +47,7 @@ function DownloadClientSettingsListItem({ client, idx }: DLSettingsItemProps) {
checked={client.enabled} checked={client.enabled}
onChange={onToggleMutation} onChange={onToggleMutation}
className={classNames( className={classNames(
client.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", client.enabled ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"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" "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"
)} )}
> >
@ -65,8 +65,8 @@ function DownloadClientSettingsListItem({ client, idx }: DLSettingsItemProps) {
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{client.host}</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{client.host}</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{DownloadClientTypeNameMap[client.type]}</td> <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">{DownloadClientTypeNameMap[client.type]}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<span className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 cursor-pointer" onClick={toggleUpdateClient}> <span className="text-blue-600 dark:text-gray-300 hover:text-blue-900 cursor-pointer" onClick={toggleUpdateClient}>
Edit Edit
</span> </span>
</td> </td>
</tr> </tr>
@ -82,8 +82,9 @@ function DownloadClientSettings() {
{ refetchOnWindowFocus: false } { refetchOnWindowFocus: false }
); );
if (error) if (error) {
return (<p>An error has occurred: </p>); return <p>Failed to fetch download clients</p>;
}
return ( return (
<div className="lg:col-span-9"> <div className="lg:col-span-9">
@ -95,16 +96,16 @@ function DownloadClientSettings() {
<div className="ml-4 mt-4"> <div className="ml-4 mt-4">
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Clients</h3> <h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">Clients</h3>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400"> <p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
Manage download clients. Manage download clients.
</p> </p>
</div> </div>
<div className="ml-4 mt-4 flex-shrink-0"> <div className="ml-4 mt-4 flex-shrink-0">
<button <button
type="button" type="button"
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
onClick={toggleAddClient} onClick={toggleAddClient}
> >
Add new Add new
</button> </button>
</div> </div>
</div> </div>

View file

@ -1,18 +1,18 @@
import {useToggle} from "../../hooks/hooks"; import { useToggle } from "../../hooks/hooks";
import {useMutation, useQuery, useQueryClient} from "react-query"; import { useMutation, useQuery, useQueryClient } from "react-query";
import {APIClient} from "../../api/APIClient"; import { APIClient } from "../../api/APIClient";
import {Menu, Switch, Transition} from "@headlessui/react"; import { Menu, Switch, Transition } from "@headlessui/react";
import {classNames} from "../../utils"; import { classNames } from "../../utils";
import {Fragment, useRef, useState} from "react"; import { Fragment, useRef, useState } from "react";
import {toast} from "react-hot-toast"; import { toast } from "react-hot-toast";
import Toast from "../../components/notifications/Toast"; import Toast from "../../components/notifications/Toast";
import {queryClient} from "../../App"; import { queryClient } from "../../App";
import {DeleteModal} from "../../components/modals"; import { DeleteModal } from "../../components/modals";
import {ArrowsRightLeftIcon, EllipsisHorizontalIcon, PencilSquareIcon, TrashIcon} from "@heroicons/react/24/outline"; import { ArrowsRightLeftIcon, EllipsisHorizontalIcon, PencilSquareIcon, TrashIcon } from "@heroicons/react/24/outline";
import {FeedUpdateForm} from "../../forms/settings/FeedForms"; import { FeedUpdateForm } from "../../forms/settings/FeedForms";
import {EmptySimple} from "../../components/emptystates"; import { EmptySimple } from "../../components/emptystates";
import {ImplementationBadges} from "./Indexer"; import { ImplementationBadges } from "./Indexer";
function FeedSettings() { function FeedSettings() {
const { data } = useQuery( const { data } = useQuery(
@ -101,7 +101,7 @@ function ListItem({ feed }: ListItemProps) {
checked={feed.enabled} checked={feed.enabled}
onChange={toggleActive} onChange={toggleActive}
className={classNames( className={classNames(
feed.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", feed.enabled ? "bg-blue-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"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" "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"
)} )}
> >

View file

@ -53,7 +53,7 @@ const ListItem = ({ indexer }: ListItemProps) => {
checked={indexer.enabled ?? false} checked={indexer.enabled ?? false}
onChange={toggleUpdate} onChange={toggleUpdate}
className={classNames( className={classNames(
indexer.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", indexer.enabled ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"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" "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"
)} )}
> >
@ -70,7 +70,7 @@ const ListItem = ({ indexer }: ListItemProps) => {
<td className="px-6 py-4 w-full whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">{indexer.name}</td> <td className="px-6 py-4 w-full whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">{indexer.name}</td>
<td className="px-6 py-4 w-full whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">{ImplementationBadges[indexer.implementation]}</td> <td className="px-6 py-4 w-full whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">{ImplementationBadges[indexer.implementation]}</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<span className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 dark:hover:text-blue-500 cursor-pointer" onClick={toggleUpdate}> <span className="text-blue-600 dark:text-gray-300 hover:text-blue-900 dark:hover:text-blue-500 cursor-pointer" onClick={toggleUpdate}>
Edit Edit
</span> </span>
</td> </td>
@ -107,7 +107,7 @@ function IndexerSettings() {
<button <button
type="button" type="button"
onClick={toggleAddIndexer} onClick={toggleAddIndexer}
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-blue-500"
> >
Add new Add new
</button> </button>

View file

@ -50,7 +50,7 @@ export const IrcSettings = () => {
<button <button
type="button" type="button"
onClick={toggleAddNetwork} onClick={toggleAddNetwork}
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
Add new Add new
</button> </button>

View file

@ -32,7 +32,7 @@ function NotificationSettings() {
<button <button
type="button" type="button"
onClick={toggleAddNotifications} onClick={toggleAddNotifications}
className="relative 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" className="relative inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 dark:bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
> >
Add new Add new
</button> </button>
@ -101,7 +101,7 @@ function ListItem({ notification }: ListItemProps) {
checked={notification.enabled} checked={notification.enabled}
onChange={toggleUpdateForm} onChange={toggleUpdateForm}
className={classNames( className={classNames(
notification.enabled ? "bg-teal-500 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-600", notification.enabled ? "bg-blue-500" : "bg-gray-200 dark:bg-gray-600",
"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" "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"
)} )}
> >
@ -130,7 +130,7 @@ function ListItem({ notification }: ListItemProps) {
</span> </span>
</div> </div>
<div className="col-span-1 flex items-center"> <div className="col-span-1 flex items-center">
<span className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 cursor-pointer" onClick={toggleUpdateForm}> <span className="text-blue-600 dark:text-gray-300 hover:text-blue-900 cursor-pointer" onClick={toggleUpdateForm}>
Edit Edit
</span> </span>
</div> </div>

View file

@ -1,36 +1,36 @@
const colors = require('tailwindcss/colors') const colors = require("tailwindcss/colors")
module.exports = { module.exports = {
content: [ content: [
'./src/**/*.{tsx,ts,html,css}', "./src/**/*.{tsx,ts,html,css}",
], ],
safelist: [ safelist: [
'col-span-1', "col-span-1",
'col-span-2', "col-span-2",
'col-span-3', "col-span-3",
'col-span-4', "col-span-4",
'col-span-5', "col-span-5",
'col-span-6', "col-span-6",
'col-span-7', "col-span-7",
'col-span-8', "col-span-8",
'col-span-9', "col-span-9",
'col-span-10', "col-span-10",
'col-span-11', "col-span-11",
'col-span-12', "col-span-12",
], ],
// purge: false, // purge: false,
darkMode: 'class', // or 'media' or 'class' darkMode: "class", // or 'media' or 'class'
theme: { theme: {
extend: { extend: {
colors: { colors: {
gray: colors.zinc, gray: colors.zinc,
}, },
},
}, },
variants: { },
extend: {}, variants: {
}, extend: {},
plugins: [ },
require('@tailwindcss/forms'), plugins: [
], require("@tailwindcss/forms"),
],
} }

View file

@ -1620,6 +1620,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.18.3":
version: 7.19.0
resolution: "@babel/runtime@npm:7.19.0"
dependencies:
regenerator-runtime: ^0.13.4
checksum: fa69c351bb05e1db3ceb9a02fdcf620c234180af68cdda02152d3561015f6d55277265d3109815992f96d910f3db709458cae4f8df1c3def66f32e0867d82294
languageName: node
linkType: hard
"@babel/template@npm:^7.16.7, @babel/template@npm:^7.3.3": "@babel/template@npm:^7.16.7, @babel/template@npm:^7.3.3":
version: 7.16.7 version: 7.16.7
resolution: "@babel/template@npm:7.16.7" resolution: "@babel/template@npm:7.16.7"
@ -2493,6 +2502,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@popperjs/core@npm:^2.11.5":
version: 2.11.6
resolution: "@popperjs/core@npm:2.11.6"
checksum: 47fb328cec1924559d759b48235c78574f2d71a8a6c4c03edb6de5d7074078371633b91e39bbf3f901b32aa8af9b9d8f82834856d2f5737a23475036b16817f0
languageName: node
linkType: hard
"@rollup/plugin-babel@npm:^5.2.0": "@rollup/plugin-babel@npm:^5.2.0":
version: 5.3.1 version: 5.3.1
resolution: "@rollup/plugin-babel@npm:5.3.1" resolution: "@rollup/plugin-babel@npm:5.3.1"
@ -8724,7 +8740,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0 version: 1.4.0
resolution: "loose-envify@npm:1.4.0" resolution: "loose-envify@npm:1.4.0"
dependencies: dependencies:
@ -10865,6 +10881,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-fast-compare@npm:^3.0.1":
version: 3.2.0
resolution: "react-fast-compare@npm:3.2.0"
checksum: 8ef272c825ae329f61633ce4ce7f15aa5b84e5214d88bc0823880236e03e985a13195befa2c7a4eda7db3b017dc7985729152d88445823f652403cf36c2b86aa
languageName: node
linkType: hard
"react-hook-form@npm:^7.32.1": "react-hook-form@npm:^7.32.1":
version: 7.32.1 version: 7.32.1
resolution: "react-hook-form@npm:7.32.1" resolution: "react-hook-form@npm:7.32.1"
@ -10917,6 +10940,34 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-popper-tooltip@npm:^4.4.2":
version: 4.4.2
resolution: "react-popper-tooltip@npm:4.4.2"
dependencies:
"@babel/runtime": ^7.18.3
"@popperjs/core": ^2.11.5
react-popper: ^2.3.0
peerDependencies:
react: ">=16.6.0"
react-dom: ">=16.6.0"
checksum: 516988b9258f05fe8f48d702654d70c3701d9a730c10a07c2b5229193322b985478fad7033092c8f0449d83e77aa536d2993747609206208ed6f6b014b98acb0
languageName: node
linkType: hard
"react-popper@npm:^2.3.0":
version: 2.3.0
resolution: "react-popper@npm:2.3.0"
dependencies:
react-fast-compare: ^3.0.1
warning: ^4.0.2
peerDependencies:
"@popperjs/core": ^2.0.0
react: ^16.8.0 || ^17 || ^18
react-dom: ^16.8.0 || ^17 || ^18
checksum: 837111c98738011c69b3069a464ea5bdcbf487105b6148e8faf90cb7337e134edb1b98b8824322941c378756cca30a15c18c25f558e53b85ed5762fa0dc8e6b2
languageName: node
linkType: hard
"react-query@npm:^3.39.1": "react-query@npm:^3.39.1":
version: 3.39.1 version: 3.39.1
resolution: "react-query@npm:3.39.1" resolution: "react-query@npm:3.39.1"
@ -12861,6 +12912,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"warning@npm:^4.0.2":
version: 4.0.3
resolution: "warning@npm:4.0.3"
dependencies:
loose-envify: ^1.0.0
checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c
languageName: node
linkType: hard
"watchpack@npm:^2.3.1": "watchpack@npm:^2.3.1":
version: 2.4.0 version: 2.4.0
resolution: "watchpack@npm:2.4.0" resolution: "watchpack@npm:2.4.0"
@ -12913,6 +12973,7 @@ __metadata:
react-hook-form: ^7.32.1 react-hook-form: ^7.32.1
react-hot-toast: ^2.2.0 react-hot-toast: ^2.2.0
react-multi-select-component: ^4.2.9 react-multi-select-component: ^4.2.9
react-popper-tooltip: ^4.4.2
react-query: ^3.39.1 react-query: ^3.39.1
react-ridge-state: 4.2.2 react-ridge-state: 4.2.2
react-router-dom: ^6.3.0 react-router-dom: ^6.3.0