Skip to content

Queues page and pausing the environment/queues #1805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
9a77709
Changed CLI clipboard fields to secondary
matt-aitken Mar 17, 2025
2ec76d6
Ignore .husky
matt-aitken Mar 17, 2025
d5561b5
Disable vscode warning about vitest
matt-aitken Mar 17, 2025
3fd4470
Fix for SSE error when trying to send when the controller has been ab…
matt-aitken Mar 17, 2025
fabfb50
WIP on queues page
matt-aitken Mar 17, 2025
a871137
WIP on queues page
matt-aitken Mar 17, 2025
6539c4a
Added pagination
matt-aitken Mar 17, 2025
c6fd181
Current badge changed
matt-aitken Mar 17, 2025
d5ee760
Get values for the queue and running
matt-aitken Mar 17, 2025
bbe30ea
Move increase limit button to the new spot
matt-aitken Mar 17, 2025
ec4511b
Add paused status to RuntimeEnvironment and TaskQueue
matt-aitken Mar 18, 2025
43413c1
Reduce the env data that determineEngineVersion needs
matt-aitken Mar 18, 2025
a51f8c5
WIP pausing
matt-aitken Mar 18, 2025
0173406
Pausing the environment is working
matt-aitken Mar 18, 2025
343e7ec
Merge remote-tracking branch 'origin/main' into queues-page
matt-aitken Mar 18, 2025
df53715
Added app version to the org setting menu
matt-aitken Mar 18, 2025
d6beb0c
Separate the presenters out, useful for the SDK
matt-aitken Mar 18, 2025
3a4991d
Loading improvements
matt-aitken Mar 18, 2025
6293933
Show 25 queues per page
matt-aitken Mar 18, 2025
50593eb
Transform the queue type and added tooltips
matt-aitken Mar 18, 2025
323a124
Merge remote-tracking branch 'origin/main' into queues-page
matt-aitken Mar 18, 2025
714e4ec
Added queues.list() SDK function
matt-aitken Mar 18, 2025
b4a2090
Don’t return more than 100 queues per page
matt-aitken Mar 18, 2025
2b8c77d
Added some JSDocs
matt-aitken Mar 18, 2025
4ee85cb
Git ignore the react hooks src/package.json
matt-aitken Mar 19, 2025
e2da181
WIP on retrieving a queue using the SDK/API
matt-aitken Mar 19, 2025
c2038b5
Retrieving a queue is working well
matt-aitken Mar 19, 2025
c74d57f
Fix for type issue
matt-aitken Mar 19, 2025
d8b1144
use TypedAwait
matt-aitken Mar 19, 2025
aa787c2
Queues page promise fix
matt-aitken Mar 19, 2025
57a57b4
InfoBox storybook file
matt-aitken Mar 19, 2025
8ddaa62
WIP on upgrade panel
matt-aitken Mar 19, 2025
34a178f
Added upgrade panel
matt-aitken Mar 19, 2025
42089a4
Pausing individual queues working
matt-aitken Mar 19, 2025
03fb6e2
Redirect to the correct page, to keep your place
matt-aitken Mar 19, 2025
46eed03
Added pause/resume functions to the SDK
matt-aitken Mar 19, 2025
5c9a305
Auto-reload the queue page every 10 seconds
matt-aitken Mar 19, 2025
110548c
Don’t use defer, it causes a horrible UI flash with revalidate
matt-aitken Mar 19, 2025
1d5dadb
Remove unused number-flow package
matt-aitken Mar 19, 2025
99e722c
Do all the environment concurrency lookups in parallel
matt-aitken Mar 19, 2025
16ff502
Better scrolling on the Queues page
matt-aitken Mar 19, 2025
4e3ef9f
Blank state if you have no queues
matt-aitken Mar 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ apps/**/public/build
.yarn
*.tsbuildinfo
/packages/cli-v3/src/package.json
.husky
/packages/react-hooks/src/package.json
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"search.exclude": {
"**/node_modules/**": true,
"packages/cli-v3/e2e": true
}
},
"vitest.disableWorkspaceWarning": true
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 21 additions & 27 deletions apps/webapp/app/components/BlankStatePanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ClockIcon,
PlusIcon,
RectangleGroupIcon,
RectangleStackIcon,
ServerStackIcon,
Squares2X2Icon,
} from "@heroicons/react/20/solid";
Expand Down Expand Up @@ -368,35 +369,28 @@ export function AlertsNoneDeployed() {
);
}

