feat(irc): improve list view (#466)

* feat(irc): add irc status examples

* feat(irc): add dropdown menu to list

* feat(irc): update heroicons and add expand button

* feat(irc): update heroicons and add expand button
This commit is contained in:
ze0s 2022-09-22 16:39:05 +02:00 committed by GitHub
parent f5faf066a9
commit 300418b9f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 478 additions and 258 deletions

View file

@ -1,8 +1,8 @@
import { Fragment } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";
import { Disclosure, Menu, Transition } from "@headlessui/react";
import { ExternalLinkIcon } from "@heroicons/react/solid";
import { ChevronDownIcon, MenuIcon, XIcon } from "@heroicons/react/outline";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/solid";
import { Bars3Icon, ChevronDownIcon, XMarkIcon } from "@heroicons/react/24/outline";
import { AuthContext } from "../utils/Context";
@ -76,7 +76,7 @@ export default function Base() {
)}
>
Docs
<ExternalLinkIcon className="inline ml-1 h-5 w-5"
<ArrowTopRightOnSquareIcon className="inline ml-1 h-5 w-5"
aria-hidden="true"/>
</a>
</div>
@ -157,9 +157,9 @@ export default function Base() {
className="bg-gray-200 dark:bg-gray-800 inline-flex items-center justify-center p-2 rounded-md text-gray-600 dark:text-gray-400 hover:text-white hover:bg-gray-700">
<span className="sr-only">Open main menu</span>
{open ? (
<XIcon className="block h-6 w-6" aria-hidden="true"/>
<XMarkIcon className="block h-6 w-6" aria-hidden="true"/>
) : (
<MenuIcon className="block h-6 w-6" aria-hidden="true"/>
<Bars3Icon className="block h-6 w-6" aria-hidden="true"/>
)}
</Disclosure.Button>
</div>

View file

@ -1,12 +1,12 @@
import { useEffect, useRef, useState } from "react";
import { ExclamationIcon } from "@heroicons/react/solid";
import {useEffect, useRef, useState} from "react";
import {ExclamationTriangleIcon} from "@heroicons/react/24/solid";
import format from "date-fns/format";
import { DebounceInput } from "react-debounce-input";
import {DebounceInput} from "react-debounce-input";
import { APIClient } from "../api/APIClient";
import { Checkbox } from "../components/Checkbox";
import { classNames } from "../utils";
import { SettingsContext } from "../utils/Context";
import {APIClient} from "../api/APIClient";
import {Checkbox} from "../components/Checkbox";
import {classNames} from "../utils";
import {SettingsContext} from "../utils/Context";
type LogEvent = {
time: string;
@ -79,7 +79,7 @@ export const Logs = () => {
<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>
<div className="flex justify-center">
<ExclamationIcon
<ExclamationTriangleIcon
className="h-5 w-5 text-yellow-400"
aria-hidden="true"
/>

View file

@ -1,15 +1,15 @@
import { NavLink, Outlet, useLocation } from "react-router-dom";
import {NavLink, Outlet, useLocation} from "react-router-dom";
import {
BellIcon,
ChatAlt2Icon,
ChatBubbleLeftRightIcon,
CogIcon,
CollectionIcon,
DownloadIcon,
FolderArrowDownIcon,
KeyIcon,
RectangleStackIcon,
RssIcon
} from "@heroicons/react/outline";
} from "@heroicons/react/24/outline";
import { classNames } from "../utils";
import {classNames} from "../utils";
interface NavTabType {
name: string;
@ -20,12 +20,12 @@ interface NavTabType {
const subNavigation: NavTabType[] = [
{ name: "Application", href: "", icon: CogIcon },
{ name: "Indexers", href: "indexers", icon: KeyIcon },
{ name: "IRC", href: "irc", icon: ChatAlt2Icon },
{ name: "IRC", href: "irc", icon: ChatBubbleLeftRightIcon },
{ name: "Feeds", href: "feeds", icon: RssIcon },
{ name: "Clients", href: "clients", icon: DownloadIcon },
{ name: "Clients", href: "clients", icon: FolderArrowDownIcon },
{ name: "Notifications", href: "notifications", icon: BellIcon },
{ name: "API keys", href: "api-keys", icon: KeyIcon },
{ name: "Releases", href: "releases", icon: CollectionIcon }
{ name: "Releases", href: "releases", icon: RectangleStackIcon }
// {name: 'Regex Playground', href: 'regex-playground', icon: CogIcon, current: false}
// {name: 'Rules', href: 'rules', icon: ClipboardCheckIcon, current: false},
];

View file

@ -1,17 +1,17 @@
import { AlertWarning } from "../../components/alerts";
import { DownloadClientSelect, NumberField, Select, SwitchGroup, TextField } from "../../components/inputs";
import { ActionContentLayoutOptions, ActionTypeNameMap, ActionTypeOptions } from "../../domain/constants";
import React, { Fragment, useRef } from "react";
import { useQuery } from "react-query";
import { APIClient } from "../../api/APIClient";
import { Field, FieldArray, FieldProps, FormikValues } from "formik";
import { EmptyListState } from "../../components/emptystates";
import { useToggle } from "../../hooks/hooks";
import { classNames } from "../../utils";
import { Dialog, Switch as SwitchBasic, Transition } from "@headlessui/react";
import { ChevronRightIcon } from "@heroicons/react/solid";
import { DeleteModal } from "../../components/modals";
import { CollapsableSection } from "./details";
import {AlertWarning} from "../../components/alerts";
import {DownloadClientSelect, NumberField, Select, SwitchGroup, TextField} from "../../components/inputs";
import {ActionContentLayoutOptions, ActionTypeNameMap, ActionTypeOptions} from "../../domain/constants";
import React, {Fragment, useRef} from "react";
import {useQuery} from "react-query";
import {APIClient} from "../../api/APIClient";
import {Field, FieldArray, FieldProps, FormikValues} from "formik";
import {EmptyListState} from "../../components/emptystates";
import {useToggle} from "../../hooks/hooks";
import {classNames} from "../../utils";
import {Dialog, Switch as SwitchBasic, Transition} from "@headlessui/react";
import {ChevronRightIcon} from "@heroicons/react/24/solid";
import {DeleteModal} from "../../components/modals";
import {CollapsableSection} from "./details";
interface FilterActionsProps {
filter: Filter;

View file

@ -1,9 +1,9 @@
import React, { useRef } from "react";
import { useMutation, useQuery } from "react-query";
import { NavLink, Route, Routes, useLocation, useNavigate, useParams } from "react-router-dom";
import { toast } from "react-hot-toast";
import { Form, Formik, FormikValues, useFormikContext } from "formik";
import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/solid";
import React, {useRef} from "react";
import {useMutation, useQuery} from "react-query";
import {NavLink, Route, Routes, useLocation, useNavigate, useParams} from "react-router-dom";
import {toast} from "react-hot-toast";
import {Form, Formik, FormikValues, useFormikContext} from "formik";
import {ChevronDownIcon, ChevronRightIcon} from "@heroicons/react/24/solid";
import {
CODECS_OPTIONS,
@ -19,10 +19,10 @@ import {
SOURCES_MUSIC_OPTIONS,
SOURCES_OPTIONS
} from "../../domain/constants";
import { queryClient } from "../../App";
import { APIClient } from "../../api/APIClient";
import { useToggle } from "../../hooks/hooks";
import { classNames } from "../../utils";
import {queryClient} from "../../App";
import {APIClient} from "../../api/APIClient";
import {useToggle} from "../../hooks/hooks";
import {classNames} from "../../utils";
import {
CheckboxField,
@ -35,10 +35,10 @@ import {
} from "../../components/inputs";
import DEBUG from "../../components/debug";
import Toast from "../../components/notifications/Toast";
import { DeleteModal } from "../../components/modals";
import { TitleSubtitle } from "../../components/headings";
import { TextArea } from "../../components/inputs/input";
import { FilterActions } from "./action";
import {DeleteModal} from "../../components/modals";
import {TitleSubtitle} from "../../components/headings";
import {TextArea} from "../../components/inputs/input";
import {FilterActions} from "./action";
interface tabType {
name: string;

View file

@ -1,26 +1,26 @@
import { Dispatch, FC, Fragment, MouseEventHandler, useReducer, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-hot-toast";
import { Listbox, Menu, Switch, Transition } from "@headlessui/react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import {Dispatch, FC, Fragment, MouseEventHandler, useReducer, useRef, useState} from "react";
import {Link} from "react-router-dom";
import {toast} from "react-hot-toast";
import {Listbox, Menu, Switch, Transition} from "@headlessui/react";
import {useMutation, useQuery, useQueryClient} from "react-query";
import {
ArrowsRightLeftIcon,
CheckIcon,
ChevronDownIcon,
DotsHorizontalIcon,
DuplicateIcon,
PencilAltIcon,
SwitchHorizontalIcon,
DocumentDuplicateIcon,
EllipsisHorizontalIcon,
PencilSquareIcon,
TrashIcon
} from "@heroicons/react/outline";
} from "@heroicons/react/24/outline";
import { queryClient } from "../../App";
import { classNames } from "../../utils";
import { FilterAddForm } from "../../forms";
import { useToggle } from "../../hooks/hooks";
import { APIClient } from "../../api/APIClient";
import {queryClient} from "../../App";
import {classNames} from "../../utils";
import {FilterAddForm} from "../../forms";
import {useToggle} from "../../hooks/hooks";
import {APIClient} from "../../api/APIClient";
import Toast from "../../components/notifications/Toast";
import { EmptyListState } from "../../components/emptystates";
import { DeleteModal } from "../../components/modals";
import {EmptyListState} from "../../components/emptystates";
import {DeleteModal} from "../../components/modals";
type FilterListState = {
indexerFilter: string[],
@ -250,7 +250,7 @@ const FilterItemDropdown = ({
text="Are you sure you want to remove this filter? This action cannot be undone."
/>
<Menu.Button className="px-4 py-2">
<DotsHorizontalIcon
<EllipsisHorizontalIcon
className="w-5 h-5 text-gray-700 hover:text-gray-900 dark:text-gray-100 dark:hover:text-gray-400"
aria-hidden="true"
/>
@ -277,7 +277,7 @@ const FilterItemDropdown = ({
"font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"
)}
>
<PencilAltIcon
<PencilSquareIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"
@ -297,7 +297,7 @@ const FilterItemDropdown = ({
)}
onClick={() => onToggle(!filter.enabled)}
>
<SwitchHorizontalIcon
<ArrowsRightLeftIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"
@ -317,7 +317,7 @@ const FilterItemDropdown = ({
)}
onClick={() => duplicateMutation.mutate(filter.id)}
>
<DuplicateIcon
<DocumentDuplicateIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"

View file

@ -1,16 +1,13 @@
import * as React from "react";
import { useQuery } from "react-query";
import { Listbox, Transition } from "@headlessui/react";
import {
CheckIcon,
ChevronDownIcon
} from "@heroicons/react/solid";
import {useQuery} from "react-query";
import {Listbox, Transition} from "@headlessui/react";
import {CheckIcon, ChevronDownIcon} from "@heroicons/react/24/solid";
import { APIClient } from "../../api/APIClient";
import { classNames } from "../../utils";
import { PushStatusOptions } from "../../domain/constants";
import { FilterProps } from "react-table";
import { DebounceInput } from "react-debounce-input";
import {APIClient} from "../../api/APIClient";
import {classNames} from "../../utils";
import {PushStatusOptions} from "../../domain/constants";
import {FilterProps} from "react-table";
import {DebounceInput} from "react-debounce-input";
interface ListboxFilterProps {
id: string;

View file

@ -1,29 +1,20 @@
import * as React from "react";
import { useQuery } from "react-query";
import {
useTable,
useSortBy,
usePagination,
useFilters,
Column
} from "react-table";
import {useQuery} from "react-query";
import {Column, useFilters, usePagination, useSortBy, useTable} from "react-table";
import {
ChevronDoubleLeftIcon,
ChevronDoubleRightIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronDoubleRightIcon
} from "@heroicons/react/solid";
ChevronRightIcon
} from "@heroicons/react/24/solid";
import { APIClient } from "../../api/APIClient";
import { EmptyListState } from "../../components/emptystates";
import {APIClient} from "../../api/APIClient";
import {EmptyListState} from "../../components/emptystates";
import * as Icons from "../../components/Icons";
import * as DataTable from "../../components/data-table";
import {
IndexerSelectColumnFilter,
PushStatusSelectColumnFilter, SearchColumnFilter
} from "./Filters";
import {IndexerSelectColumnFilter, PushStatusSelectColumnFilter, SearchColumnFilter} from "./Filters";
type TableState = {
queryPageIndex: number;

View file

@ -1,16 +1,16 @@
import { queryClient } from "../../App";
import { useRef } from "react";
import { useMutation, useQuery } from "react-query";
import { KeyField } from "../../components/fields/text";
import { DeleteModal } from "../../components/modals";
import {queryClient} from "../../App";
import {useRef} from "react";
import {useMutation, useQuery} from "react-query";
import {KeyField} from "../../components/fields/text";
import {DeleteModal} from "../../components/modals";
import APIKeyAddForm from "../../forms/settings/APIKeyAddForm";
import Toast from "../../components/notifications/Toast";
import { APIClient } from "../../api/APIClient";
import { useToggle } from "../../hooks/hooks";
import { toast } from "react-hot-toast";
import { classNames } from "../../utils";
import { TrashIcon } from "@heroicons/react/outline";
import { EmptySimple } from "../../components/emptystates";
import {APIClient} from "../../api/APIClient";
import {useToggle} from "../../hooks/hooks";
import {toast} from "react-hot-toast";
import {classNames} from "../../utils";
import {TrashIcon} from "@heroicons/react/24/outline";
import {EmptySimple} from "../../components/emptystates";
function APISettings() {
const [addFormIsOpen, toggleAddForm] = useToggle(false);

View file

@ -1,23 +1,18 @@
import { useToggle } from "../../hooks/hooks";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { APIClient } from "../../api/APIClient";
import { Menu, Switch, Transition } from "@headlessui/react";
import {useToggle} from "../../hooks/hooks";
import {useMutation, useQuery, useQueryClient} from "react-query";
import {APIClient} from "../../api/APIClient";
import {Menu, Switch, Transition} from "@headlessui/react";
import { classNames } from "../../utils";
import { Fragment, useRef, useState } from "react";
import { toast } from "react-hot-toast";
import {classNames} from "../../utils";
import {Fragment, useRef, useState} from "react";
import {toast} from "react-hot-toast";
import Toast from "../../components/notifications/Toast";
import { queryClient } from "../../App";
import { DeleteModal } from "../../components/modals";
import {
DotsHorizontalIcon,
PencilAltIcon,
SwitchHorizontalIcon,
TrashIcon
} from "@heroicons/react/outline";
import { FeedUpdateForm } from "../../forms/settings/FeedForms";
import { EmptySimple } from "../../components/emptystates";
import { ImplementationBadges } from "./Indexer";
import {queryClient} from "../../App";
import {DeleteModal} from "../../components/modals";
import {ArrowsRightLeftIcon, EllipsisHorizontalIcon, PencilSquareIcon, TrashIcon} from "@heroicons/react/24/outline";
import {FeedUpdateForm} from "../../forms/settings/FeedForms";
import {EmptySimple} from "../../components/emptystates";
import {ImplementationBadges} from "./Indexer";
function FeedSettings() {
const { data } = useQuery(
@ -183,7 +178,7 @@ const FeedItemDropdown = ({
text="Are you sure you want to remove this feed? This action cannot be undone."
/>
<Menu.Button className="px-4 py-2">
<DotsHorizontalIcon
<EllipsisHorizontalIcon
className="w-5 h-5 text-gray-700 hover:text-gray-900 dark:text-gray-100 dark:hover:text-gray-400"
aria-hidden="true"
/>
@ -210,7 +205,7 @@ const FeedItemDropdown = ({
)}
onClick={() => toggleUpdate()}
>
<PencilAltIcon
<PencilSquareIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"
@ -230,7 +225,7 @@ const FeedItemDropdown = ({
)}
onClick={() => onToggle(!feed.enabled)}
>
<SwitchHorizontalIcon
<ArrowsRightLeftIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"

View file

@ -1,14 +1,28 @@
import { useQuery } from "react-query";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { simplifyDate, IsEmptyDate, classNames } from "../../utils";
import { classNames, IsEmptyDate, simplifyDate } from "../../utils";
import { IrcNetworkAddForm, IrcNetworkUpdateForm } from "../../forms";
import { useToggle } from "../../hooks/hooks";
import { APIClient } from "../../api/APIClient";
import { EmptySimple } from "../../components/emptystates";
import { ExclamationCircleIcon } from "@heroicons/react/outline";
import { LockClosedIcon, LockOpenIcon } from "@heroicons/react/solid";
import { LockClosedIcon, LockOpenIcon } from "@heroicons/react/24/solid";
import { Menu, Transition } from "@headlessui/react";
import { Fragment, useRef } from "react";
import { DeleteModal } from "../../components/modals";
import { toast } from "react-hot-toast";
import Toast from "../../components/notifications/Toast";
import {
ArrowsPointingInIcon,
ArrowsPointingOutIcon,
EllipsisHorizontalIcon,
ExclamationCircleIcon,
PencilSquareIcon,
TrashIcon
} from "@heroicons/react/24/outline";
export const IrcSettings = () => {
const [expandNetworks, toggleExpand] = useToggle(false);
const [addNetworkIsOpen, toggleAddNetwork] = useToggle(false);
const { data } = useQuery("networks", () => APIClient.irc.getNetworks(), {
@ -42,23 +56,63 @@ export const IrcSettings = () => {
</button>
</div>
</div>
<div className="flex justify-between flex-col sm:flex-row mt-10 px-1">
<ol className="flex flex-col sm:flex-row sm:gap-2 pb-4 sm:pb-0 sm:divide-x sm:divide-gray-200 sm:dark:divide-gray-700">
<li className="flex items-center">
<span
className="mr-2 flex h-4 w-4 relative"
title="Network healthy"
>
<span className="animate-ping inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
<span className="inline-flex absolute rounded-full h-4 w-4 bg-green-500" />
</span>
<span className="text-sm text-gray-800 dark:text-gray-500">Network healthy</span>
</li>
<li className="flex items-center sm:pl-2">
<span
className="mr-2 flex h-4 w-4 rounded-full opacity-75 bg-yellow-400 over:text-yellow-600"
title="Network unhealthy"
/>
<span className="text-sm text-gray-800 dark:text-gray-500">Network unhealthy</span>
</li>
<li className="flex items-center sm:pl-2">
<span
className="mr-2 flex h-4 w-4 rounded-full opacity-75 bg-gray-500"
title="Network disabled"
>
</span>
<span className="text-sm text-gray-800 dark:text-gray-500">Network disabled</span>
</li>
</ol>
<div className="flex gap-x-2">
<button className="flex items-center text-sm text-gray-800 dark:text-gray-400 p-1 px-2 rounded shadow bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600" onClick={toggleExpand} title={expandNetworks ? "collapse" : "expand"}>
{expandNetworks
? <span className="flex items-center">Collapse <ArrowsPointingInIcon className="ml-1 w-4 h-4"/></span>
: <span className="flex items-center">Expand <ArrowsPointingOutIcon className="ml-1 w-4 h-4"/></span>
}</button>
</div>
</div>
{data && data.length > 0 ? (
<section className="mt-6 light:bg-white dark:bg-gray-800 light:shadow sm:rounded-md">
<ol className="min-w-full">
<ol className="min-w-full relative">
<li className="grid grid-cols-12 gap-4 border-b border-gray-200 dark:border-gray-700">
<div className="col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Network
</div>
<div className="col-span-5 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
<div className="hidden sm:flex col-span-5 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Server
</div>
<div className="col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
<div className="hidden sm:flex col-span-3 px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Nick
</div>
</li>
{data &&
data.map((network, idx) => (
<ListItem key={idx} idx={idx} network={network} />
<ListItem key={idx} idx={idx} expanded={expandNetworks} network={network} />
))}
</ol>
</section>
@ -78,15 +132,16 @@ export const IrcSettings = () => {
interface ListItemProps {
idx: number;
network: IrcNetworkWithHealth;
expanded: boolean;
}
const ListItem = ({ idx, network }: ListItemProps) => {
const ListItem = ({ idx, network, expanded }: ListItemProps) => {
const [updateIsOpen, toggleUpdate] = useToggle(false);
const [edit, toggleEdit] = useToggle(false);
return (
<li key={idx}>
<div className={classNames("grid grid-cols-12 gap-2 lg:gap-4 items-center py-4", network.enabled && !network.healthy ? "bg-red-50 dark:bg-red-900 hover:bg-red-100 dark:hover:bg-red-800" : "hover:bg-gray-50 dark:hover:bg-gray-700 ")}>
<div className={classNames("grid grid-cols-12 gap-2 lg:gap-4 items-center py-2", network.enabled && !network.healthy ? "bg-red-50 dark:bg-red-900 hover:bg-red-100 dark:hover:bg-red-800" : "hover:bg-gray-50 dark:hover:bg-gray-700 ")}>
<IrcNetworkUpdateForm
isOpen={updateIsOpen}
toggle={toggleUpdate}
@ -94,7 +149,7 @@ const ListItem = ({ idx, network }: ListItemProps) => {
/>
<div
className="col-span-3 items-center sm:px-6 text-sm font-medium text-gray-900 dark:text-white cursor-pointer"
className="col-span-10 xs:col-span-3 sm:col-span-3 items-center sm:px-6 text-sm font-medium text-gray-900 dark:text-white cursor-pointer"
onClick={toggleEdit}
>
<div className="flex">
@ -126,7 +181,7 @@ const ListItem = ({ idx, network }: ListItemProps) => {
</div>
</div>
<div
className="col-span-5 sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
className="hidden sm:flex col-span-5 sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
onClick={toggleEdit}
>
<div
@ -155,7 +210,7 @@ const ListItem = ({ idx, network }: ListItemProps) => {
</div>
{network.nickserv && network.nickserv.account ? (
<div
className="col-span-3 items-center sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
className="hidden sm:flex col-span-3 items-center sm:px-6 text-sm text-gray-500 dark:text-gray-400 cursor-pointer"
onClick={toggleEdit}
>
<div className="overflow-x-auto flex">
@ -166,15 +221,10 @@ const ListItem = ({ idx, network }: ListItemProps) => {
<div className="col-span-3" />
)}
<div className="col-span-1 text-sm text-gray-500 dark:text-gray-400">
<span
className="text-indigo-600 dark:text-gray-300 hover:text-indigo-900 cursor-pointer"
onClick={toggleUpdate}
>
Edit
</span>
<ListItemDropdown network={network} toggleUpdate={toggleUpdate} />
</div>
</div>
{edit && (
{(edit || expanded) && (
<div className="px-4 py-4 flex border-b border-x-0 dark:border-gray-600 dark:bg-gray-700">
<div className="min-w-full">
{network.channels.length > 0 ? (
@ -238,3 +288,168 @@ const ListItem = ({ idx, network }: ListItemProps) => {
</li>
);
};
interface ListItemDropdownProps {
network: IrcNetwork;
toggleUpdate: () => void;
}
const ListItemDropdown = ({
network,
toggleUpdate
}: ListItemDropdownProps) => {
const cancelModalButtonRef = useRef(null);
const queryClient = useQueryClient();
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
const deleteMutation = useMutation(
(id: number) => APIClient.irc.deleteNetwork(id),
{
onSuccess: () => {
queryClient.invalidateQueries(["networks"]);
queryClient.invalidateQueries(["networks", network.id]);
toast.custom((t) => <Toast type="success" body={`Network ${network.name} was deleted`} t={t}/>);
}
}
);
const restartMutation = useMutation(
(id: number) => APIClient.irc.restartNetwork(id),
{
onSuccess: () => {
toast.custom((t) => <Toast type="success"
body={`${network.name} was successfully restarted`}
t={t}/>);
queryClient.invalidateQueries(["networks"]);
queryClient.invalidateQueries(["networks", network.id]);
}
}
);
const restart = (id: number) => {
restartMutation.mutate(id);
};
return (
<Menu as="div">
<DeleteModal
isOpen={deleteModalIsOpen}
toggle={toggleDeleteModal}
buttonRef={cancelModalButtonRef}
deleteAction={() => {
deleteMutation.mutate(network.id);
toggleDeleteModal();
}}
title={`Remove network: ${network.name}`}
text="Are you sure you want to remove this network? This action cannot be undone."
/>
<Menu.Button className="px-4 py-2">
<EllipsisHorizontalIcon
className="w-5 h-5 text-gray-700 hover:text-gray-900 dark:text-gray-100 dark:hover:text-gray-400"
aria-hidden="true"
/>
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="absolute right-0 w-32 sm:w-56 mt-2 origin-top-right bg-white dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700 rounded-md shadow-lg ring-1 ring-black ring-opacity-10 focus:outline-none"
>
<div className="px-1 py-1">
<Menu.Item>
{({ active }) => (
<button
className={classNames(
active ? "bg-blue-600 text-white" : "text-gray-900 dark:text-gray-300",
"font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"
)}
onClick={() => toggleUpdate()}
>
<PencilSquareIcon
className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"
)}
aria-hidden="true"
/>
Edit
</button>
)}
</Menu.Item>
{/*<Menu.Item>*/}
{/* {({ active }) => (*/}
{/* <button*/}
{/* className={classNames(*/}
{/* active ? "bg-blue-600 text-white" : "text-gray-900 dark:text-gray-300",*/}
{/* "font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"*/}
{/* )}*/}
{/* onClick={() => onToggle(!network.enabled)}*/}
{/* >*/}
{/* <SwitchHorizontalIcon*/}
{/* className={classNames(*/}
{/* active ? "text-white" : "text-blue-500",*/}
{/* "w-5 h-5 mr-2"*/}
{/* )}*/}
{/* aria-hidden="true"*/}
{/* />*/}
{/* {network.enabled ? "Disable" : "Enable"}*/}
{/* </button>*/}
{/* )}*/}
{/*</Menu.Item>*/}
<Menu.Item>
{({ active }) => (
<button
className={classNames(
active ? "bg-blue-600 text-white" : "text-gray-900 dark:text-gray-300",
"font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"
)}
onClick={() => restart(network.id)}
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className={classNames(
active ? "text-white" : "text-blue-500",
"w-5 h-5 mr-2"
)}>
<path strokeLinecap="round" strokeLinejoin="round" d="M5.636 5.636a9 9 0 1012.728 0M12 3v9" />
</svg>
Restart
</button>
)}
</Menu.Item>
</div>
<div className="px-1 py-1">
<Menu.Item>
{({ active }) => (
<button
className={classNames(
active ? "bg-red-600 text-white" : "text-gray-900 dark:text-gray-300",
"font-medium group flex rounded-md items-center w-full px-2 py-2 text-sm"
)}
onClick={() => toggleDeleteModal()}
>
<TrashIcon
className={classNames(
active ? "text-white" : "text-red-500",
"w-5 h-5 mr-2"
)}
aria-hidden="true"
/>
Delete
</button>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</Menu>
);
};