Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

schema database trigger #221

Merged
merged 17 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"sql-formatter": "^15.3.2",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7",
"use-immer": "^0.11.0",
"xlsx": "^0.18.5",
"zod": "^3.22.4"
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/gui/query-explanation-diagram/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function QueryExplanationFlow(props: LayoutFlowProps) {
setEdges(build.edges as Edge[])
setLoading(false)
}
}, [props, loading])
}, [props, loading, setEdges, setNodes])

return (
<ReactFlow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useSchema } from "@/context/schema-provider";
import { LucideAlertCircle, LucideLoader, LucideSave } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { useDatabaseDriver } from "@/context/driver-provider";
import { DatabaseSchemaChange } from "@/drivers/base-driver";
import { SchemaDatabaseCollation } from "./schema-database-collation";

export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?: string; onClose: () => void; }) {
const { databaseDriver } = useDatabaseDriver();
const { schema, refresh: refreshSchema } = useSchema();
const [loading, setLoading] = useState(false);
const [currentCollate, setCurrentCollate] = useState('');
const [isExecuting, setIsExecuting] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const [value, setValue] = useState<DatabaseSchemaChange>({
Expand All @@ -27,40 +24,6 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:
return databaseDriver.createUpdateDatabaseSchema(value).join(";\n");
}, [databaseDriver, value]);

const fetchData = useCallback(async () => {
setLoading(true);

try {
const { rows } = await databaseDriver.query(`
SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${schemaName}';
`)

if (rows.length > 0) {
setValue({
...value,
name: {
new: schemaName,
old: schemaName
},
collate: String(rows[0].DEFAULT_COLLATION_NAME)
})
setCurrentCollate(String(rows[0].DEFAULT_COLLATION_NAME))
}
} catch (error) {
//
} finally {
setLoading(false);
}
}, [databaseDriver, schemaName, value])

useEffect(() => {
if (schemaName) {
fetchData().then().catch(console.error);
}
}, [])

// const toggleSave = useCallback(() => setSaving(!isSaving), [isSaving])

const onSave = useCallback(() => {
{
setIsExecuting(true);
Expand All @@ -75,7 +38,6 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:

const schemaNames = Object.keys(schema).filter(s => s !== schemaName).map(s => s);
const schemaNameExists = schemaNames.includes(value.name.new || '');
const isChange = value.name.new !== value.name.old || currentCollate !== value.collate

return (
<div className="flex h-full flex-col overflow-hidden relative">
Expand All @@ -100,32 +62,20 @@ export function SchemaDatabaseCreateForm({ schemaName, onClose }: { schemaName?:
}
})
}}
disabled={loading || !!schemaName}
disabled={!!schemaName}
className={`w-full ${schemaNameExists ? 'border-red-600' : ''}`}
/>
{
schemaNameExists && <small className="text-xs text-red-500">The schema name `{value.name.new}` already exists.</small>
}
</div>
<div>
<div className="text-xs font-medium mb-1">Collation</div>
<SchemaDatabaseCollation
value={value.collate}
onChange={(selectedSchema) => {
setValue({
...value,
collate: selectedSchema
})
}}
/>
</div>
</div>
<div className="w-full flex flex-col pt-3">
<div className="grow-0 shrink-0">
<div className="p-1 flex gap-2 justify-end">
<Button
variant="ghost"
disabled={!!schemaNameExists || loading || !isChange}
disabled={!!schemaNameExists}
size={"sm"}
onClick={onSave}
>
Expand Down

This file was deleted.

23 changes: 17 additions & 6 deletions src/components/gui/schema-sidebar-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,9 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
(item?: DatabaseSchemaItem) => {
const selectedName = item?.name;
const isTable = item?.type === "table";
const isTrigger = item?.type === "trigger";

return [
item?.type === "schema" && {
title: "Edit",
onClick: () => {
setEditSchema(item.schemaName);
},
},
{
title: "Copy Name",
disabled: !selectedName,
Expand Down Expand Up @@ -176,6 +171,22 @@ export default function SchemaList({ search }: Readonly<SchemaListProps>) {
},
}
: undefined,
databaseDriver.getFlags().supportCreateUpdateTrigger
? { separator: true }
: undefined,
databaseDriver.getFlags().supportCreateUpdateTrigger
? {
title: isTrigger ? "Edit Trigger" : "Create New Trigger",
onClick: () => {
openTab({
type: "trigger",
schemaName: item?.schemaName ?? currentSchemaName,
name: isTrigger ? item.name : "",
tableName: item?.tableSchema?.tableName,
});
},
}
: undefined,
databaseDriver.getFlags().supportCreateUpdateTable
? { separator: true }
: undefined,
Expand Down
113 changes: 76 additions & 37 deletions src/components/gui/schema-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,74 @@
import { LucideSearch } from "lucide-react";
import { useCallback, useState } from "react";
import { useMemo, useState } from "react";
import SchemaList from "./schema-sidebar-list";
import { openTab } from "@/messages/open-tab";
import { useSchema } from "@/context/schema-provider";
import { cn } from "@/lib/utils";
import { buttonVariants } from "../ui/button";
import { Plus } from "@phosphor-icons/react";
import { useDatabaseDriver } from "@/context/driver-provider";
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
import { Command, CommandItem, CommandList } from "../ui/command";
import SchemaCreateDialog from "./schema-editor/schema-create";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";

export default function SchemaView() {
const [search, setSearch] = useState("");
const { databaseDriver } = useDatabaseDriver();
const { currentSchemaName } = useSchema();
const [isCreateSchema, setIsCreateSchema] = useState(false);

const onNewTable = useCallback(() => {
openTab({
type: "schema",
schemaName: currentSchemaName,
});
}, [currentSchemaName]);
const contentMenu = useMemo(() => {
const items: {
name: string;
onClick: () => void;
}[] = [];

const toggleNewDate = useCallback(() => setIsCreateSchema(!isCreateSchema), [isCreateSchema])
const flags = databaseDriver.getFlags();

const ActivatorButton = () => {
if (flags.supportCreateUpdateTable) {
items.push({
name: "Create Table",
onClick: () => {
openTab({
type: "schema",
schemaName: currentSchemaName,
});
},
});
}

if (flags.supportCreateUpdateDatabase) {
items.push({
name: "Create Database/Schema",
onClick: () => {
setIsCreateSchema(true);
},
});
}

if (!databaseDriver.getFlags().supportCreateUpdateDatabase && !databaseDriver.getFlags().supportCreateUpdateTable) {
return <></>
if (flags.supportCreateUpdateTrigger) {
items.push({
name: "Create Trigger",
onClick: () => {
openTab({
type: "trigger",
schemaName: currentSchemaName,
});
},
});
}

if (databaseDriver.getFlags().dialect === 'sqlite' && databaseDriver.getFlags().supportCreateUpdateTable) {
return items;
}, [databaseDriver, currentSchemaName]);

const activatorButton = useMemo(() => {
if (contentMenu.length === 0) return null;

if (contentMenu.length === 1) {
return (
<button
className={cn(
Expand All @@ -41,16 +77,16 @@ export default function SchemaView() {
}),
"rounded-full h-8 w-8 bg-neutral-800 dark:bg-neutral-200"
)}
onClick={onNewTable}
onClick={contentMenu[0].onClick}
>
<Plus size={16} weight="bold" />
</button>
)
);
}

return (
<Popover>
<PopoverTrigger asChild>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={cn(
buttonVariants({
Expand All @@ -61,30 +97,33 @@ export default function SchemaView() {
>
<Plus size={16} weight="bold" />
</button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandList>
{
databaseDriver.getFlags().supportCreateUpdateDatabase && <CommandItem onSelect={toggleNewDate}>New schema/database</CommandItem>
}
{
databaseDriver.getFlags().supportCreateUpdateTable && <CommandItem onSelect={() => onNewTable()}>New table</CommandItem>
}
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}

</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="start">
{contentMenu.map((menu) => {
return (
<DropdownMenuItem key={menu.name} onClick={menu.onClick}>
{menu.name}
</DropdownMenuItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}, [contentMenu]);
return (
<div className="flex flex-col overflow-hidden grow">
{isCreateSchema && <SchemaCreateDialog onClose={toggleNewDate} />}
{isCreateSchema && (
<SchemaCreateDialog
onClose={() => {
setIsCreateSchema(false);
}}
/>
)}

<div className="p-4 pb-2 flex flex-col">
<div className="flex justify-between mb-5 items-center">
<h1 className="text-xl font-medium text-primary">Tables</h1>
<ActivatorButton />
{activatorButton}
</div>

<div className="overflow-hidden cursor-text items-center has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 has-[:focus]:outline-neutral-400/70 has-[:enabled]:active:outline-neutral-400/70 dark:has-[:focus]:outline-neutral-600 dark:has-[:enabled]:active:outline-neutral-600 flex w-full rounded-md bg-white px-3 py-2.5 text-base text-neutral-900 outline outline-1 outline-neutral-200 focus:outline-neutral-400/70 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-neutral-900 dark:text-white dark:outline-neutral-800 dark:focus:outline-neutral-600 h-[32px]">
Expand Down
Loading
Loading