mirror of
https://github.com/chartdb/chartdb.git
synced 2025-11-11 09:27:33 +00:00
add different clients support
This commit is contained in:
committed by
Guy Ben-Aharon
parent
1fbf7cc677
commit
eee9832fe1
31
package-lock.json
generated
31
package-lock.json
generated
@@ -25,6 +25,7 @@
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
@@ -2192,6 +2193,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
|
||||
"integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.0",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-presence": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-roving-focus": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.1.tgz",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.2.1",
|
||||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
|
||||
@@ -7,17 +7,19 @@ export interface CodeSnippetProps {
|
||||
className?: string;
|
||||
codeProps?: CodeBlockProps;
|
||||
code: string;
|
||||
language?: 'sql' | 'bash';
|
||||
}
|
||||
|
||||
export const CodeSnippet: React.FC<CodeSnippetProps> = ({
|
||||
className,
|
||||
codeProps,
|
||||
code,
|
||||
language = 'sql',
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn('flex flex-1', className)}>
|
||||
<CopyBlock
|
||||
language="sql"
|
||||
language={language}
|
||||
text={code}
|
||||
theme={atomOneDark}
|
||||
customStyle={{
|
||||
|
||||
53
src/components/tabs/tabs.tsx
Normal file
53
src/components/tabs/tabs.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import * as React from 'react';
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Tabs = TabsPrimitive.Root;
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
@@ -15,9 +15,9 @@ import { generateId } from '@/lib/utils';
|
||||
import { useChartDB } from '@/hooks/use-chartdb';
|
||||
import { useDialog } from '@/hooks/use-dialog';
|
||||
import { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||
import { CreateDiagramDialogSelectDatabase } from './create-diagram-dialog-select-database/create-diagram-dialog-select-database';
|
||||
import { SelectDatabaseStep } from './select-database-step/select-database-step';
|
||||
import { CreateDiagramDialogStep } from './create-diagram-dialog-step';
|
||||
import { CreateDiagramDialogImportDatabase } from './create-diagram-dialog-import-database/create-diagram-dialog-import-database';
|
||||
import { ImportDatabaseStep } from './import-database-step/import-database-step';
|
||||
|
||||
const errorScriptOutputMessage =
|
||||
'Invalid JSON. Please correct it or contact us at chartdb.io@gmail.com for help.';
|
||||
@@ -142,11 +142,11 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
className="flex w-[90vw] flex-col overflow-visible xl:min-w-[45vw]"
|
||||
className="flex max-h-[90vh] w-[90vw] flex-col overflow-y-auto md:overflow-visible xl:min-w-[45vw]"
|
||||
showClose={hasExistingDiagram}
|
||||
>
|
||||
{step === CreateDiagramDialogStep.SELECT_DATABASE ? (
|
||||
<CreateDiagramDialogSelectDatabase
|
||||
<SelectDatabaseStep
|
||||
createNewDiagram={createNewDiagram}
|
||||
databaseType={databaseType}
|
||||
hasExistingDiagram={hasExistingDiagram}
|
||||
@@ -154,7 +154,7 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
|
||||
setStep={setStep}
|
||||
/>
|
||||
) : (
|
||||
<CreateDiagramDialogImportDatabase
|
||||
<ImportDatabaseStep
|
||||
createNewDiagram={createNewDiagram}
|
||||
databaseEdition={databaseEdition}
|
||||
databaseType={databaseType}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button } from '@/components/button/button';
|
||||
import {
|
||||
DialogClose,
|
||||
@@ -26,8 +26,14 @@ import {
|
||||
import { CreateDiagramDialogStep } from '../create-diagram-dialog-step';
|
||||
import { SSMSInfo } from './ssms-info/ssms-info';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/tabs/tabs';
|
||||
import {
|
||||
DatabaseClient,
|
||||
databaseClientToLabelMap,
|
||||
databaseTypeToClientsMap,
|
||||
} from '@/lib/domain/database-clients';
|
||||
|
||||
export interface CreateDiagramDialogImportDatabaseProps {
|
||||
export interface ImportDatabaseStepProps {
|
||||
setStep: React.Dispatch<React.SetStateAction<CreateDiagramDialogStep>>;
|
||||
createNewDiagram: () => void;
|
||||
scriptResult: string;
|
||||
@@ -40,9 +46,7 @@ export interface CreateDiagramDialogImportDatabaseProps {
|
||||
errorMessage: string;
|
||||
}
|
||||
|
||||
export const CreateDiagramDialogImportDatabase: React.FC<
|
||||
CreateDiagramDialogImportDatabaseProps
|
||||
> = ({
|
||||
export const ImportDatabaseStep: React.FC<ImportDatabaseStepProps> = ({
|
||||
setScriptResult,
|
||||
setStep,
|
||||
scriptResult,
|
||||
@@ -52,6 +56,10 @@ export const CreateDiagramDialogImportDatabase: React.FC<
|
||||
setDatabaseEdition,
|
||||
errorMessage,
|
||||
}) => {
|
||||
const databaseClients = databaseTypeToClientsMap[databaseType];
|
||||
const [databaseClient, setDatabaseClient] = useState<
|
||||
DatabaseClient | undefined
|
||||
>();
|
||||
const { t } = useTranslation();
|
||||
const handleInputChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
@@ -158,12 +166,56 @@ export const CreateDiagramDialogImportDatabase: React.FC<
|
||||
<SSMSInfo />
|
||||
)}
|
||||
</div>
|
||||
{databaseTypeToClientsMap[databaseType].length > 0 ? (
|
||||
<Tabs
|
||||
value={
|
||||
!databaseClient ? 'dbclient' : databaseClient
|
||||
}
|
||||
onValueChange={(value) => {
|
||||
setDatabaseClient(
|
||||
value === 'dbclient'
|
||||
? undefined
|
||||
: (value as DatabaseClient)
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-1">
|
||||
<TabsList className="h-8 justify-start rounded-none rounded-t-sm ">
|
||||
<TabsTrigger
|
||||
value="dbclient"
|
||||
className="h-6 w-20"
|
||||
>
|
||||
DB Client
|
||||
</TabsTrigger>
|
||||
|
||||
{databaseClients?.map((client) => (
|
||||
<TabsTrigger
|
||||
key={client}
|
||||
value={client}
|
||||
className="h-6 !w-20"
|
||||
>
|
||||
{databaseClientToLabelMap[client]}
|
||||
</TabsTrigger>
|
||||
)) ?? []}
|
||||
</TabsList>
|
||||
</div>
|
||||
<CodeSnippet
|
||||
className="max-h-40 w-full"
|
||||
code={importMetadataScripts[databaseType]({
|
||||
databaseEdition,
|
||||
databaseClient,
|
||||
})}
|
||||
language={databaseClient ? 'bash' : 'sql'}
|
||||
/>
|
||||
</Tabs>
|
||||
) : (
|
||||
<CodeSnippet
|
||||
className="max-h-40 w-full"
|
||||
code={importMetadataScripts[databaseType]({
|
||||
databaseEdition,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex h-48 flex-col gap-1">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -192,6 +244,8 @@ export const CreateDiagramDialogImportDatabase: React.FC<
|
||||
handleInputChange,
|
||||
scriptResult,
|
||||
setDatabaseEdition,
|
||||
databaseClients,
|
||||
databaseClient,
|
||||
t,
|
||||
]);
|
||||
|
||||
@@ -16,7 +16,7 @@ import { LayoutGrid } from 'lucide-react';
|
||||
import { CreateDiagramDialogStep } from '../create-diagram-dialog-step';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface CreateDiagramDialogSelectDatabaseProps {
|
||||
export interface SelectDatabaseStepProps {
|
||||
setStep: React.Dispatch<React.SetStateAction<CreateDiagramDialogStep>>;
|
||||
databaseType: DatabaseType;
|
||||
setDatabaseType: React.Dispatch<React.SetStateAction<DatabaseType>>;
|
||||
@@ -24,9 +24,7 @@ export interface CreateDiagramDialogSelectDatabaseProps {
|
||||
createNewDiagram: () => void;
|
||||
}
|
||||
|
||||
export const CreateDiagramDialogSelectDatabase: React.FC<
|
||||
CreateDiagramDialogSelectDatabaseProps
|
||||
> = ({
|
||||
export const SelectDatabaseStep: React.FC<SelectDatabaseStepProps> = ({
|
||||
setStep,
|
||||
databaseType,
|
||||
setDatabaseType,
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DatabaseClient } from '@/lib/domain/database-clients';
|
||||
import {
|
||||
DatabaseEdition,
|
||||
databaseEditionToLabelMap,
|
||||
@@ -6,6 +7,7 @@ import {
|
||||
export const getPostgresQuery = (
|
||||
options: {
|
||||
databaseEdition?: DatabaseEdition;
|
||||
databaseClient?: DatabaseClient;
|
||||
} = {}
|
||||
): string => {
|
||||
const databaseEdition: DatabaseEdition | undefined =
|
||||
@@ -227,5 +229,11 @@ SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
|
||||
FROM fk_info${databaseEdition ? '_' + databaseEdition : ''}, pk_info, cols, indexes_metadata, tbls, config, views;
|
||||
`;
|
||||
|
||||
if (options.databaseClient === DatabaseClient.POSTGRESQL_PSQL) {
|
||||
return `psql -h HOST_NAME -p PORT -U USER_NAME -d DATABASE_NAME -c "
|
||||
${query}
|
||||
" -t -A | pbcopy; LG='\\033[0;32m'; NC='\\033[0m'; echo "You got the resultset ($(pbpaste | wc -c | xargs) characters) in Copy/Paste. \${LG}Go back & paste in ChartDB :)\${NC}";`;
|
||||
}
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
@@ -5,10 +5,14 @@ import { sqliteQuery } from './sqlite-script';
|
||||
import { sqlServerQuery } from './sqlserver-script';
|
||||
import { mariaDBQuery } from './maria-script';
|
||||
import { DatabaseEdition } from '@/lib/domain/database-edition';
|
||||
import { DatabaseClient } from '@/lib/domain/database-clients';
|
||||
|
||||
export const importMetadataScripts: Record<
|
||||
DatabaseType,
|
||||
(options?: { databaseEdition?: DatabaseEdition }) => string
|
||||
(options?: {
|
||||
databaseEdition?: DatabaseEdition;
|
||||
databaseClient?: DatabaseClient;
|
||||
}) => string
|
||||
> = {
|
||||
[DatabaseType.GENERIC]: () => '',
|
||||
[DatabaseType.POSTGRESQL]: getPostgresQuery,
|
||||
|
||||
21
src/lib/domain/database-clients.ts
Normal file
21
src/lib/domain/database-clients.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { DatabaseType } from './database-type';
|
||||
|
||||
export enum DatabaseClient {
|
||||
// PostgreSQL
|
||||
POSTGRESQL_PSQL = 'psql',
|
||||
}
|
||||
|
||||
export const databaseClientToLabelMap: Record<DatabaseClient, string> = {
|
||||
// PostgreSQL
|
||||
[DatabaseClient.POSTGRESQL_PSQL]: 'PSQL',
|
||||
};
|
||||
|
||||
export const databaseTypeToClientsMap: Record<DatabaseType, DatabaseClient[]> =
|
||||
{
|
||||
[DatabaseType.POSTGRESQL]: [DatabaseClient.POSTGRESQL_PSQL],
|
||||
[DatabaseType.MYSQL]: [],
|
||||
[DatabaseType.SQLITE]: [],
|
||||
[DatabaseType.GENERIC]: [],
|
||||
[DatabaseType.SQL_SERVER]: [],
|
||||
[DatabaseType.MARIADB]: [],
|
||||
};
|
||||
Reference in New Issue
Block a user