Skip to content

Commit 9002b63

Browse files
authored
[Fix] Fix Various bugs on the Explore Apps Page (#1142)
### Context There are a smattering of bugs on the explore apps page. Clicking "Enable App" may enable the app, but the button still shows, plus it causes unnecessary redirects while the modal is still up. This behavior can be seen in the linked clip https://github.com/user-attachments/assets/09b12ccf-e174-4289-91f3-4f0e73cfe7e4 ### Summary of Changes We dynamically handle the modal open state, and track the path updates. This lets us deal with the bugs above while avoiding unnecessary renders, allowing reopening of previously opened modals, and preventing unnecessary redirects. Dealing with the enable apps button issues also now allows users to navigate to the app page from the explore apps modal. We also add a disable button to the modal. Previously, users had to check the options for each app in order to disable it. Now they can do it on the modal itself, which is in line with how the "Enable App" functionality works. ### UI Demo https://github.com/user-attachments/assets/5bfd35c6-5d28-4f99-958a-9300533e2351
1 parent abc320b commit 9002b63

2 files changed

Lines changed: 42 additions & 6 deletions

File tree

apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/@modal/(.)apps/[appId]/page-client.tsx

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,67 @@ import { Dialog, DialogContent, DialogTitle } from "@/components/ui";
77
import { ALL_APPS_FRONTEND, getAppPath } from "@/lib/apps-frontend";
88
import { useUpdateConfig } from "@/lib/config-update";
99
import { AppId } from "@stackframe/stack-shared/dist/apps/apps-config";
10-
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
10+
import { usePathname } from "next/navigation";
11+
import { useEffect, useState } from "react";
1112

1213
export default function AppDetailsModalPageClient({ appId }: { appId: AppId }) {
1314
const router = useRouter();
15+
const pathname = usePathname();
16+
const [isOpen, setIsOpen] = useState(true);
1417

1518
const adminApp = useAdminApp();
1619
const project = adminApp.useProject();
20+
const config = project.useConfig();
1721
const updateConfig = useUpdateConfig();
1822

23+
const isEnabled = config.apps.installed[appId]?.enabled ?? false;
24+
25+
// Control modal visibility based on whether we're on a modal route.
26+
// This ensures the modal only closes when navigation actually succeeds,
27+
// preventing issues if router.replace is vetoed by a confirmation dialog.
28+
useEffect(() => {
29+
const isModalRoute = /^\/projects\/[^/]+\/apps\/[^/]+$/.test(pathname);
30+
setIsOpen(isModalRoute);
31+
}, [pathname]);
32+
1933
const handleEnable = async () => {
20-
await wait(1000);
2134
await updateConfig({
2235
adminApp,
2336
configUpdate: { [`apps.installed.${appId}.enabled`]: true },
2437
pushable: true,
2538
});
39+
};
40+
41+
const handleDisable = async () => {
42+
await updateConfig({
43+
adminApp,
44+
configUpdate: { [`apps.installed.${appId}.enabled`]: false },
45+
pushable: true,
46+
});
47+
};
48+
49+
const handleOpen = () => {
2650
const path = getAppPath(project.id, ALL_APPS_FRONTEND[appId]);
27-
router.push(path);
51+
// Navigate to the app page. Modal stays open until pathname changes.
52+
router.replace(path);
53+
};
54+
55+
const handleOpenChange = (open: boolean) => {
56+
if (!open) {
57+
// Navigate back to apps list. Modal stays open until pathname changes.
58+
router.replace(`/projects/${project.id}/apps`);
59+
}
2860
};
2961

3062
return (
31-
<Dialog open={true} onOpenChange={(open) => !open && router.back()} modal>
63+
<Dialog open={isOpen} onOpenChange={handleOpenChange} modal>
3264
<DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden p-0 flex flex-col" noCloseButton>
3365
<AppStoreEntry
3466
appId={appId}
67+
isEnabled={isEnabled}
3568
onEnable={handleEnable}
69+
onDisable={handleDisable}
70+
onOpen={handleOpen}
3671
titleComponent={DialogTitle}
3772
/>
3873
</DialogContent>

apps/dashboard/src/components/app-store-entry.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,9 @@ export function AppStoreEntry({
156156
{onDisable && (
157157
<Button
158158
onClick={onDisable}
159-
variant="secondary"
160-
size="sm"
159+
variant="ghost"
160+
size="lg"
161+
className="text-red-600 hover:bg-red-50 hover:text-red-700 dark:text-red-400 dark:hover:bg-red-950 dark:hover:text-red-300"
161162
>
162163
Disable
163164
</Button>

0 commit comments

Comments
 (0)