Skip to content

Commit

Permalink
Merge pull request #7 from riverqueue/bg-tweak-job-list-states
Browse files Browse the repository at this point in the history
Tweak job list states, selectable refresh interval
  • Loading branch information
bgentry authored May 20, 2024
2 parents b444349 + 84ee9f5 commit ceca356
Show file tree
Hide file tree
Showing 12 changed files with 158 additions and 59 deletions.
2 changes: 1 addition & 1 deletion ui/src/components/JobList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ const JobList = (props: JobListProps) => {
params={{}}
>
<span className="">{item.name}</span>
<span className="col-span-2 ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-white px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200 dark:bg-gray-900 dark:text-white dark:ring-gray-700">
<span className="col-span-4 ml-auto w-9 min-w-max whitespace-nowrap rounded-full bg-white px-2.5 py-0.5 text-center text-xs font-medium leading-5 text-gray-600 ring-1 ring-inset ring-gray-200 dark:bg-gray-900 dark:text-white dark:ring-gray-700">
{item.count.toString()}
</span>
</DropdownItem>
Expand Down
127 changes: 110 additions & 17 deletions ui/src/components/RefreshPauser.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,117 @@
import { PauseIcon } from "@heroicons/react/24/outline";
import { useEffect, useState } from "react";
import {
Label,
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from "@headlessui/react";
import clsx from "clsx";

import { useRefreshSetting } from "@contexts/RefreshSettings.hook";
import { ArrowPathIcon, PauseIcon } from "@heroicons/react/24/outline";
import { ArrowPathIcon } from "@heroicons/react/20/solid";

type RefreshIntervalSetting = {
name: string;
value: number;
};

const refreshIntervals: RefreshIntervalSetting[] = [
{ name: "Pause", value: 0 },
{ name: "2s", value: 2000 },
{ name: "5s", value: 5000 },
{ name: "10s", value: 10000 },
{ name: "30s", value: 30000 },
{ name: "60s", value: 60000 },
];

export function RefreshPauser(
props: React.ComponentPropsWithoutRef<
typeof Listbox<"div", RefreshIntervalSetting>
>
) {
const { intervalMs, setIntervalMs } = useRefreshSetting();
const [mounted, setMounted] = useState(false);

useEffect(() => {
setMounted(true);
}, []);

if (!mounted) {
return <div className="size-6" />;
}

export function RefreshPauser(_props: React.ComponentPropsWithoutRef<"div">) {
const { disabled, setDisabled } = useRefreshSetting();
const disabled = intervalMs === 0;
const selectedInterval =
refreshIntervals.find((i) => i.value === intervalMs) ??
({ name: "Custom", value: intervalMs } as RefreshIntervalSetting);

return (
<button
type="button"
className="-m-2.5 p-2.5 text-gray-400 hover:text-gray-500"
onClick={() => setDisabled(!disabled)}
title={disabled ? "Resume live updates" : "Pause live updates"}
<Listbox
as="div"
value={selectedInterval}
by="value"
onChange={(newInterval) => setIntervalMs(newInterval.value)}
{...props}
>
<span className="sr-only">
{disabled ? "Resume live updates" : "Pause live updates"}
</span>
{disabled ? (
<ArrowPathIcon className="size-6" aria-hidden="true" />
) : (
<PauseIcon className="size-6" aria-hidden="true" />
)}
</button>
<Label className="sr-only">
{" "}
{disabled ? "Resume live updates" : "Pause live updates"}{" "}
</Label>
<ListboxButton
className="relative z-10 flex size-7 items-center justify-center rounded-lg text-slate-700 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100"
aria-label="Theme"
// type="button"
// className="-m-2.5 p-2.5 text-gray-400 hover:text-gray-500"
// onClick={() => setDisabled(!disabled)}
// title={disabled ? "Resume live updates" : "Pause live updates"}
>
<span className="sr-only">
{disabled ? "Resume live updates" : "Pause live updates"}
</span>
{disabled ? (
<PauseIcon className="size-6 text-slate-400" aria-hidden="true" />
) : (
<ArrowPathIcon
className="size-6 text-slate-400 motion-safe:animate-spin-50-50"
aria-hidden="true"
/>
)}
</ListboxButton>
<ListboxOptions className="absolute right-0 top-full mt-3 w-32 space-y-1 rounded-xl bg-white p-3 text-sm font-medium shadow-md shadow-black/5 ring-1 ring-black/5 dark:bg-slate-800 dark:ring-white/5">
<div className="px-2 text-xs font-semibold leading-6 text-slate-500">
Live Updates
</div>
{refreshIntervals.map((intervalSetting) => (
<ListboxOption
key={intervalSetting.value}
value={intervalSetting}
className={({ focus, selected }) =>
clsx(
"flex cursor-pointer select-none rounded-[0.625rem] px-2 py-1",
{
"text-blue-600 dark:text-blue-400": selected,
"text-slate-900 dark:text-white": focus && !selected,
"text-slate-700 dark:text-slate-300": !focus && !selected,
"bg-slate-100 dark:bg-slate-700": focus,
}
)
}
>
{({ selected }) => (
<div
className={clsx({
"text-blue-600 dark:text-blue-400": selected,
"text-slate-600 dark:text-slate-300": !selected,
})}
>
{intervalSetting.name}
</div>
)}
</ListboxOption>
))}
</ListboxOptions>
</Listbox>
);
}
36 changes: 21 additions & 15 deletions ui/src/components/ThemeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { useEffect, useState } from "react";
import { useTheme } from "next-themes";
import { Listbox } from "@headlessui/react";
import {
Label,
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
} from "@headlessui/react";
import clsx from "clsx";

const themes = [
Expand Down Expand Up @@ -61,8 +67,8 @@ export function ThemeSelector(

return (
<Listbox as="div" value={theme} onChange={setTheme} {...props}>
<Listbox.Label className="sr-only">Theme</Listbox.Label>
<Listbox.Button
<Label className="sr-only">Theme</Label>
<ListboxButton
className="relative z-10 flex size-7 items-center justify-center rounded-lg text-slate-700 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-slate-100"
aria-label="Theme"
>
Expand All @@ -78,20 +84,20 @@ export function ThemeSelector(
theme === "system" ? "fill-slate-400" : "fill-sky-400"
)}
/>
</Listbox.Button>
<Listbox.Options className="absolute right-0 top-full mt-3 w-36 space-y-1 rounded-xl bg-white p-3 text-sm font-medium shadow-md shadow-black/5 ring-1 ring-black/5 dark:bg-slate-800 dark:ring-white/5">
</ListboxButton>
<ListboxOptions className="absolute right-0 top-full mt-3 w-36 space-y-1 rounded-xl bg-white p-3 text-sm font-medium shadow-md shadow-black/5 ring-1 ring-black/5 dark:bg-slate-800 dark:ring-white/5">
{themes.map((theme) => (
<Listbox.Option
<ListboxOption
key={theme.value}
value={theme.value}
className={({ active, selected }) =>
className={({ focus, selected }) =>
clsx(
"flex cursor-pointer select-none items-center rounded-[0.625rem] p-1",
{
"text-brand-primary dark:text-blue-400": selected,
"text-slate-900 dark:text-white": active && !selected,
"text-slate-700 dark:text-slate-300": !active && !selected,
"bg-slate-100 dark:bg-slate-900/40": active,
"text-blue-600 dark:text-blue-400": selected,
"text-slate-900 dark:text-white": focus && !selected,
"text-slate-700 dark:text-slate-300": !focus && !selected,
"bg-slate-100 dark:bg-slate-700": focus,
}
)
}
Expand All @@ -103,17 +109,17 @@ export function ThemeSelector(
className={clsx(
"size-4",
selected
? "fill-sky-400 dark:fill-sky-400"
: "fill-slate-400"
? "fill-blue-600 dark:fill-blue-400"
: "fill-slate-600 dark:fill-slate-400"
)}
/>
</div>
<div className="ml-3">{theme.name}</div>
</>
)}
</Listbox.Option>
</ListboxOption>
))}
</Listbox.Options>
</ListboxOptions>
</Listbox>
);
}
2 changes: 1 addition & 1 deletion ui/src/components/TopNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const TopNav = ({ children }: TopNavProps) => {
className="hidden lg:block lg:h-6 lg:w-px lg:bg-slate-200 dark:lg:bg-slate-700"
aria-hidden="true"
/>
<RefreshPauser />
<RefreshPauser className="relative z-10" />
<ThemeSelector className="relative z-10" />
</div>
</div>
Expand Down
2 changes: 0 additions & 2 deletions ui/src/contexts/RefreshSettings.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import {
} from "./RefreshSettings";

const defaultContext: UseRefreshSettingProps = {
disabled: false,
intervalMs: 2000,
setDisabled: () => {},
setIntervalMs: () => {},
};

Expand Down
10 changes: 2 additions & 8 deletions ui/src/contexts/RefreshSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { Fragment, createContext, useContext, useMemo, useState } from "react";

export interface UseRefreshSettingProps {
disabled: boolean;
/** List of all available theme names */
intervalMs: number;
/** Update the disabled setting */
setDisabled: React.Dispatch<React.SetStateAction<boolean>>;
/** Update the interval setting */
/** Update the interval setting. Set to 0 to disable refresh. */
setIntervalMs: React.Dispatch<React.SetStateAction<number>>;
}

Expand All @@ -31,17 +28,14 @@ export const RefreshSettingProvider: React.FC<RefreshSettingProviderProps> = (
const RefreshSetting: React.FC<RefreshSettingProviderProps> = ({
children,
}) => {
const [disabled, setDisabled] = useState(false);
const [intervalMs, setIntervalMs] = useState(2000);

const providerValue = useMemo(
() => ({
disabled,
intervalMs,
setDisabled,
setIntervalMs,
}),
[disabled, intervalMs, setDisabled, setIntervalMs]
[intervalMs, setIntervalMs]
);

return (
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/jobs/$jobId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ function JobComponent() {
const { jobId } = Route.useParams();
const { queryOptions } = Route.useRouteContext();
const refreshSettings = useRefreshSetting();
queryOptions.refetchInterval = !refreshSettings.disabled
? refreshSettings.intervalMs
: 0;
queryOptions.refetchInterval = refreshSettings.intervalMs;

const queryClient = useQueryClient();
const jobQuery = useQuery(queryOptions);
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/jobs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ function JobsIndexComponent() {
const navigate = Route.useNavigate();
const { limit } = Route.useLoaderDeps();
const refreshSettings = useRefreshSetting();
const refetchInterval = !refreshSettings.disabled
? refreshSettings.intervalMs
: 0;
const refetchInterval = refreshSettings.intervalMs;

const jobsQuery = useSuspenseQuery(
jobsQueryOptions(Route.useLoaderDeps(), { refetchInterval })
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/queues/$name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ function QueueComponent() {
const { name } = Route.useParams();
const { queryOptions } = Route.useRouteContext();
const refreshSettings = useRefreshSetting();
queryOptions.refetchInterval = !refreshSettings.disabled
? refreshSettings.intervalMs
: 0;
queryOptions.refetchInterval = refreshSettings.intervalMs;

const queueQuery = useQuery(queryOptions);
const { data: queue } = queueQuery;
Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/queues/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ export const Route = createFileRoute("/queues/")({
function QueuesIndexComponent() {
const { queryOptions } = Route.useRouteContext();
const refreshSettings = useRefreshSetting();
queryOptions.refetchInterval = !refreshSettings.disabled
? refreshSettings.intervalMs
: 0;
queryOptions.refetchInterval = refreshSettings.intervalMs;

const queryClient = useQueryClient();

Expand Down
4 changes: 1 addition & 3 deletions ui/src/routes/workflows/$workflowId.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ export const Route = createFileRoute("/workflows/$workflowId")({
function WorkflowComponent() {
const { queryOptions } = Route.useRouteContext();
const refreshSettings = useRefreshSetting();
queryOptions.refetchInterval = !refreshSettings.disabled
? refreshSettings.intervalMs
: 0;
queryOptions.refetchInterval = refreshSettings.intervalMs;

const workflowQuery = useQuery(queryOptions);

Expand Down
18 changes: 18 additions & 0 deletions ui/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,30 @@ export default {
darkMode: "class",
theme: {
extend: {
animation: {
"spin-slow": "spin 3s linear infinite",
"spin-50-50": "pausingSpin 6s infinite",
},
colors: {
"brand-primary": "rgb(37, 99, 235)",
},
fontFamily: {
sans: ["Inter var", ...defaultTheme.fontFamily.sans],
},
keyframes: {
pausingSpin: {
"0%": {
transform: "rotate(0)",
animationTimingFunction: "ease-in-out",
},
"42%,50%": { transform: "rotate(180deg)" },
"50%": {
transform: "rotate(180deg)",
animationTimingFunction: "ease-in-out",
},
"92%,100%": { transform: "rotate(360deg)" },
},
},
},
},
plugins: [formsPlugin, typographyPlugin, headlessuiPlugin],
Expand Down

0 comments on commit ceca356

Please sign in to comment.