function AlertsNoneProd() {
export function QueuesHasNoTasks() {
const organization = useOrganization();
const project = useProject();
const environment = useEnvironment();

return (
<div className="space-y-8">
<InfoPanel
icon={BellAlertIcon}
iconClassName="text-red-500"
title="Adding alerts"
panelClassName="max-w-full"
<InfoPanel
title="You have no queues"
icon={RectangleStackIcon}
iconClassName="text-purple-500"
panelClassName="max-w-full"
>
<Paragraph spacing variant="small">
This means you haven't got any tasks yet in this environment.
</Paragraph>
<LinkButton
to={v3EnvironmentPath(organization, project, environment)}
variant="tertiary/medium"
>
<Paragraph spacing variant="small">
You can get alerted when deployed runs fail.
</Paragraph>
<Paragraph spacing variant="small">
We don't support alerts in the Development environment. Switch to a deployed environment
to setup alerts.
</Paragraph>
<div className="flex gap-3">
<LinkButton
to={docsPath("troubleshooting-alerts")}
variant="docs/medium"
LeadingIcon={BookOpenIcon}
className="inline-flex"
>
How to setup alerts
</LinkButton>
</div>
</InfoPanel>
<SwitcherPanel />
</div>
Add tasks
</LinkButton>
</InfoPanel>
);
}

Expand Down
2 changes: 0 additions & 2 deletions apps/webapp/app/components/SetupCommands.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { createContext, useContext, useState } from "react";
import { useAppOrigin } from "~/hooks/useAppOrigin";
import { useProject } from "~/hooks/useProject";
import { InlineCode } from "./code/InlineCode";
import {
ClientTabs,
ClientTabsContent,
ClientTabsList,
ClientTabsTrigger,
} from "./primitives/ClientTabs";
import { ClipboardField } from "./primitives/ClipboardField";
import { Paragraph } from "./primitives/Paragraph";
import { Header3 } from "./primitives/Headers";

type PackageManagerContextType = {
Expand Down
18 changes: 18 additions & 0 deletions apps/webapp/app/components/admin/debugTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TooltipProvider,
TooltipTrigger,
} from "~/components/primitives/Tooltip";
import { useOptionalEnvironment } from "~/hooks/useEnvironment";
import { useIsImpersonating, useOptionalOrganization } from "~/hooks/useOrganizations";
import { useOptionalProject } from "~/hooks/useProject";
import { useHasAdminAccess, useUser } from "~/hooks/useUser";
Expand Down Expand Up @@ -35,6 +36,7 @@ export function AdminDebugTooltip({ children }: { children?: React.ReactNode })
function Content({ children }: { children: React.ReactNode }) {
const organization = useOptionalOrganization();
const project = useOptionalProject();
const environment = useOptionalEnvironment();
const user = useUser();

return (
Expand Down Expand Up @@ -62,6 +64,22 @@ function Content({ children }: { children: React.ReactNode }) {
</Property.Item>
</>
)}
{environment && (
<>
<Property.Item>
<Property.Label>Environment ID</Property.Label>
<Property.Value>{environment.id}</Property.Value>
</Property.Item>
<Property.Item>
<Property.Label>Environment type</Property.Label>
<Property.Value>{environment.type}</Property.Value>
</Property.Item>
<Property.Item>
<Property.Label>Environment paused</Property.Label>
<Property.Value>{environment.paused ? "Yes" : "No"}</Property.Value>
</Property.Item>
</>
)}
</Property.Table>
<div className="pt-2">{children}</div>
</div>
Expand Down
16 changes: 1 addition & 15 deletions apps/webapp/app/components/layout/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { useOptionalOrganization } from "~/hooks/useOrganizations";
import { cn } from "~/utils/cn";
import { useShowUpgradePrompt } from "../billing/UpgradePrompt";

/** This container is used to surround the entire app, it correctly places the nav bar */
export function AppContainer({ children }: { children: React.ReactNode }) {
Expand All @@ -13,19 +11,7 @@ export function MainBody({ children }: { children: React.ReactNode }) {

/** This container should be placed around the content on a page */
export function PageContainer({ children }: { children: React.ReactNode }) {
const organization = useOptionalOrganization();
const showUpgradePrompt = useShowUpgradePrompt(organization);

return (
<div
className={cn(
"grid overflow-hidden",
showUpgradePrompt.shouldShow ? "grid-rows-[5rem_1fr]" : "grid-rows-[2.5rem_1fr]"
)}
>
{children}
</div>
);
return <div className="grid grid-rows-[auto_1fr] overflow-hidden">{children}</div>;
}

export function PageBody({
Expand Down
55 changes: 55 additions & 0 deletions apps/webapp/app/components/metrics/BigNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { type ReactNode } from "react";
import { AnimatedNumber } from "../primitives/AnimatedNumber";
import { Spinner } from "../primitives/Spinner";
import { cn } from "~/utils/cn";

interface BigNumberProps {
title: ReactNode;
animate?: boolean;
loading?: boolean;
value?: number;
valueClassName?: string;
defaultValue?: number;
accessory?: ReactNode;
suffix?: string;
suffixClassName?: string;
}

export function BigNumber({
title,
value,
defaultValue,
valueClassName,
suffix,
suffixClassName,
accessory,
animate = false,
loading = false,
}: BigNumberProps) {
const v = value ?? defaultValue;
return (
<div className="grid grid-rows-[1.5rem_auto] gap-4 rounded-sm border border-grid-dimmed bg-background-bright p-4">
<div className="flex items-center justify-between">
<div className="text-2sm text-text-dimmed">{title}</div>
{accessory && <div className="flex-shrink-0">{accessory}</div>}
</div>
<div
className={cn(
"h-[3.75rem] text-[3.75rem] font-normal tabular-nums leading-none text-text-bright",
valueClassName
)}
>
{loading ? (
<Spinner className="size-6" />
) : v !== undefined ? (
<div className="flex items-baseline gap-1">
{animate ? <AnimatedNumber value={v} /> : v}
{suffix && <div className={cn("text-xs", suffixClassName)}>{suffix}</div>}
</div>
) : (
"–"
)}
</div>
</div>
);
}
3 changes: 1 addition & 2 deletions apps/webapp/app/components/navigation/AccountSideMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ export function AccountSideMenu({ user }: { user: User }) {
to={rootPath()}
fullWidth
textAlignLeft
className="text-text-bright"
>
Back to app
<span className="text-text-bright">Back to app</span>
</LinkButton>
</div>
<div className="mb-6 flex grow flex-col gap-1 overflow-y-auto px-1 pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
Expand Down
57 changes: 57 additions & 0 deletions apps/webapp/app/components/navigation/EnvironmentPausedBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ExclamationCircleIcon } from "@heroicons/react/20/solid";
import { useLocation } from "@remix-run/react";
import { AnimatePresence, motion } from "framer-motion";
import { useOptionalEnvironment } from "~/hooks/useEnvironment";
import { useOptionalOrganization } from "~/hooks/useOrganizations";
import { useOptionalProject } from "~/hooks/useProject";
import { v3QueuesPath } from "~/utils/pathBuilder";
import { environmentFullTitle } from "../environments/EnvironmentLabel";
import { LinkButton } from "../primitives/Buttons";
import { Icon } from "../primitives/Icon";
import { Paragraph } from "../primitives/Paragraph";

export function EnvironmentPausedBanner() {
const organization = useOptionalOrganization();
const project = useOptionalProject();
const environment = useOptionalEnvironment();
const location = useLocation();

const hideButton = location.pathname.endsWith("/queues");

return (
<AnimatePresence initial={false}>
{organization && project && environment && environment.paused ? (
<motion.div
className="flex h-10 items-center justify-between overflow-hidden border-y border-amber-400/20 bg-warning/20 py-0 pl-3 pr-2"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "2.5rem" }}
exit={{ opacity: 0, height: 0 }}
>
<div className="flex items-center gap-2">
<Icon icon={ExclamationCircleIcon} className="h-5 w-5 text-amber-400" />
<Paragraph variant="small" className="text-amber-200">
{environmentFullTitle(environment)} environment paused. No new runs will be dequeued
and executed.
</Paragraph>
</div>
{hideButton ? null : (
<div>
<LinkButton
variant="tertiary/small"
to={v3QueuesPath(organization, project, environment)}
>
Manage
</LinkButton>
</div>
)}
</motion.div>
) : null}
</AnimatePresence>
);
}

export function useShowEnvironmentPausedBanner() {
const environment = useOptionalEnvironment();
const shouldShow = environment?.paused ?? false;
return { shouldShow };
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ import { HelpAndFeedback } from "./HelpAndFeedbackPopover";
import { SideMenuHeader } from "./SideMenuHeader";
import { SideMenuItem } from "./SideMenuItem";
import { useCurrentPlan } from "~/routes/_app.orgs.$organizationSlug/route";
import { Paragraph } from "../primitives/Paragraph";

export function OrganizationSettingsSideMenu({
organization,
version,
}: {
organization: MatchedOrganization;
version: string;
}) {
const { isManagedCloud } = useFeatures();
const currentPlan = useCurrentPlan();
Expand All @@ -42,48 +45,55 @@ export function OrganizationSettingsSideMenu({
to={rootPath()}
fullWidth
textAlignLeft
className="text-text-bright"
>
Back to app
<span className="text-text-bright">Back to app</span>
</LinkButton>
</div>
<div className="mb-6 flex grow flex-col gap-1 overflow-y-auto px-1 pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<SideMenuHeader title="Organization" />
<SideMenuItem
name="Usage"
icon={ChartBarIcon}
activeIconColor="text-indigo-500"
to={v3UsagePath(organization)}
data-action="usage"
/>
{isManagedCloud && (
<div className="mb-6 flex grow flex-col gap-4 overflow-y-auto px-1 pt-2 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600">
<div className="flex flex-col gap-1">
<SideMenuHeader title="Organization" />
<SideMenuItem
name="Billing"
icon={CreditCardIcon}
activeIconColor="text-emerald-500"
to={v3BillingPath(organization)}
data-action="billing"
badge={
currentPlan?.v3Subscription?.isPaying
? currentPlan?.v3Subscription?.plan?.title
: undefined
}
name="Usage"
icon={ChartBarIcon}
activeIconColor="text-indigo-500"
to={v3UsagePath(organization)}
data-action="usage"
/>
)}
<SideMenuItem
name="Team"
icon={UserGroupIcon}
activeIconColor="text-amber-500"
to={organizationTeamPath(organization)}
data-action="team"
/>
<SideMenuItem
name="Settings"
icon={Cog8ToothIcon}
activeIconColor="text-blue-500"
to={organizationSettingsPath(organization)}
data-action="settings"
/>
{isManagedCloud && (
<SideMenuItem
name="Billing"
icon={CreditCardIcon}
activeIconColor="text-emerald-500"
to={v3BillingPath(organization)}
data-action="billing"
badge={
currentPlan?.v3Subscription?.isPaying
? currentPlan?.v3Subscription?.plan?.title
: undefined
}
/>
)}
<SideMenuItem
name="Team"
icon={UserGroupIcon}
activeIconColor="text-amber-500"
to={organizationTeamPath(organization)}
data-action="team"
/>
<SideMenuItem
name="Settings"
icon={Cog8ToothIcon}
activeIconColor="text-blue-500"
to={organizationSettingsPath(organization)}
data-action="settings"
/>
</div>
<div className="flex flex-col gap-1">
<SideMenuHeader title="App version" />
<Paragraph variant="extra-small" className="px-2 text-text-dimmed">
v{version}
</Paragraph>
</div>
</div>
<div className="flex flex-col gap-1 border-t border-grid-bright p-1">
<HelpAndFeedback />
Expand Down
Loading