error handle

This commit is contained in:
Guy Ben-Aharon
2024-08-24 17:15:12 +03:00
parent ed90268366
commit 4a0800e66d
9 changed files with 171 additions and 73 deletions

View File

@@ -1,6 +1,7 @@
import { createContext } from 'react';
import { emptyFn } from '@/lib/utils';
import { DatabaseType } from '@/lib/domain/database-type';
import { BaseAlertDialogProps } from '@/dialogs/base-alert-dialog/base-alert-dialog';
export interface DialogContext {
// Create diagram dialog
@@ -16,13 +17,7 @@ export interface DialogContext {
closeExportSQLDialog: () => void;
// Alert dialog
showAlert: (params: {
onAction: () => void;
title: string;
description: string;
actionLabel: string;
closeLabel: string;
}) => void;
showAlert: (params: BaseAlertDialogProps) => void;
closeAlert: () => void;
}

View File

@@ -4,7 +4,10 @@ import { CreateDiagramDialog } from '@/dialogs/create-diagram-dialog/create-diag
import { OpenDiagramDialog } from '@/dialogs/open-diagram-dialog/open-diagram-dialog';
import { ExportSQLDialog } from '@/dialogs/export-sql-dialog/export-sql-dialog';
import { DatabaseType } from '@/lib/domain/database-type';
import { BaseAlertDialog } from '@/dialogs/base-alert-dialog/base-alert-dialog';
import {
BaseAlertDialog,
BaseAlertDialogProps,
} from '@/dialogs/base-alert-dialog/base-alert-dialog';
export const DialogProvider: React.FC<React.PropsWithChildren> = ({
children,
@@ -16,18 +19,8 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
targetDatabaseType: DatabaseType;
}>({ targetDatabaseType: DatabaseType.GENERIC });
const [showAlert, setShowAlert] = useState(false);
const [alertParams, setAlertParams] = useState<{
onAction: () => void;
title: string;
description: string;
actionLabel: string;
closeLabel: string;
}>({
onAction: () => {},
const [alertParams, setAlertParams] = useState<BaseAlertDialogProps>({
title: '',
description: '',
actionLabel: '',
closeLabel: '',
});
const openExportSQLDialogHandler: DialogContext['openExportSQLDialog'] =
@@ -40,14 +33,8 @@ export const DialogProvider: React.FC<React.PropsWithChildren> = ({
);
const showAlertHandler: DialogContext['showAlert'] = useCallback(
({ onAction, title, description, actionLabel, closeLabel }) => {
setAlertParams({
onAction,
title,
description,
actionLabel,
closeLabel,
});
(params) => {
setAlertParams(params);
setShowAlert(true);
},
[setShowAlert, setAlertParams]

View File

@@ -6,6 +6,7 @@ import {
import { Dialog, DialogContent } from '@/components/dialog/dialog';
import { Spinner } from '@/components/spinner/spinner';
import { Hourglass } from 'lucide-react';
import { DialogTitle } from '@radix-ui/react-dialog';
export const FullScreenLoaderProvider: React.FC<React.PropsWithChildren> = ({
children,
@@ -36,6 +37,7 @@ export const FullScreenLoaderProvider: React.FC<React.PropsWithChildren> = ({
{children}
<Dialog open={open}>
<DialogContent className="shadow-none bg-transparent border-none outline-none justify-center">
<DialogTitle className="hidden"></DialogTitle>
<div className="bg-white w-fit p-3 rounded-xl">
{animated ? (
<Spinner size={'large'} />

View File

@@ -14,11 +14,12 @@ import { useDialog } from '@/hooks/use-dialog';
export interface BaseAlertDialogProps {
title: string;
description: string;
actionLabel: string;
closeLabel: string;
onAction: () => void;
dialog: AlertDialogProps;
description?: string;
actionLabel?: string;
closeLabel?: string;
onAction?: () => void;
dialog?: AlertDialogProps;
content?: React.ReactNode;
}
export const BaseAlertDialog: React.FC<BaseAlertDialogProps> = ({
@@ -28,10 +29,11 @@ export const BaseAlertDialog: React.FC<BaseAlertDialogProps> = ({
closeLabel,
onAction,
dialog,
content,
}) => {
const { closeAlert } = useDialog();
const alertHandler = useCallback(() => {
onAction();
onAction?.();
closeAlert();
}, [onAction, closeAlert]);
return (
@@ -46,17 +48,24 @@ export const BaseAlertDialog: React.FC<BaseAlertDialogProps> = ({
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
<AlertDialogDescription>
{description}
</AlertDialogDescription>
{description && (
<AlertDialogDescription>
{description}
</AlertDialogDescription>
)}
{content}
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={closeAlert}>
{closeLabel}
</AlertDialogCancel>
<AlertDialogAction onClick={alertHandler}>
{actionLabel}
</AlertDialogAction>
{closeLabel && (
<AlertDialogCancel onClick={closeAlert}>
{closeLabel}
</AlertDialogCancel>
)}
{actionLabel && (
<AlertDialogAction onClick={alertHandler}>
{actionLabel}
</AlertDialogAction>
)}
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>

View File

@@ -222,7 +222,7 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
onChange={handleInputChange}
/>
{errorMessage && (
<p className="text-red-500 text-sm mt-2">
<p className="text-red-700 text-sm mt-2">
{errorMessage}
</p>
)}

View File

@@ -20,7 +20,7 @@ import {
import { databaseTypeToLabelMap } from '@/lib/databases';
import { DatabaseType } from '@/lib/domain/database-type';
import { DialogProps } from '@radix-ui/react-dialog';
import { Sparkles } from 'lucide-react';
import { Annoyed, Sparkles } from 'lucide-react';
import React, { useCallback, useEffect } from 'react';
export interface ExportSQLDialogProps {
@@ -35,6 +35,7 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
const { closeExportSQLDialog } = useDialog();
const { currentDiagram } = useChartDB();
const [script, setScript] = React.useState<string>();
const [error, setError] = React.useState<boolean>(false);
const exportSQLScript = useCallback(async () => {
if (targetDatabaseType === DatabaseType.GENERIC) {
@@ -47,12 +48,59 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
useEffect(() => {
if (!dialog.open) return;
setScript(undefined);
setError(false);
const fetchScript = async () => {
const script = await exportSQLScript();
setScript(script);
try {
const script = await exportSQLScript();
setScript(script);
} catch (e) {
setError(true);
}
};
fetchScript();
}, [dialog.open, setScript, exportSQLScript]);
}, [dialog.open, setScript, exportSQLScript, setError]);
const renderError = useCallback(
() => (
<div className="flex flex-col gap-2">
<div className="flex items-center justify-center flex-col text-sm gap-1">
<Annoyed className="w-10 h-10" />
<Label className="text-sm">
Error generating SQL script. Please try again later or{' '}
<Button
variant="link"
className="text-pink-600 p-0"
onClick={() =>
window.open(
'mailto:chartdb.io@gmail.com',
'_blank'
)
}
>
contact us
</Button>
.
</Label>
<div>
Feel free to use your OPENAI_TOKEN, see the manual{' '}
<Button
variant="link"
className="text-pink-600 p-0"
onClick={() =>
window.open(
'https://github.com/chartdb/chartdb',
'_blank'
)
}
>
here.
</Button>
</div>
</div>
</div>
),
[]
);
const renderLoader = useCallback(
() => (
@@ -98,7 +146,9 @@ export const ExportSQLDialog: React.FC<ExportSQLDialogProps> = ({
</DialogDescription>
</DialogHeader>
<div className="flex flex-1 items-center justify-center">
{(script?.length ?? 0) === 0 ? (
{error ? (
renderError()
) : (script?.length ?? 0) === 0 ? (
renderLoader()
) : (
<CodeSnippet

View File

@@ -1,3 +1,4 @@
export interface ChartDBConfig {
defaultDiagramId: string;
exportActions?: Date[];
}

View File

@@ -1 +1,3 @@
export const OPENAI_API_KEY = import.meta.env.VITE_OPENAI_API_KEY;
export const OPENAI_API_KEY: string = import.meta.env.VITE_OPENAI_API_KEY;
export const IS_CHARTDB_IO: boolean =
import.meta.env.VITE_IS_CHARTDB_IO === 'true';

View File

@@ -32,6 +32,8 @@ import {
databaseTypeToLabelMap,
} from '@/lib/databases';
import { DatabaseType } from '@/lib/domain/database-type';
import { useConfig } from '@/hooks/use-config';
import { IS_CHARTDB_IO } from '@/lib/env';
export interface TopNavbarProps {}
@@ -49,6 +51,7 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
openExportSQLDialog,
showAlert,
} = useDialog();
const { config, updateConfig } = useConfig();
const [editMode, setEditMode] = useState(false);
const { exportImage } = useExportImage();
const [editedDiagramName, setEditedDiagramName] =
@@ -109,6 +112,69 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
);
}, []);
const exportSQL = useCallback(
(databaseType: DatabaseType) => {
if (databaseType === DatabaseType.GENERIC) {
openExportSQLDialog({
targetDatabaseType: DatabaseType.GENERIC,
});
return;
}
if (IS_CHARTDB_IO) {
const now = new Date();
const lastExportsInLastHalfHour =
config?.exportActions?.filter(
(date) =>
now.getTime() - date.getTime() < 30 * 60 * 1000
) ?? [];
if (lastExportsInLastHalfHour.length >= 5) {
showAlert({
title: 'Export SQL Limit Reached',
content: (
<div className="flex text-sm gap-1 flex-col">
<div>
We set a budget to allow the community to
check the feature. You have reached the
limit of 5 AI exports every 30min.
</div>
<div>
Feel free to use your OPENAI_TOKEN, see the
manual{' '}
<Button
variant="link"
className="text-pink-600 p-0"
onClick={() =>
window.open(
'https://github.com/chartdb/chartdb',
'_blank'
)
}
>
here.
</Button>
</div>
</div>
),
closeLabel: 'Close',
});
return;
}
updateConfig({
exportActions: [...lastExportsInLastHalfHour, now],
});
}
openExportSQLDialog({
targetDatabaseType: databaseType,
});
},
[config?.exportActions, updateConfig, showAlert, openExportSQLDialog]
);
const emojiAI = '✨';
return (
@@ -146,20 +212,16 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
<MenubarSubContent>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.GENERIC,
})
exportSQL(DatabaseType.GENERIC)
}
>
{databaseTypeToLabelMap['generic']}
</MenubarItem>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.POSTGRESQL,
})
exportSQL(
DatabaseType.POSTGRESQL
)
}
>
{
@@ -173,10 +235,7 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
</MenubarItem>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.MYSQL,
})
exportSQL(DatabaseType.MYSQL)
}
>
{databaseTypeToLabelMap['mysql']}
@@ -186,10 +245,9 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
</MenubarItem>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.SQL_SERVER,
})
exportSQL(
DatabaseType.SQL_SERVER
)
}
>
{
@@ -203,10 +261,7 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
</MenubarItem>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.MARIADB,
})
exportSQL(DatabaseType.MARIADB)
}
>
{databaseTypeToLabelMap['mariadb']}
@@ -216,10 +271,7 @@ export const TopNavbar: React.FC<TopNavbarProps> = () => {
</MenubarItem>
<MenubarItem
onClick={() =>
openExportSQLDialog({
targetDatabaseType:
DatabaseType.SQLITE,
})
exportSQL(DatabaseType.SQLITE)
}
>
{databaseTypeToLabelMap['sqlite']}