Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,67 @@ import { Dialog, DialogContent, DialogTitle } from "@/components/ui";
import { ALL_APPS_FRONTEND, getAppPath } from "@/lib/apps-frontend";
import { useUpdateConfig } from "@/lib/config-update";
import { AppId } from "@stackframe/stack-shared/dist/apps/apps-config";
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
import { usePathname } from "next/navigation";
import { useEffect, useState } from "react";

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

const adminApp = useAdminApp();
const project = adminApp.useProject();
const config = project.useConfig();
const updateConfig = useUpdateConfig();

const isEnabled = config.apps.installed[appId]?.enabled ?? false;

// Control modal visibility based on whether we're on a modal route.
// This ensures the modal only closes when navigation actually succeeds,
// preventing issues if router.replace is vetoed by a confirmation dialog.
useEffect(() => {
const isModalRoute = /^\/projects\/[^/]+\/apps\/[^/]+$/.test(pathname);
setIsOpen(isModalRoute);
}, [pathname]);

const handleEnable = async () => {
await wait(1000);
await updateConfig({
adminApp,
configUpdate: { [`apps.installed.${appId}.enabled`]: true },
pushable: true,
});
Comment thread
nams1570 marked this conversation as resolved.
};
Comment thread
nams1570 marked this conversation as resolved.

const handleDisable = async () => {
await updateConfig({
adminApp,
configUpdate: { [`apps.installed.${appId}.enabled`]: false },
pushable: true,
});
};

const handleOpen = () => {
const path = getAppPath(project.id, ALL_APPS_FRONTEND[appId]);
router.push(path);
// Navigate to the app page. Modal stays open until pathname changes.
router.replace(path);
};

const handleOpenChange = (open: boolean) => {
if (!open) {
// Navigate back to apps list. Modal stays open until pathname changes.
router.replace(`/projects/${project.id}/apps`);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
};

return (
<Dialog open={true} onOpenChange={(open) => !open && router.back()} modal>
<Dialog open={isOpen} onOpenChange={handleOpenChange} modal>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-hidden p-0 flex flex-col" noCloseButton>
<AppStoreEntry
appId={appId}
isEnabled={isEnabled}
onEnable={handleEnable}
onDisable={handleDisable}
onOpen={handleOpen}
titleComponent={DialogTitle}
/>
</DialogContent>
Expand Down
5 changes: 3 additions & 2 deletions apps/dashboard/src/components/app-store-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ export function AppStoreEntry({
{onDisable && (
<Button
onClick={onDisable}
variant="secondary"
size="sm"
variant="ghost"
size="lg"
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"
>
Disable
</Button>
Expand Down