Skip to content

Commit 3b7314c

Browse files
committed
Work on showing the extra concurrency
1 parent 654c985 commit 3b7314c

File tree

2 files changed

+133
-24
lines changed

2 files changed

+133
-24
lines changed

apps/webapp/app/presenters/v3/ManageConcurrencyPresenter.server.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export type ConcurrencyResult = {
88
environments: EnvironmentWithConcurrency[];
99
extraConcurrency: number;
1010
extraAllocatedConcurrency: number;
11+
extraUnallocatedConcurrency: number;
1112
};
1213

1314
export type EnvironmentWithConcurrency = {
@@ -48,6 +49,11 @@ export class ManageConcurrencyPresenter extends BasePresenter {
4849
parentEnvironmentId: true,
4950
isBranchableEnvironment: true,
5051
maximumConcurrencyLimit: true,
52+
orgMember: {
53+
select: {
54+
userId: true,
55+
},
56+
},
5157
},
5258
where: {
5359
organizationId,
@@ -73,6 +79,10 @@ export class ManageConcurrencyPresenter extends BasePresenter {
7379
}
7480

7581
if (environment.projectId === projectId) {
82+
if (environment.type === "DEVELOPMENT" && environment.orgMember?.userId !== userId) {
83+
continue;
84+
}
85+
7686
projectEnvironments.push({
7787
id: environment.id,
7888
type: environment.type,
@@ -85,10 +95,13 @@ export class ManageConcurrencyPresenter extends BasePresenter {
8595
}
8696
}
8797

98+
const extraAllocated = Math.min(extraConcurrency, extraAllocatedConcurrency);
99+
88100
return {
89101
canAddConcurrency,
90102
extraConcurrency,
91-
extraAllocatedConcurrency,
103+
extraAllocatedConcurrency: extraAllocated,
104+
extraUnallocatedConcurrency: extraConcurrency - extraAllocated,
92105
environments: sortEnvironments(projectEnvironments).reverse(),
93106
};
94107
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.concurrency/route.tsx

Lines changed: 119 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
1-
import { MetaFunction } from "@remix-run/react";
1+
import { PlusIcon } from "@heroicons/react/20/solid";
2+
import { type MetaFunction } from "@remix-run/react";
23
import { type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/server-runtime";
34
import { tryCatch } from "@trigger.dev/core";
45
import { typedjson, useTypedLoaderData } from "remix-typedjson";
56
import { z } from "zod";
67
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
78
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
89
import {
9-
MainCenteredContainer,
1010
MainHorizontallyCenteredContainer,
1111
PageBody,
1212
PageContainer,
1313
} from "~/components/layout/AppLayout";
14-
import { Header2 } from "~/components/primitives/Headers";
15-
import { InputGroup } from "~/components/primitives/InputGroup";
16-
import { Label } from "~/components/primitives/Label";
14+
import { Button, LinkButton } from "~/components/primitives/Buttons";
15+
import { Header2, Header3 } from "~/components/primitives/Headers";
1716
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
1817
import { Paragraph } from "~/components/primitives/Paragraph";
1918
import * as Property from "~/components/primitives/PropertyTable";
@@ -25,20 +24,20 @@ import {
2524
TableHeaderCell,
2625
TableRow,
2726
} from "~/components/primitives/Table";
27+
import { InfoIconTooltip } from "~/components/primitives/Tooltip";
28+
import { useFeatures } from "~/hooks/useFeatures";
2829
import { useOrganization } from "~/hooks/useOrganizations";
2930
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
3031
import { findProjectBySlug } from "~/models/project.server";
3132
import {
32-
EnvironmentWithConcurrency,
33+
type ConcurrencyResult,
34+
type EnvironmentWithConcurrency,
3335
ManageConcurrencyPresenter,
3436
} from "~/presenters/v3/ManageConcurrencyPresenter.server";
3537
import { requireUser, requireUserId } from "~/services/session.server";
36-
import { cn } from "~/utils/cn";
3738
import { EnvironmentParamSchema, regionsPath, v3BillingPath } from "~/utils/pathBuilder";
3839
import { SetDefaultRegionService } from "~/v3/services/setDefaultRegion.server";
3940
import { useCurrentPlan } from "../_app.orgs.$organizationSlug/route";
40-
import { useFeatures } from "~/hooks/useFeatures";
41-
import { LinkButton } from "~/components/primitives/Buttons";
4241

4342
export const meta: MetaFunction = () => {
4443
return [
@@ -123,8 +122,13 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
123122
};
124123

125124
export default function Page() {
126-
const { canAddConcurrency, environments } = useTypedLoaderData<typeof loader>();
127-
const organization = useOrganization();
125+
const {
126+
canAddConcurrency,
127+
extraConcurrency,
128+
extraAllocatedConcurrency,
129+
extraUnallocatedConcurrency,
130+
environments,
131+
} = useTypedLoaderData<typeof loader>();
128132

129133
return (
130134
<PageContainer>
@@ -149,18 +153,13 @@ export default function Page() {
149153
<PageBody scrollable={false}>
150154
<MainHorizontallyCenteredContainer>
151155
{canAddConcurrency ? (
152-
<div>
153-
<div className="mb-3 border-b border-grid-dimmed pb-1">
154-
<Header2>Manage your concurrency</Header2>
155-
</div>
156-
<div className="flex flex-col gap-6">
157-
<InputGroup fullWidth>
158-
<div className="flex w-full items-center justify-between">
159-
<Label>Secret key</Label>
160-
</div>
161-
</InputGroup>
162-
</div>
163-
</div>
156+
<Upgradable
157+
canAddConcurrency={canAddConcurrency}
158+
extraConcurrency={extraConcurrency}
159+
extraAllocatedConcurrency={extraAllocatedConcurrency}
160+
extraUnallocatedConcurrency={extraUnallocatedConcurrency}
161+
environments={environments}
162+
/>
164163
) : (
165164
<NotUpgradable environments={environments} />
166165
)}
@@ -170,6 +169,103 @@ export default function Page() {
170169
);
171170
}
172171

172+
function Upgradable({
173+
canAddConcurrency,
174+
extraConcurrency,
175+
extraAllocatedConcurrency,
176+
extraUnallocatedConcurrency,
177+
environments,
178+
}: ConcurrencyResult) {
179+
const organization = useOrganization();
180+
181+
return (
182+
<div className="flex flex-col gap-3">
183+
<div className="border-b border-grid-dimmed pb-1">
184+
<Header2>Your concurrency</Header2>
185+
</div>
186+
<Paragraph variant="small">
187+
Concurrency limits determine how many runs you can execute at the same time. You can add
188+
extra concurrency to your organization which you can allocate to environments in your
189+
projects.
190+
</Paragraph>
191+
<div className="mt-3 flex flex-col gap-6">
192+
<div className="flex flex-col gap-2">
193+
<div className="flex items-center first-letter:pb-1">
194+
<Header3 className="grow">Extra concurrency</Header3>
195+
<Button variant="primary/small" LeadingIcon={PlusIcon}>
196+
Purchase extra concurrency...
197+
</Button>
198+
</div>
199+
<Table>
200+
<TableHeader>
201+
<TableRow>
202+
<TableHeaderCell className="pl-0">Extra concurrency purchased</TableHeaderCell>
203+
<TableHeaderCell alignment="right" className="text-text-bright">
204+
{extraConcurrency}
205+
</TableHeaderCell>
206+
</TableRow>
207+
</TableHeader>
208+
<TableBody>
209+
<TableRow>
210+
<TableCell>Allocated concurrency</TableCell>
211+
<TableCell alignment="right" className="text-text-bright">
212+
{extraAllocatedConcurrency}
213+
</TableCell>
214+
</TableRow>
215+
<TableRow>
216+
<TableCell>Unallocated concurrency</TableCell>
217+
<TableCell
218+
alignment="right"
219+
className={extraUnallocatedConcurrency > 0 ? "text-success" : "text-text-bright"}
220+
>
221+
{extraUnallocatedConcurrency}
222+
</TableCell>
223+
</TableRow>
224+
</TableBody>
225+
</Table>
226+
</div>
227+
<div className="flex flex-col gap-2">
228+
<div className="flex items-center pb-1">
229+
<Header3 className="grow">Concurrency allocation</Header3>
230+
</div>
231+
<Table>
232+
<TableHeader>
233+
<TableRow>
234+
<TableHeaderCell className="pl-0">Environment</TableHeaderCell>
235+
<TableHeaderCell alignment="right">
236+
<span className="flex items-center gap-x-1">
237+
Included{" "}
238+
<InfoIconTooltip content="This is the included concurrency based on your plan." />
239+
</span>
240+
</TableHeaderCell>
241+
<TableHeaderCell alignment="right">Extra concurrency</TableHeaderCell>
242+
<TableHeaderCell alignment="right">Total</TableHeaderCell>
243+
</TableRow>
244+
</TableHeader>
245+
<TableBody>
246+
{environments.map((environment) => (
247+
<TableRow key={environment.id}>
248+
<TableCell className="pl-0">
249+
<EnvironmentCombo environment={environment} />
250+
</TableCell>
251+
<TableCell alignment="right">{environment.planConcurrencyLimit}</TableCell>
252+
<TableCell alignment="right" className="text-text-bright">
253+
{Math.max(
254+
0,
255+
environment.maximumConcurrencyLimit - environment.planConcurrencyLimit
256+
)}
257+
</TableCell>
258+
<TableCell alignment="right">{environment.maximumConcurrencyLimit}</TableCell>
259+
</TableRow>
260+
))}
261+
</TableBody>
262+
</Table>
263+
</div>
264+
</div>
265+
</div>
266+
);
267+
}
268+
173269
function NotUpgradable({ environments }: { environments: EnvironmentWithConcurrency[] }) {
174270
const { isManagedCloud } = useFeatures();
175271
const plan = useCurrentPlan();

0 commit comments

Comments
 (0)