mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-03 13:33:25 +00:00
error handle
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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'} />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export interface ChartDBConfig {
|
||||
defaultDiagramId: string;
|
||||
exportActions?: Date[];
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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']}
|
||||
|
||||
Reference in New Issue
Block a user