Skip to content

Commit 96ed782

Browse files
committed
feat: add create database modal
1 parent 119369c commit 96ed782

50 files changed

Lines changed: 224 additions & 133 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,6 @@ jobs:
8888
- name: Install deps
8989
run: pnpm install --frozen-lockfile --prefer-offline
9090

91-
- name: Install deps
92-
run: pnpm run prettier:check
93-
9491
build-windows:
9592
name: Build Windows & Create Release
9693
runs-on: windows-latest

apps/desktop/main/ipc/handlers/connection.handler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface ConnectionHistoryItem {
1212
user: string
1313
password?: string
1414
database: string
15-
connectionType: 'postgresql' | 'mysql' | 'sqlite'
15+
type: 'postgresql' | 'mysql' | 'sqlite'
1616
ssl?: boolean
1717
lastUsed?: number
1818
}
@@ -37,7 +37,7 @@ export function registerConnectionHandlers() {
3737
c.port === connection.port &&
3838
c.user === connection.user &&
3939
c.database === connection.database &&
40-
c.connectionType === connection.connectionType
40+
c.type === connection.type
4141
)
4242

4343
const newConnection = { ...connection, lastUsed: Date.now() }
@@ -61,7 +61,7 @@ export function registerConnectionHandlers() {
6161
c.port === connection.port &&
6262
c.user === connection.user &&
6363
c.database === connection.database &&
64-
c.connectionType === connection.connectionType
64+
c.type === connection.type
6565
)
6666
)
6767
store.set('history', filtered)

apps/desktop/main/services/db/adapter.registry.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

