Skip to content
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
19 changes: 17 additions & 2 deletions src/renderer/src/components/connection-wizard/wizard-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,17 +151,32 @@ export function ConnectionWizard(): JSX.Element {
api
.post('/api/user/db/create/profile', filteredPayload)
.then((response) => {
const id = response.data.id as number
const id = response.data.id as string
setConnectionDetail((prev) => ({
...prev,
id: id
}))
createAnnotation(id)
})
.catch(() => {
toast.error('데이터베이스 연결 생성 중 오류가 발생했습니다.')
})
.finally(() => {
// NOTE: 페이지 새로고침 -> 저장된거 불러오도록
window.location.reload()
onClose()
})
}

onClose()
const createAnnotation = (db_profile_id: string): void => {
api
.post('/api/annotations/create', {
db_profile_id: db_profile_id
})
.then(() => {})
.catch(() => {
toast.error('어노테이션 생성 중 오류가 발생했습니다.')
})
}

return (
Expand Down
18 changes: 10 additions & 8 deletions src/renderer/src/components/erd/table-detail-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ import {
Table2,
KeyRound,
Link,
Square,
SquareDashedBottomIcon as SquareDashed,
Database,
Calendar,
Hash,
Type,
Check,
LucideProps
LucideProps,
Diamond
} from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
Expand All @@ -29,20 +28,20 @@ interface TableDetailSidebarProps {
const constraintIcons = {
primary: KeyRound,
foreign: Link,
'not-null': Square,
nullable: SquareDashed,
'not-null': Diamond,
nullable: Diamond,
unique: Hash,
index: Database
} as const

// 제약 조건들 색상
const constraintColors = {
primary: 'text-yellow-400 bg-yellow-400/10',
primary: 'text-purple-400 bg-purple-400/10',
foreign: 'text-blue-400 bg-blue-400/10',
'not-null': 'text-gray-400 bg-gray-400/10',
nullable: 'text-gray-500 bg-gray-500/10',
unique: 'text-green-400 bg-green-400/10',
index: 'text-purple-400 bg-purple-400/10'
index: 'text-yellow-400 bg-yellow-400/10'
} as const

// 제약 조건 표시
Expand Down Expand Up @@ -194,7 +193,10 @@ export function TableDetailSidebar({
className={`flex items-center gap-1 px-2 py-1 rounded text-xs ${colorClass}`}
title={label}
>
<IconComponent className="w-3 h-3" />
<IconComponent
className="w-3 h-3"
fill={constraint === 'not-null' ? 'gray' : 'black'}
/>
<span>{label}</span>
</div>
)
Expand Down
7 changes: 5 additions & 2 deletions src/renderer/src/components/layout/side-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import AppIcon from '@renderer/assets/icon.svg'
import { Database, Plus, Settings, Tag } from 'lucide-react'
import { cn } from '@/lib/utils'
import type { NavItem } from '../workspace/types'
import { useNavigate } from 'react-router-dom'
import { useLocation, useNavigate } from 'react-router-dom'

const bottomNavItems: NavItem[] = [
{
Expand Down Expand Up @@ -59,21 +59,24 @@ function NavButton({ item }: { item: NavItem }): React.JSX.Element {

export function Sidebar(): React.JSX.Element {
const navigate = useNavigate()
const location = useLocation()

const topNavItems: NavItem[] = [
{
id: 'database',
icon: Database,
active: true,
active: location.pathname === '/',
onClick: (): void | Promise<void> => navigate('/')
},
{
id: 'tags',
icon: Tag,
active: location.pathname === '/erd',
disabled: false,
onClick: (): void | Promise<void> => navigate('/erd')
} // TODO: DB 연결 후에 disabled: false
]

return (
<aside className="h-full flex flex-col bg-zinc-900 p-4 items-center gap-6">
<img src={AppIcon} className="size-6" alt="QGenie" />
Expand Down
38 changes: 38 additions & 0 deletions src/renderer/src/erd/db-schema-panel/annotation-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { DATABASES } from '@/components/connection-wizard/wizard.type'
import { AnnotationItemProps } from './annotation.types'

export default function AnnotationItem({
db,
index,
hasFocus,
onToggle
}: AnnotationItemProps): React.JSX.Element {
const handleToggle = (): void => {
onToggle(index)
}
const dbItem = DATABASES.find((d) => d.id === db.type)

const text = db.view_name ? db.view_name : db.type

return (
<div className="flex flex-col justify-start items-start w-full">
<div
data-state="Default"
className={`w-full pl-2 pr-2 py-1 rounded inline-flex justify-start items-center gap-1 cursor-pointer ${hasFocus && 'bg-genie-600'} overflow-hidden`}
onClick={handleToggle}
>
<div className="size-6 p-0 flex items-center justify-center shrink-0">
<img src={dbItem?.icon} className="w-full h-full" />
</div>
<div className="flex gap-1 overflow-auto items-center truncate shrink-0">
<div className="justify-start h-4 content-center text-neutral-200 text-xs font-semibold font-['Pretendard'] leading-none">
{text}
</div>
<div className="justify-start h-4 content-center text-neutral-400 text-xs font-normal font-['Pretendard'] leading-none">
{db.host}:{db.port}
</div>
</div>
</div>
</div>
)
}
71 changes: 71 additions & 0 deletions src/renderer/src/erd/db-schema-panel/annotation-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useState, useRef } from 'react'
import AnnotationItem from './annotation-item'
import { AnnotationNode } from './annotation.types'

interface AnnotationSidebarProps {
dbList: AnnotationNode[]
selectedDB: AnnotationNode | null
setSelectedDB: (selectedNode: AnnotationNode) => void
}

export default function AnnotationSidebar({
dbList,
selectedDB,
setSelectedDB
}: AnnotationSidebarProps): React.JSX.Element {
const [width, setWidth] = useState(220)
const isResizing = useRef(false)
const startX = useRef(0)
const startWidth = useRef(0)

const handleMouseDown = (e: React.MouseEvent): void => {
isResizing.current = true
startX.current = e.clientX
startWidth.current = width

document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}

const handleMouseMove = (e: MouseEvent): void => {
if (!isResizing.current) return
const deltaX = e.clientX - startX.current
const newWidth = startWidth.current + deltaX
if (newWidth > 120 && newWidth < 500) {
setWidth(newWidth)
}
}

const handleMouseUp = (): void => {
isResizing.current = false
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}

const handleToggle = (nodeId: number): void => {
setSelectedDB(dbList[nodeId])
}

return (
<div className="flex h-full">
<div
style={{ width, minWidth: 200, maxWidth: 500 }}
className="h-full p-3 bg-neutral-800 outline-1 flex-col justify-start items-start inline-flex select-none"
>
{dbList.map((db, index) => {
return (
<AnnotationItem
db={db}
hasFocus={selectedDB === db}
index={index}
onToggle={handleToggle}
key={db.id}
/>
)
})}
</div>

<div onMouseDown={handleMouseDown} className="w-1 cursor-col-resize bg-genie-800" />
</div>
)
}
15 changes: 15 additions & 0 deletions src/renderer/src/erd/db-schema-panel/annotation.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface AnnotationNode {
id: string
type: 'mysql' | 'sqlite' | 'postgresql' | 'oracle' | 'mariadb'
host: string
port: number
name?: string
view_name?: string
}

export interface AnnotationItemProps {
db: AnnotationNode
index: number
hasFocus: boolean
onToggle: (nodeIndex: number) => void
}
Loading