Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/keppere/outerbase-studio
Browse files Browse the repository at this point in the history
…into export-query-result
  • Loading branch information
keppere committed Jan 1, 2025
2 parents 779c0b5 + 2011b39 commit 8ba043b
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 3 deletions.
70 changes: 70 additions & 0 deletions src/app/storybook/common-dialog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"use client";
import {
CommonDialogProvider,
useCommonDialog,
} from "@/components/common-dialog";
import { Button } from "@/components/ui/button";
import { TrashIcon } from "lucide-react";

function StorybookBody() {
const { showDialog } = useCommonDialog();

return (
<div className="p-4 flex flex-row gap-2">
<Button
onClick={() => {
showDialog({
destructive: true,
title: "Delete Database",
content: "Are you sure you want to delete this?",
previewCode: "DROP DATABASE my_database",
actions: [
{
text: "Delete",
icon: TrashIcon,
onClick: async () => {
await new Promise((resolve) => setTimeout(resolve, 3000));
},
},
],
});
}}
>
Show
</Button>

<Button
onClick={() => {
showDialog({
destructive: true,
title: "Delete Database",
content: "Are you sure you want to delete this?",
previewCode: "DROP DATABASE my_database",
actions: [
{
text: "Delete",
icon: TrashIcon,
onClick: async () => {
await new Promise((resolve) => setTimeout(resolve, 3000));
throw new Error(
"Failed to delete. You do not have permission"
);
},
},
],
});
}}
>
Show with error action
</Button>
</div>
);
}

export default function CommonDialogStorybook() {
return (
<CommonDialogProvider>
<StorybookBody />
</CommonDialogProvider>
);
}
145 changes: 145 additions & 0 deletions src/components/common-dialog/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use client";
import { noop } from "lodash";
import {
PropsWithChildren,
ReactElement,
createContext,
useCallback,
useContext,
useState,
} from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
} from "../ui/dialog";
import { Button } from "../ui/button";
import { Icon } from "@phosphor-icons/react";
import { Loader } from "lucide-react";
import CodePreview from "../gui/code-preview";

interface ShowDialogProps {
title: string;
content: string | ReactElement;
destructive?: boolean;
previewCode?: string;
actions?: {
text: string;
icon?: Icon;
onClick: () => Promise<void>;
onComplete?: () => void;
}[];
}

interface CommonDialogContextProps {
showDialog(option: ShowDialogProps): void;
}

const CommonDialogContext = createContext<CommonDialogContextProps>({
showDialog: noop,
});

export function CommonDialogProvider({ children }: PropsWithChildren) {
const [dialogOption, setDialogOption] = useState<ShowDialogProps | null>(
null
);

const [errorMessage, setErrorMessage] = useState("");
const [loading, setLoading] = useState(false);

const hideDialog = useCallback(() => {
setErrorMessage("");
setDialogOption(null);
}, []);

return (
<CommonDialogContext.Provider value={{ showDialog: setDialogOption }}>
{children}
{dialogOption && (
<Dialog
open={dialogOption !== null}
onOpenChange={(openState) => {
if (!openState) {
hideDialog();
}
}}
>
<DialogContent>
<DialogHeader
className={dialogOption?.destructive ? "text-red-500" : ""}
>
{dialogOption.title}
</DialogHeader>
<DialogDescription className="flex flex-col gap-2">
{errorMessage && (
<div className="text-sm text-red-500 font-mono flex gap-4 items-end">
<p>{errorMessage}</p>
</div>
)}

<div>{dialogOption.content}</div>
{dialogOption.previewCode && (
<CodePreview code={dialogOption.previewCode} />
)}
</DialogDescription>
<DialogFooter>
{dialogOption.actions?.map((action) => (
<Button
key={action.text}
className={
dialogOption.destructive ? "bg-red-500 text-white" : ""
}
disabled={loading}
onClick={() => {
setLoading(true);

action
.onClick()
.then(() => {
hideDialog();
if (action.onComplete) {
action.onComplete();
}
})
.catch((e) => {
if (e instanceof Error) {
setErrorMessage(e.message);
} else {
setErrorMessage("An error occurred");
}
})
.finally(() => {
setLoading(false);
});
}}
>
{loading && <Loader className="w-4 h-4 mr-2 animate-spin" />}
{action.icon && !loading && (
<action.icon className="w-4 h-4 mr-2" />
)}
{action.text}
</Button>
))}

<Button
disabled={loading}
variant={"secondary"}
onClick={() => {
hideDialog();
}}
>
Close
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</CommonDialogContext.Provider>
);
}

export function useCommonDialog() {
return useContext(CommonDialogContext);
}
9 changes: 6 additions & 3 deletions src/components/gui/studio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { StudioContextMenuItem } from "@/messages/open-context-menu";
import { CollaborationBaseDriver } from "@/drivers/collaboration-driver-base";
import { SavedDocDriver } from "@/drivers/saved-doc/saved-doc-driver";
import { FullEditorProvider } from "./providers/full-editor-provider";
import { CommonDialogProvider } from "../common-dialog";

export interface StudioExtension {
contextMenu?: (state: OptimizeTableState) => StudioContextMenuItem[];
Expand Down Expand Up @@ -75,9 +76,11 @@ export function Studio({
onBack={onBack}
sideBarFooterComponent={sideBarFooterComponent}
>
<FullEditorProvider>
<MainScreen />
</FullEditorProvider>
<CommonDialogProvider>
<FullEditorProvider>
<MainScreen />
</FullEditorProvider>
</CommonDialogProvider>
</ConfigProvider>
</DriverProvider>
);
Expand Down

0 comments on commit 8ba043b

Please sign in to comment.