create diagram dialog refactor

This commit is contained in:
Guy Ben-Aharon
2024-09-01 16:43:08 +03:00
committed by Guy Ben-Aharon
parent e8f54c621a
commit dbe4b335e1
7 changed files with 476 additions and 354 deletions

View File

@@ -1,20 +1,7 @@
import React, { useCallback, useEffect, useState } from 'react';
import { Button } from '@/components/button/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/dialog/dialog';
import { Dialog, DialogContent } from '@/components/dialog/dialog';
import { DialogProps } from '@radix-ui/react-dialog';
import { ToggleGroup, ToggleGroupItem } from '@/components/toggle/toggle-group';
import { DatabaseType } from '@/lib/domain/database-type';
import { databaseLogoMap, databaseSecondaryLogoMap } from '@/lib/databases';
import { CodeSnippet } from '@/components/code-snippet/code-snippet';
import { Textarea } from '@/components/textarea/textarea';
import { useStorage } from '@/hooks/use-storage';
import { Diagram, loadFromDatabaseMetadata } from '@/lib/domain/diagram';
import { useNavigate } from 'react-router-dom';
@@ -27,25 +14,10 @@ import {
import { generateId } from '@/lib/utils';
import { useChartDB } from '@/hooks/use-chartdb';
import { useDialog } from '@/hooks/use-dialog';
import { importMetadataScripts } from '@/lib/data/import-metadata/scripts/scripts';
import { Link } from '@/components/link/link';
import { LayoutGrid } from 'lucide-react';
import {
DatabaseEdition,
databaseEditionToImageMap,
databaseEditionToLabelMap,
databaseTypeToEditionMap,
} from '@/lib/domain/database-edition';
import {
Avatar,
AvatarFallback,
AvatarImage,
} from '@/components/avatar/avatar';
enum CreateDiagramDialogStep {
SELECT_DATABASE = 'SELECT_DATABASE',
IMPORT_DATABASE = 'IMPORT_DATABASE',
}
import { DatabaseEdition } from '@/lib/domain/database-edition';
import { CreateDiagramDialogSelectDatabase } from './create-diagram-dialog-select-database';
import { CreateDiagramDialogStep } from './create-diagram-dialog-step';
import { CreateDiagramDialogImportDatabase } from './create-diagram-dialog-import-database';
const errorScriptOutputMessage =
'Invalid JSON. Please correct it or contact us at chartdb.io@gmail.com for help.';
@@ -112,11 +84,6 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
const hasExistingDiagram = (diagramId ?? '').trim().length !== 0;
const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
const inputValue = e.target.value;
setScriptResult(inputValue);
};
const createNewDiagram = useCallback(async () => {
let diagram: Diagram = {
id: generateId(),
@@ -161,319 +128,6 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
errorMessage,
]);
const renderDatabaseOption = useCallback((type: DatabaseType) => {
const logo = databaseLogoMap[type];
return (
<ToggleGroupItem
value={type}
aria-label="Toggle bold"
className="flex size-20 md:size-32"
>
<img src={logo} alt="PostgreSQL" />
</ToggleGroupItem>
);
}, []);
const renderExamplesOption = useCallback(
() => (
<div
className="flex size-20 cursor-pointer flex-col items-center rounded-md border py-3 text-center md:size-32"
onClick={() => window.open('/examples')}
>
<div className="flex flex-1 items-center">
<Link href="/examples" className="text-sm text-primary">
<LayoutGrid size={34} />
</Link>
</div>
<div className="flex flex-col-reverse">
<Link
href="/examples"
className="hidden text-sm text-primary md:flex"
>
Check Examples
</Link>
<Link
href="/examples"
className="flex text-xs text-primary md:hidden"
>
Examples
</Link>
</div>
</div>
),
[]
);
const renderHeader = useCallback(() => {
switch (step) {
case CreateDiagramDialogStep.SELECT_DATABASE:
return (
<DialogHeader>
<DialogTitle>What is your Database?</DialogTitle>
<DialogDescription>
Each database has its own unique features and
capabilities.
</DialogDescription>
</DialogHeader>
);
case CreateDiagramDialogStep.IMPORT_DATABASE:
return (
<DialogHeader>
<DialogTitle>Import your Database</DialogTitle>
</DialogHeader>
);
default:
return null;
}
}, [step]);
const renderContent = useCallback(() => {
switch (step) {
case CreateDiagramDialogStep.SELECT_DATABASE:
return (
<div className="flex flex-1 items-center justify-center">
<ToggleGroup
value={databaseType}
onValueChange={(value: DatabaseType) => {
if (!value) {
setDatabaseType(DatabaseType.GENERIC);
} else {
setDatabaseType(value);
setStep(
CreateDiagramDialogStep.IMPORT_DATABASE
);
}
}}
type="single"
className="grid grid-flow-row grid-cols-3 gap-6"
>
{renderDatabaseOption(DatabaseType.MYSQL)}
{renderDatabaseOption(DatabaseType.POSTGRESQL)}
{renderDatabaseOption(DatabaseType.MARIADB)}
{renderDatabaseOption(DatabaseType.SQLITE)}
{renderDatabaseOption(DatabaseType.SQL_SERVER)}
{renderExamplesOption()}
</ToggleGroup>
</div>
);
case CreateDiagramDialogStep.IMPORT_DATABASE:
return (
<div className="flex w-full flex-1 flex-col gap-6">
{databaseTypeToEditionMap[databaseType].length > 0 ? (
<div className="flex flex-col gap-1 md:flex-row">
<p className="text-sm leading-6 text-muted-foreground">
Database edition:
</p>
<ToggleGroup
type="single"
className="ml-1 gap-2"
value={
!databaseEdition
? 'regular'
: databaseEdition
}
onValueChange={(value) => {
setDatabaseEdition(
value === 'regular'
? undefined
: (value as DatabaseEdition)
);
}}
>
<ToggleGroupItem
value="regular"
variant="outline"
className="h-6 gap-1 p-0 px-2 shadow-none"
>
<Avatar className="size-4">
<AvatarImage
src={
databaseSecondaryLogoMap[
databaseType
]
}
alt="Regular"
/>
<AvatarFallback>
Regular
</AvatarFallback>
</Avatar>
Regular
</ToggleGroupItem>
{databaseTypeToEditionMap[databaseType].map(
(edition) => (
<ToggleGroupItem
value={edition}
key={edition}
variant="outline"
className="h-6 gap-1 p-0 px-2 shadow-none"
>
<Avatar className="size-4">
<AvatarImage
src={
databaseEditionToImageMap[
edition
]
}
alt={
databaseEditionToLabelMap[
edition
]
}
/>
<AvatarFallback>
{
databaseEditionToLabelMap[
edition
]
}
</AvatarFallback>
</Avatar>
{
databaseEditionToLabelMap[
edition
]
}
</ToggleGroupItem>
)
)}
</ToggleGroup>
</div>
) : null}
<div className="flex flex-col gap-1">
<p className="text-sm text-muted-foreground">
1. Run this script in your database:
</p>
<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">
2. Paste the script result here:
</p>
<Textarea
className="w-full flex-1 rounded-md bg-muted p-2 text-sm"
placeholder="Script result here..."
value={scriptResult}
onChange={handleInputChange}
/>
{errorMessage && (
<p className="mt-2 text-sm text-red-700">
{errorMessage}
</p>
)}
</div>
</div>
);
default:
return null;
}
}, [
errorMessage,
databaseEdition,
step,
databaseType,
scriptResult,
renderDatabaseOption,
setDatabaseType,
renderExamplesOption,
]);
const renderFooter = useCallback(() => {
switch (step) {
case CreateDiagramDialogStep.SELECT_DATABASE:
return (
<DialogFooter className="mt-4 flex !justify-between gap-2">
{hasExistingDiagram ? (
<DialogClose asChild>
<Button type="button" variant="secondary">
Cancel
</Button>
</DialogClose>
) : (
<div></div>
)}
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:space-x-2">
<Button
type="button"
variant="outline"
onClick={createNewDiagram}
>
Empty diagram
</Button>
<Button
type="button"
variant="default"
disabled={databaseType === DatabaseType.GENERIC}
onClick={() =>
setStep(
CreateDiagramDialogStep.IMPORT_DATABASE
)
}
>
Continue
</Button>
</div>
</DialogFooter>
);
case CreateDiagramDialogStep.IMPORT_DATABASE:
return (
<DialogFooter className="mt-4 flex !justify-between gap-2">
<div className="flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2">
<Button
type="button"
variant="secondary"
onClick={() =>
setStep(
CreateDiagramDialogStep.SELECT_DATABASE
)
}
>
Back
</Button>
</div>
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end sm:space-x-2">
<DialogClose asChild>
<Button
type="button"
variant="outline"
onClick={createNewDiagram}
>
Empty diagram
</Button>
</DialogClose>
<DialogClose asChild>
<Button
type="button"
variant="default"
disabled={
scriptResult.trim().length === 0 ||
errorMessage.length > 0
}
onClick={createNewDiagram}
>
Import
</Button>
</DialogClose>
</div>
</DialogFooter>
);
default:
return null;
}
}, [
step,
databaseType,
scriptResult,
createNewDiagram,
hasExistingDiagram,
errorMessage,
]);
return (
<Dialog
{...dialog}
@@ -491,9 +145,26 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
className="flex w-[90vw] flex-col overflow-y-auto xl:min-w-[45vw]"
showClose={hasExistingDiagram}
>
{renderHeader()}
{renderContent()}
{renderFooter()}
{step === CreateDiagramDialogStep.SELECT_DATABASE ? (
<CreateDiagramDialogSelectDatabase
createNewDiagram={createNewDiagram}
databaseType={databaseType}
hasExistingDiagram={hasExistingDiagram}
setDatabaseType={setDatabaseType}
setStep={setStep}
/>
) : (
<CreateDiagramDialogImportDatabase
createNewDiagram={createNewDiagram}
databaseEdition={databaseEdition}
databaseType={databaseType}
errorMessage={errorMessage}
scriptResult={scriptResult}
setDatabaseEdition={setDatabaseEdition}
setStep={setStep}
setScriptResult={setScriptResult}
/>
)}
</DialogContent>
</Dialog>
);