Skip to content

Commit 80b62d4

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/compute-workload-manager
2 parents 63424fa + c0b6309 commit 80b62d4

File tree

36 files changed

+829
-155
lines changed

36 files changed

+829
-155
lines changed

.changeset/warm-falcons-joke.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

.server-changes/add-errors-page.md

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: feature
4+
---
5+
6+
Added `/engine/v1/dev/disconnect` endpoint to auto-cancel runs when the CLI disconnects. Maximum of 500 runs can be cancelled. Uses the bulk action system when there are more than 25 runs to cancel.

.server-changes/test-page-sidebar-tabs.md

Lines changed: 0 additions & 6 deletions
This file was deleted.

apps/webapp/app/components/onboarding/TechnologyPicker.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,22 @@ export function TechnologyPicker({
210210

211211
const addCustomValue = useCallback(() => {
212212
const trimmed = otherInputValue.trim();
213-
if (trimmed && !customValues.includes(trimmed) && !value.includes(trimmed)) {
213+
if (!trimmed) return;
214+
215+
const matchedOption = TECHNOLOGY_OPTIONS.find(
216+
(opt) => opt.toLowerCase() === trimmed.toLowerCase()
217+
);
218+
219+
if (matchedOption) {
220+
if (!value.includes(matchedOption)) {
221+
onChange([...value, matchedOption]);
222+
}
223+
} else if (!customValues.includes(trimmed) && !value.includes(trimmed)) {
214224
onCustomValuesChange([...customValues, trimmed]);
215-
setOtherInputValue("");
216225
}
217-
}, [otherInputValue, customValues, onCustomValuesChange, value]);
226+
227+
setOtherInputValue("");
228+
}, [otherInputValue, customValues, onCustomValuesChange, value, onChange]);
218229

219230
const handleOtherKeyDown = useCallback(
220231
(e: React.KeyboardEvent) => {

apps/webapp/app/components/primitives/Buttons.tsx

Lines changed: 82 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import { Link, type LinkProps, NavLink, type NavLinkProps } from "@remix-run/react";
2-
import React, { forwardRef, type ReactNode, useImperativeHandle, useRef } from "react";
2+
import React, {
3+
forwardRef,
4+
type ReactNode,
5+
useEffect,
6+
useImperativeHandle,
7+
useRef,
8+
useState,
9+
} from "react";
310
import { type ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys";
411
import { cn } from "~/utils/cn";
512
import { ShortcutKey } from "./ShortcutKey";
613
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./Tooltip";
714
import { Icon, type RenderIcon } from "./Icon";
15+
import { Spinner } from "./Spinner";
816

917
const sizes = {
1018
small: {
@@ -180,6 +188,7 @@ export type ButtonContentPropsType = {
180188
tooltip?: ReactNode;
181189
iconSpacing?: string;
182190
hideShortcutKey?: boolean;
191+
isLoading?: boolean;
183192
};
184193

185194
export function ButtonContent(props: ButtonContentPropsType) {
@@ -196,7 +205,19 @@ export function ButtonContent(props: ButtonContentPropsType) {
196205
tooltip,
197206
iconSpacing,
198207
hideShortcutKey,
208+
isLoading,
199209
} = props;
210+
211+
const [showSpinner, setShowSpinner] = useState(false);
212+
useEffect(() => {
213+
if (!isLoading) {
214+
setShowSpinner(false);
215+
return;
216+
}
217+
const timer = setTimeout(() => setShowSpinner(true), 200);
218+
return () => clearTimeout(timer);
219+
}, [isLoading]);
220+
200221
const variation = allVariants.variant[props.variant];
201222

202223
const btnClassName = cn(allVariants.$all, variation.button);
@@ -217,56 +238,64 @@ export function ButtonContent(props: ButtonContentPropsType) {
217238

218239
const buttonContent = (
219240
<div className={cn("flex", fullWidth ? "" : "w-fit text-xxs", btnClassName, className)}>
220-
<div
221-
className={cn(
222-
textAlignLeft ? "text-left" : "justify-center",
223-
"flex w-full items-center",
224-
iconSpacingClassName,
225-
iconSpacing
226-
)}
227-
>
228-
{LeadingIcon && (
229-
<Icon
230-
icon={LeadingIcon}
231-
className={cn(
232-
iconClassName,
233-
variation.icon,
234-
leadingIconClassName,
235-
"shrink-0 justify-start"
236-
)}
237-
/>
238-
)}
241+
<div className={cn("relative", "flex w-full items-center")}>
242+
<div
243+
className={cn(
244+
textAlignLeft ? "text-left" : "justify-center",
245+
"flex w-full items-center",
246+
iconSpacingClassName,
247+
iconSpacing,
248+
showSpinner && "invisible"
249+
)}
250+
>
251+
{LeadingIcon && (
252+
<Icon
253+
icon={LeadingIcon}
254+
className={cn(
255+
iconClassName,
256+
variation.icon,
257+
leadingIconClassName,
258+
"shrink-0 justify-start"
259+
)}
260+
/>
261+
)}
239262

240-
{text &&
241-
(typeof text === "string" ? (
242-
<span className={cn("mx-auto grow self-center truncate", textColorClassName)}>
243-
{text}
244-
</span>
245-
) : (
246-
<>{text}</>
247-
))}
248-
249-
{shortcut &&
250-
!tooltip &&
251-
props.shortcutPosition === "before-trailing-icon" &&
252-
renderShortcutKey()}
253-
254-
{TrailingIcon && (
255-
<Icon
256-
icon={TrailingIcon}
257-
className={cn(
258-
iconClassName,
259-
variation.icon,
260-
trailingIconClassName,
261-
"shrink-0 justify-end"
262-
)}
263-
/>
264-
)}
263+
{text &&
264+
(typeof text === "string" ? (
265+
<span className={cn("mx-auto grow self-center truncate", textColorClassName)}>
266+
{text}
267+
</span>
268+
) : (
269+
<>{text}</>
270+
))}
271+
272+
{shortcut &&
273+
!tooltip &&
274+
props.shortcutPosition === "before-trailing-icon" &&
275+
renderShortcutKey()}
265276

266-
{shortcut &&
267-
!tooltip &&
268-
(!props.shortcutPosition || props.shortcutPosition === "after-trailing-icon") &&
269-
renderShortcutKey()}
277+
{TrailingIcon && (
278+
<Icon
279+
icon={TrailingIcon}
280+
className={cn(
281+
iconClassName,
282+
variation.icon,
283+
trailingIconClassName,
284+
"shrink-0 justify-end"
285+
)}
286+
/>
287+
)}
288+
289+
{shortcut &&
290+
!tooltip &&
291+
(!props.shortcutPosition || props.shortcutPosition === "after-trailing-icon") &&
292+
renderShortcutKey()}
293+
</div>
294+
{showSpinner && (
295+
<span className="absolute inset-0 flex items-center justify-center">
296+
<Spinner className="size-3.5" color="white" />
297+
</span>
298+
)}
270299
</div>
271300
</div>
272301
);
@@ -298,6 +327,8 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
298327
const innerRef = useRef<HTMLButtonElement>(null);
299328
useImperativeHandle(ref, () => innerRef.current as HTMLButtonElement);
300329

330+
const isDisabled = disabled || props.isLoading;
331+
301332
useShortcutKeys({
302333
shortcut: props.shortcut,
303334
action: (e) => {
@@ -307,14 +338,14 @@ export const Button = forwardRef<HTMLButtonElement, ButtonPropsType>(
307338
e.stopPropagation();
308339
}
309340
},
310-
disabled: disabled || !props.shortcut,
341+
disabled: isDisabled || !props.shortcut,
311342
});
312343

313344
return (
314345
<button
315346
className={cn("group/button outline-none focus-custom", props.fullWidth ? "w-full" : "")}
316347
type={type}
317-
disabled={disabled}
348+
disabled={isDisabled}
318349
onClick={onClick}
319350
name={props.name}
320351
value={props.value}

apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { Input } from "~/components/primitives/Input";
2222
import { InputGroup } from "~/components/primitives/InputGroup";
2323
import { Label } from "~/components/primitives/Label";
2424
import { Select, SelectItem } from "~/components/primitives/Select";
25-
import { ButtonSpinner } from "~/components/primitives/Spinner";
25+
2626
import { prisma } from "~/db.server";
2727
import { featuresForRequest } from "~/features.server";
2828
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
@@ -169,6 +169,8 @@ const schema = z.object({
169169
technologiesOther: z.string().optional(),
170170
goals: z.string().optional(),
171171
goalsOther: z.string().optional(),
172+
workingOnPositions: z.string().optional(),
173+
goalsPositions: z.string().optional(),
172174
});
173175

174176
export const action: ActionFunction = async ({ request, params }) => {
@@ -200,10 +202,25 @@ export const action: ActionFunction = async ({ request, params }) => {
200202
}
201203
}
202204

205+
const numberArraySchema = z.array(z.number());
206+
function safeParseNumberArray(value: string | undefined): number[] | undefined {
207+
if (!value) return undefined;
208+
try {
209+
const result = numberArraySchema.safeParse(JSON.parse(value));
210+
return result.success && result.data.length > 0 ? result.data : undefined;
211+
} catch {
212+
return undefined;
213+
}
214+
}
215+
203216
const onboardingData: Record<string, Prisma.InputJsonValue> = {};
204217

205218
const workingOn = safeParseStringArray(submission.value.workingOn);
206-
if (workingOn) onboardingData.workingOn = workingOn;
219+
if (workingOn) {
220+
onboardingData.workingOn = workingOn;
221+
const workingOnPositions = safeParseNumberArray(submission.value.workingOnPositions);
222+
if (workingOnPositions) onboardingData.workingOnPositions = workingOnPositions;
223+
}
207224

208225
if (submission.value.workingOnOther) {
209226
onboardingData.workingOnOther = submission.value.workingOnOther;
@@ -216,7 +233,11 @@ export const action: ActionFunction = async ({ request, params }) => {
216233
if (technologiesOther) onboardingData.technologiesOther = technologiesOther;
217234

218235
const goals = safeParseStringArray(submission.value.goals);
219-
if (goals) onboardingData.goals = goals;
236+
if (goals) {
237+
onboardingData.goals = goals;
238+
const goalsPositions = safeParseNumberArray(submission.value.goalsPositions);
239+
if (goalsPositions) onboardingData.goalsPositions = goalsPositions;
240+
}
220241

221242
if (submission.value.goalsOther) {
222243
onboardingData.goalsOther = submission.value.goalsOther;
@@ -376,6 +397,13 @@ export default function Page() {
376397
<InputGroup>
377398
<Label>What are you working on?</Label>
378399
<input type="hidden" name="workingOn" value={JSON.stringify(selectedWorkingOn)} />
400+
<input
401+
type="hidden"
402+
name="workingOnPositions"
403+
value={JSON.stringify(
404+
selectedWorkingOn.map((v) => shuffledWorkingOn.indexOf(v) + 1)
405+
)}
406+
/>
379407
<MultiSelectField
380408
value={selectedWorkingOn}
381409
setValue={setSelectedWorkingOn}
@@ -421,6 +449,13 @@ export default function Page() {
421449
<InputGroup>
422450
<Label>What are you trying to do with Trigger.dev?</Label>
423451
<input type="hidden" name="goals" value={JSON.stringify(selectedGoals)} />
452+
<input
453+
type="hidden"
454+
name="goalsPositions"
455+
value={JSON.stringify(
456+
selectedGoals.map((v) => shuffledGoals.indexOf(v) + 1)
457+
)}
458+
/>
424459
<MultiSelectField
425460
value={selectedGoals}
426461
setValue={setSelectedGoals}
@@ -445,13 +480,8 @@ export default function Page() {
445480

446481
<FormButtons
447482
confirmButton={
448-
<Button
449-
type="submit"
450-
variant={"primary/small"}
451-
disabled={isLoading}
452-
TrailingIcon={isLoading ? ButtonSpinner : undefined}
453-
>
454-
{isLoading ? "Creating…" : "Create"}
483+
<Button type="submit" variant={"primary/small"} isLoading={isLoading}>
484+
Create
455485
</Button>
456486
}
457487
cancelButton={

apps/webapp/app/routes/_app.orgs.new/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ export default function NewOrganizationPage() {
220220

221221
<FormButtons
222222
confirmButton={
223-
<Button type="submit" variant={"primary/small"} disabled={isLoading}>
223+
<Button type="submit" variant={"primary/small"} isLoading={isLoading}>
224224
Create
225225
</Button>
226226
}

0 commit comments

Comments
 (0)