apps/desktop/main/services/db/connection.manager.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import type { DbAdapterPort } from '@datary/core'
1+
import type { ConnectionProps, DbAdapterPort } from '@datary/core'
22
import {
3-
type DBConnectionConfig,
43
MysqlAdapter,
54
MysqlMetadataRepository,
65
PostgresAdapter,
@@ -15,8 +14,8 @@ export class ConnectionManager {
1514

1615
private metadataRepo: any = null
1716

18-
public async connect(config: DBConnectionConfig) {
19-
switch (config.connectionType) {
17+
public async connect(config: ConnectionProps) {
18+
switch (config.type) {
2019
case 'postgresql':
2120
this.adapter = new PostgresAdapter(config)
2221
await this.adapter.connect()
@@ -34,10 +33,10 @@ export class ConnectionManager {
3433
break
3534

3635
default:
37-
throw new Error(`Unsupported DB type: ${config.connectionType}`)
36+
throw new Error(`Unsupported DB type: ${config.type}`)
3837
}
3938

40-
logger.info(`DB connected (${config.connectionType})`)
39+
logger.info(`DB connected (${config.type})`)
4140
}
4241

4342
public getAdapterType() {

apps/desktop/preload/expose/db.api.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { DBConnectionConfig } from '@datary/db'
1+
import type { ConnectionProps } from '@datary/core'
22
import { IPC_CHANNELS } from '@datary/ipc'
33
import { ipcRenderer } from 'electron'
44

55
export const dbApi = {
6-
connect: (config: DBConnectionConfig) => ipcRenderer.invoke(IPC_CHANNELS.DB_CONNECT, config),
6+
connect: (config: ConnectionProps) => ipcRenderer.invoke(IPC_CHANNELS.DB_CONNECT, config),
77

88
getAdapterType: () => ipcRenderer.invoke(IPC_CHANNELS.DB_GET_ADAPTER_TYPE),
99

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type ConnectionType = 'postgresql' | 'mysql' | 'mariadb' | 'sqlite' | 'mssql'
1+
export type ConnectionType = 'postgresql' | 'mysql' | 'mariadb' | 'mssql'
22

33
export interface DatabaseConnection {
44
id: string
@@ -7,7 +7,7 @@ export interface DatabaseConnection {
77
port: number
88
user: string
99
database: string
10-
connectionType: ConnectionType
10+
type: ConnectionType
1111
ssl: boolean
1212
lastConnected?: string
1313
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { zodResolver } from '@hookform/resolvers/zod'
2+
import { useForm } from 'react-hook-form'
3+
import toast from 'react-hot-toast'
4+
5+
import { DATABASE_ENCODINGS } from '../lib/constants'
6+
import {
7+
type CreateDatabaseFormValues,
8+
createDatabaseSchema
9+
} from '../lib/validators/create-database'
10+
11+
import { Button } from '@/shared/ui/button'
12+
import {
13+
Dialog,
14+
DialogContent,
15+
DialogDescription,
16+
DialogFooter,
17+
DialogHeader,
18+
DialogTitle
19+
} from '@/shared/ui/dialog'
20+
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/shared/ui/form'
21+
import { Input } from '@/shared/ui/input'
22+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
23+
24+
interface Props {
25+
open: boolean
26+
onOpenChange: (open: boolean) => void
27+
}
28+
29+
export function CreateDatabaseDialog({ open, onOpenChange }: Props) {
30+
const form = useForm<CreateDatabaseFormValues>({
31+
// @ts-ignore
32+
resolver: zodResolver(createDatabaseSchema),
33+
defaultValues: {
34+
name: '',
35+
encoding: 'utf8'
36+
}
37+
})
38+
39+
const handleSubmit = (values: CreateDatabaseFormValues) => {
40+
toast.success(`Database "${values.name}" successfully created`)
41+
onOpenChange(false)
42+
}
43+
44+
return (
45+
<Dialog open={open} onOpenChange={onOpenChange}>
46+
<DialogContent className="sm:max-w-130">
47+
<DialogHeader>
48+
<DialogTitle>Create New Database</DialogTitle>
49+
<DialogDescription>
50+
Enter the database name and choose encoding.
51+
</DialogDescription>
52+
</DialogHeader>
53+
54+
<Form {...form}>
55+
<form
56+
onSubmit={
57+
// @ts-ignore
58+
form.handleSubmit(handleSubmit)
59+
}
60+
className="space-y-4 pt-3"
61+
>
62+
<FormField
63+
// @ts-ignore
64+
control={form.control}
65+
name="name"
66+
render={({ field }) => (
67+
<FormItem>
68+
<FormLabel>Database Name</FormLabel>
69+
<FormControl>
70+
<Input placeholder="mydb" {...field} />
71+
</FormControl>
72+
<FormMessage />
73+
</FormItem>
74+
)}
75+
/>
76+
77+
<FormField
78+
// @ts-ignore
79+
control={form.control}
80+
name="encoding"
81+
render={({ field }) => (
82+
<FormItem>
83+
<FormLabel>Encoding</FormLabel>
84+
<FormControl>
85+
<Select onValueChange={field.onChange} value={field.value}>
86+
<SelectTrigger className="w-full">
87+
<SelectValue placeholder="Select encoding" />
88+
</SelectTrigger>
89+
<SelectContent position="popper">
90+
{DATABASE_ENCODINGS.map(enc => (
91+
<SelectItem key={enc} value={enc}>
92+
{enc.toUpperCase()}
93+
</SelectItem>
94+
))}
95+
</SelectContent>
96+
</Select>
97+
</FormControl>
98+
<FormMessage />
99+
</FormItem>
100+
)}
101+
/>
102+
103+
<DialogFooter>
104+
<Button variant="outline" onClick={() => onOpenChange(false)}>
105+
Cancel
106+
</Button>
107+
<Button type="submit">Create</Button>
108+
</DialogFooter>
109+
</form>
110+
</Form>
111+
</DialogContent>
112+
</Dialog>
113+
)
114+
}

apps/desktop/renderer/src/modules/explorer/components/sidebar/SidebarHeader.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import { Database, PanelLeftClose, PanelLeftOpen, Plus } from 'lucide-react'
2+
import { useState } from 'react'
3+
4+
import { CreateDatabaseDialog } from '../CreateDatabaseDialog'
25

36
import { Button } from '@/shared/ui/button'
7+
import {
8+
DropdownMenu,
9+
DropdownMenuContent,
10+
DropdownMenuItem,
11+
DropdownMenuTrigger
12+
} from '@/shared/ui/dropdown-menu'
413
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
514
import { Tooltip, TooltipContent, TooltipTrigger } from '@/shared/ui/tooltip'
615

@@ -21,6 +30,8 @@ export function SidebarHeader({
2130
onSelectDatabase,
2231
onNewQuery
2332
}: Props) {
33+
const [createDialogOpen, setCreateDialogOpen] = useState(false)
34+
2435
if (collapsed) {
2536
return (
2637
<div className="border-sidebar-border flex items-center justify-center border-b py-2.5">
@@ -63,10 +74,22 @@ export function SidebarHeader({
6374
</SelectContent>
6475
</Select>
6576

66-
<Button variant="ghost" size="icon" className="h-8 w-8 shrink-0" onClick={onNewQuery}>
67-
<Plus className="h-4 w-4" />
68-
<span className="sr-only">New query</span>
69-
</Button>
77+
<DropdownMenu>
78+
<DropdownMenuTrigger asChild>
79+
<Button variant="ghost" size="icon">
80+
<Plus className="h-4 w-4" />
81+
<span className="sr-only">New</span>
82+
</Button>
83+
</DropdownMenuTrigger>
84+
<DropdownMenuContent align="start">
85+
<DropdownMenuItem onClick={() => setCreateDialogOpen(true)}>
86+
New Database
87+
</DropdownMenuItem>
88+
<DropdownMenuItem onClick={onNewQuery}>New Query</DropdownMenuItem>
89+
</DropdownMenuContent>
90+
</DropdownMenu>
91+
92+
<CreateDatabaseDialog open={createDialogOpen} onOpenChange={setCreateDialogOpen} />
7093
</div>
7194
)
7295
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const DATABASE_ENCODINGS = ['utf8', 'latin5', 'utf16', 'ascii'] as const
2+
3+
export type DatabaseEncoding = (typeof DATABASE_ENCODINGS)[number]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { z } from 'zod'
2+
3+
import { DATABASE_ENCODINGS, type DatabaseEncoding } from '../constants'
4+
5+
export const createDatabaseSchema = z.object({
6+
name: z.string().min(1, 'Database name is required'),
7+
encoding: z
8+
.enum(DATABASE_ENCODINGS)
9+
.default('utf8')
10+
.transform(val => val as DatabaseEncoding)
11+
})
12+
13+
export type CreateDatabaseFormValues = z.infer<typeof createDatabaseSchema>

0 commit comments

Comments
 (0)