refactor: releases table-related code and fix for #158 (#159)

* refactor(APIClient): updated the newly added findQuery function to use URLSearchParams instead of manually crafting the URI string itself.

* refactor: moved duplicate dashboard/release code to a separate folder: components/data-table.

* refactor(SlideOver): added proper typings to the SlideOver component and added a sanity check to prevent passing of null/undefined values to the child component before rendering.

* refactor: removed the redundant Network and Channel typings and updated relevant typings to match the backend. adapted relevant code to match these changes.

* fix(ChannelsFieldArray): fixed a bug where it was unable to add a new irc network due to the validation object being initialized as non-empty (formik requires that successful validated entries return empty objects)

* refactor(screens/settings/Irc): replaced incorrect typings, sanitized potentially null values and cleaned up the code.

* fix: included changes should fix issue #158 as well.

* feat: send chan empty array
This commit is contained in:
stacksmash76 2022-03-04 21:13:46 +01:00 committed by GitHub
parent 5a45851677
commit 9ea29d02a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 974 additions and 1187 deletions

View file

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

View file

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

View file

@ -0,0 +1,76 @@
import * as React from "react";
import { formatDistanceToNowStrict } from "date-fns";
import { CheckIcon } from "@heroicons/react/solid";
import { ClockIcon, BanIcon, ExclamationCircleIcon } from "@heroicons/react/outline";
import { classNames, simplifyDate } from "../../utils";
interface CellProps {
value: string;
}
export const AgeCell = ({ value }: CellProps) => (
<div className="text-sm text-gray-500" title={value}>
{formatDistanceToNowStrict(new Date(value), { addSuffix: true })}
</div>
);
export const ReleaseCell = ({ value }: CellProps) => (
<div className="text-sm font-medium text-gray-900 dark:text-gray-300" title={value}>
{value}
</div>
);
export const IndexerCell = ({ value }: CellProps) => (
<div className="text-sm font-medium text-gray-900 dark:text-gray-500" title={value}>
{value}
</div>
);
interface ReleaseStatusCellProps {
value: ReleaseActionStatus[];
column: any;
row: any;
}
interface StatusCellMapEntry {
colors: string;
icon: React.ReactElement;
}
const StatusCellMap: Record<string, StatusCellMapEntry> = {
"PUSH_ERROR": {
colors: "bg-pink-100 text-pink-800 hover:bg-pink-300",
icon: <ExclamationCircleIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_REJECTED": {
colors: "bg-blue-200 dark:bg-blue-100 text-blue-400 dark:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-400",
icon: <BanIcon className="h-5 w-5" aria-hidden="true" />
},
"PUSH_APPROVED": {
colors: "bg-green-100 text-green-800 hover:bg-green-300",
icon: <CheckIcon className="h-5 w-5" aria-hidden="true" />
},
"PENDING": {
colors: "bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
icon: <ClockIcon className="h-5 w-5" aria-hidden="true" />
}
};
export const ReleaseStatusCell = ({ value }: ReleaseStatusCellProps) => (
<div className="flex text-sm font-medium text-gray-900 dark:text-gray-300">
{value.map((v, idx) => (
<div
key={idx}
title={`action: ${v.action}, type: ${v.type}, status: ${v.status}, time: ${simplifyDate(v.timestamp)}, rejections: ${v?.rejections}`}
className={classNames(
StatusCellMap[v.status].colors,
"mr-1 inline-flex items-center rounded text-xs font-semibold uppercase cursor-pointer"
)}
>
{StatusCellMap[v.status].icon}
</div>
))}
</div>
);

View file

@ -0,0 +1,2 @@
export * from "./Buttons";
export * from "./Cells";

View file

@ -33,7 +33,7 @@ interface EmptyListStateProps {
export function EmptyListState({ text, buttonText, buttonOnClick }: EmptyListStateProps) {
return (
<div className="px-4 py-12 flex flex-col items-center">
<p className="text-center text-gray-500 dark:text-white">{text}</p>
<p className="text-center text-gray-800 dark:text-white">{text}</p>
{buttonText && buttonOnClick && (
<button
type="button"

View file

@ -7,22 +7,31 @@ import { useToggle } from "../../hooks/hooks";
import { DeleteModal } from "../modals";
import { classNames } from "../../utils";
interface SlideOverProps {
interface SlideOverProps<DataType> {
title: string;
initialValues: any;
validate?: any;
onSubmit: any;
initialValues: DataType;
validate?: (values?: any) => void;
onSubmit: (values?: DataType) => void;
isOpen: boolean;
toggle: any;
children?: (values: any) => React.ReactNode;
deleteAction?: any
toggle: () => void;
children?: (values: DataType) => React.ReactNode;
deleteAction?: () => void;
type: "CREATE" | "UPDATE";
}
function SlideOver({ title, initialValues, validate, onSubmit, deleteAction, isOpen, toggle, type, children }: SlideOverProps) {
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false)
const cancelModalButtonRef = useRef(null)
function SlideOver<DataType>({
title,
initialValues,
validate,
onSubmit,
deleteAction,
isOpen,
toggle,
type,
children
}: SlideOverProps<DataType>): React.ReactElement {
const cancelModalButtonRef = useRef(null);
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
return (
<Transition.Root show={isOpen} as={Fragment}>
@ -84,7 +93,9 @@ function SlideOver({ title, initialValues, validate, onSubmit, deleteAction, isO
</div>
</div>
{children !== undefined && children(values)}
{!!values && children !== undefined ? (
children(values)
) : null}
</div>
<div className="flex-shrink-0 px-4 border-t border-gray-200 dark:border-gray-700 py-5 sm:px-6">