Skip to content

Commit 1099481

Browse files
committed
fix(webapp): address Devin review findings on schedules sheet
- Schedule detail action: honor `_format=json` in the project-not-found guard so fetcher callers get a structured error envelope. - ScheduleSheet: treat schedule data as loading when its friendlyId doesn't match the currently open id (fixes stale-data flash when switching schedules). - ScheduleSheet: render an explicit "schedule no longer exists" panel when the loader returns `null`, instead of an infinite spinner.
1 parent a295a8b commit 1099481

2 files changed

Lines changed: 35 additions & 4 deletions

File tree

  • apps/webapp/app/routes
    • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam
    • _app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.tasks.scheduled.$taskParam

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
8585
});
8686

8787
if (!project) {
88+
const message = `No project found with slug ${projectParam}`;
89+
if (wantsJson) {
90+
return json({ ok: false as const, message }, { status: 404 });
91+
}
8892
return redirectWithErrorMessage(
8993
v3SchedulePath(
9094
{ slug: organizationSlug },
@@ -93,7 +97,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
9397
{ friendlyId: scheduleParam }
9498
),
9599
request,
96-
`No project found with slug ${projectParam}`
100+
message
97101
);
98102
}
99103

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

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,18 @@ function ScheduleSheet({
638638
}, [deleteFetcher.state, deleteFetcher.data, toast, revalidator, onClose]);
639639

640640
const schedule = detailFetcher.data?.schedule;
641+
// Treat stale data (previous schedule still in fetcher cache after the
642+
// user clicked a different row) as loading — otherwise we briefly flash
643+
// the previous schedule's content while the new fetch is in flight.
644+
const isStaleSchedule = !!schedule && !!openScheduleId && schedule.friendlyId !== openScheduleId;
641645
const isDetailLoading =
642-
detailFetcher.state === "loading" || (!!openScheduleId && !schedule);
646+
detailFetcher.state === "loading" ||
647+
isStaleSchedule ||
648+
(!!openScheduleId && schedule === undefined);
649+
// Distinct from loading: the loader has resolved and the schedule is
650+
// genuinely gone (returned `null`, e.g. deleted externally).
651+
const isScheduleMissing =
652+
!!openScheduleId && !isDetailLoading && detailFetcher.data?.schedule === null;
643653
const editData = editFetcher.data;
644654
const isEditLoading =
645655
mode === "edit" && (editFetcher.state === "loading" || !editData);
@@ -665,16 +675,20 @@ function ScheduleSheet({
665675
submitFetcher={updateFetcher}
666676
/>
667677
)
668-
) : isDetailLoading || !schedule ? (
678+
) : isDetailLoading ? (
669679
<TableLoading />
670-
) : (
680+
) : isScheduleMissing ? (
681+
<ScheduleMissingPanel onClose={onClose} />
682+
) : schedule ? (
671683
<ScheduleInspector
672684
schedule={schedule}
673685
actionPath={detailPath}
674686
onEdit={() => setMode("edit")}
675687
activeToggleFetcher={activeToggleFetcher}
676688
deleteFetcher={deleteFetcher}
677689
/>
690+
) : (
691+
<TableLoading />
678692
)}
679693
</SheetContent>
680694
</Sheet>
@@ -1098,3 +1112,16 @@ function TableLoading() {
10981112
</div>
10991113
);
11001114
}
1115+
1116+
function ScheduleMissingPanel({ onClose }: { onClose: () => void }) {
1117+
return (
1118+
<div className="flex h-full flex-col items-center justify-center gap-3 bg-background-bright p-6 text-center">
1119+
<Paragraph variant="small" className="text-text-bright">
1120+
This schedule no longer exists.
1121+
</Paragraph>
1122+
<Button variant="secondary/small" onClick={onClose}>
1123+
Close
1124+
</Button>
1125+
</div>
1126+
);
1127+
}

0 commit comments

Comments
 (0)