8000 SQL-Designer/sql-designer.html at main · davidsangouard/SQL-Designer · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1830 lines (1616 loc) · 78.8 KB

File metadata and controls

1830 lines (1616 loc) · 78.8 KB
<!--
═════════════════════════════════════════════════════════
██████╗ ███████╗
██╔══██╗██╔════╝
██║ ██║███████╗ David Sangouard
██║ ██║╚════██║
██████╔╝███████║ github.com/davidsangouard
╚═════╝ ╚══════╝
» Architect of digital solutions
» Building the web, one pixel at a time
═════════════════════════════════════════════════════════
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SQL Designer - Visual Database Schema Builder</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--midnight: #212842;
--cream: #F0E7D5;
--accent: #4A90E2;
--success: #6BCF7F;
--warning: #F39C12;
--danger: #E74C3C;
--border: rgba(240, 231, 213, 0.1);
--hover: rgba(240, 231, 213, 0.05);
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
background: var(--midnight);
color: var(--cream);
height: 100vh;
overflow: hidden;
}
.container {
display: grid;
grid-template-columns: 280px 1fr 320px;
height: 100vh;
}
.sidebar {
background: rgba(0, 0, 0, 0.2);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
overflow: hidden;
}
.header {
padding: 24px;
border-bottom: 1px solid var(--border);
}
.logo {
font-size: 20px;
font-weight: 600;
letter-spacing: -0.5px;
display: flex;
align-items: center;
gap: 12px;
}
.logo svg {
width: 24px;
height: 24px;
}
.section {
padding: 20px;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.section-title {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1px;
opacity: 0.5;
margin-bottom: 12px;
}
.btn {
width: 100%;
padding: 12px 16px;
background: var(--accent);
color: var(--cream);
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
}
.btn-secondary {
background: transparent;
border: 1px solid var(--border);
}
.btn-secondary:hover {
background: var(--hover);
box-shadow: none;
}
.btn-sm {
padding: 8px 12px;
font-size: 13px;
}
.tables-list {
flex: 1;
overflow-y: auto;
padding: 20px;
}
.table-item {
padding: 12px;
background: var(--hover);
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 8px;
cursor: move;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: space-between;
}
.table-item:hover {
background: rgba(240, 231, 213, 0.08);
border-color: var(--accent);
}
.table-item.selected {
background: rgba(74, 144, 226, 0.15);
border-color: var(--accent);
}
.table-item-name {
font-weight: 500;
font-size: 14px;
}
.table-item-fields {
font-size: 12px;
opacity: 0.6;
margin-top: 4px;
}
.icon-btn {
background: none;
border: none;
color: var(--cream);
opacity: 0.6;
cursor: pointer;
padding: 4px;
transition: opacity 0.2s;
}
.icon-btn:hover {
opacity: 1;
}
.canvas {
position: relative;
background:
repeating-linear-gradient(0deg, transparent, transparent 49px, rgba(240, 231, 213, 0.03) 49px, rgba(240, 231, 213, 0.03) 50px),
repeating-linear-gradient(90deg, transparent, transparent 49px, rgba(240, 231, 213, 0.03) 49px, rgba(240, 231, 213, 0.03) 50px);
overflow: auto;
}
.table-node {
position: absolute;
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border);
border-radius: 8px;
min-width: 240px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
}
.table-node.selected {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
.table-header {
padding: 16px;
border-bottom: 1px solid var(--border);
font-weight: 600;
cursor: move;
display: flex;
align-items: center;
justify-content: space-between;
}
.table-body {
padding: 8px;
}
.field-row {
padding: 8px 12px;
display: grid;
grid-template-columns: 1fr auto auto;
gap: 8px;
align-items: center;
border-radius: 4px;
transition: background 0.2s;
font-size: 13px;
}
.field-row:hover {
background: var(--hover);
}
.field-name {
font-weight: 500;
}
.field-type {
opacity: 0.6;
font-size: 12px;
}
.field-key {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
}
.properties-panel {
background: rgba(0, 0, 0, 0.2);
border-left: 1px solid var(--border);
overflow-y: auto;
}
.panel-section {
padding: 20px;
border-bottom: 1px solid var(--border);
}
.form-group {
margin-bottom: 16px;
}
.form-label {
display: block;
font-size: 12px;
opacity: 0.7;
margin-bottom: 6px;
}
.form-input {
width: 100%;
padding: 10px 12px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--cream);
font-size: 14px;
transition: all 0.2s;
}
.form-input:focus {
outline: none;
border-color: var(--accent);
background: rgba(0, 0, 0, 0.4);
}
textarea.form-input {
resize: vertical;
font-family: 'Courier New', monospace;
line-height: 1.5;
}
.form-select {
width: 100%;
padding: 10px 12px;
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--border);
border-radius: 6px;
color: var(--cream);
font-size: 14px;
cursor: pointer;
}
.checkbox-group {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
cursor: pointer;
}
.checkbox-label input {
cursor: pointer;
}
.fields-list {
max-height: 300px;
overflow-y: auto;
}
.field-item {
padding: 12px;
background: var(--hover);
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 8px;
}
.field-item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.toolbar {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 8px;
background: rgba(0, 0, 0, 0.6);
padding: 8px;
border-radius: 8px;
border: 1px solid var(--border);
backdrop-filter: blur(10px);
z-index: 100;
}
.toolbar-btn {
padding: 8px 16px;
background: transparent;
border: none;
color: var(--cream);
cursor: pointer;
border-radius: 6px;
font-size: 13px;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.2s;
}
.toolbar-btn:hover {
background: var(--hover);
}
.toolbar-btn.active {
background: var(--accent);
}
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: none;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal.active {
display: flex;
}
.modal-content {
background: var(--midnight);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.modal-title {
font-size: 18px;
font-weight: 600;
}
.code-block {
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border);
border-radius: 6px;
padding: 16px;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
overflow-x: auto;
white-space: pre;
}
.btn-group {
display: flex;
gap: 8px;
margin-top: 16px;
}
.connection-line {
position: absolute;
pointer-events: none;
z-index: 1;
}
.empty-state {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
opacity: 0.3;
}
.empty-state svg {
width: 64px;
height: 64px;
margin-bottom: 16px;
opacity: 0.5;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(240, 231, 213, 0.2);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(240, 231, 213, 0.3);
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="header">
<div class="logo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 3v18"/>
</svg>
SQL Designer
</div>
</div>
<div class="section">
<div class="section-title">Actions</div>
<button class="btn" onclick="app.addTable()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
New Table
</button>
</div>
<div class="tables-list" id="tablesList">
<div class="empty-state">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18"/>
<path d="M9 3v18"/>
</svg>
<div>No tables yet</div>
</div>
</div>
<div class="section">
<button class="btn btn-secondary btn-sm" onclick="app.importSQL()">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="17 8 12 3 7 8"/>
<line x1="12" y1="3" x2="12" y2="15"/>
</svg>
Import SQL
</button>
<button class="btn btn-secondary btn-sm" onclick="app.exportSQL()" style="margin-top: 8px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Export SQL
</button>
</div>
</div>
<div class="canvas" id="canvas">
<div class="toolbar">
<button class="toolbar-btn" onclick="app.autoArrange()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7"/>
<rect x="14" y="3" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/>
<rect x="3" y="14" width="7" height="7"/>
</svg>
</button>
<button class="toolbar-btn" onclick="app.zoomIn()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
<line x1="11" y1="8" x2="11" y2="14"/>
<line x1="8" y1="11" x2="14" y2="11"/>
</svg>
</button>
<button class="toolbar-btn" onclick="app.zoomOut()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="11" cy="11" r="8"/>
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
<line x1="8" y1="11" x2="14" y2="11"/>
</svg>
</button>
<button class="toolbar-btn" onclick="app.resetView()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/>
<path d="M21 3v5h-5"/>
</svg>
</button>
<button class="toolbar-btn" onclick="app.clearAll()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>
</button>
</div>
<svg id="connections" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 1;"></svg>
</div>
<div class="properties-panel" id="propertiesPanel">
<div class="panel-section">
<div class="section-title">Table Properties</div>
<div class="form-group">
<label class="form-label">Table Name</label>
<input type="text" class="form-input" id="tableName" placeholder="users">
</div>
<div class="form-group">
<label class="form-label">Comment</label>
<input type="text" class="form-input" id="tableComment& 5276 quot; placeholder="User accounts table">
</div>
<div class="form-group">
<label class="form-label">Engine</label>
<select class="form-select" id="tableEngine">
<option>InnoDB</option>
<option>MyISAM</option>
<option>Memory</option>
<option>Archive</option>
<option>CSV</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Charset</label>
<select class="form-select" id="tableCharset">
<option>utf8mb4</option>
<option>utf8</option>
<option>latin1</option>
<option>ascii</option>
<option>binary</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Collation</label>
<select class="form-select" id="tableCollation">
<option>utf8mb4_unicode_ci</option>
<option>utf8mb4_general_ci</option>
<option>utf8_general_ci</option>
<option>latin1_swedish_ci</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Auto Increment</label>
<input type="number" class="form-input" id="tableAutoIncrement" placeholder="1">
</div>
<div class="form-group">
<label class="form-label">Row Format</label>
<select class="form-select" id="tableRowFormat">
<option>Dynamic</option>
<option>Compact</option>
<option>Redundant</option>
<option>Compressed</option>
</select>
</div>
</div>
<div class="panel-section">
<div class="section-title">Fields</div>
<div class="fields-list" id="fieldsList"></div>
<button class="btn btn-secondary btn-sm" onclick="app.addField()" style="margin-top: 12px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="12" y1="5" x2="12" y2="19"/>
<line x1="5" y1="12" x2="19" y2="12"/>
</svg>
Add Field
</button>
</div>
<div class="panel-section">
<div class="section-title">Foreign Keys</div>
<div class="fields-list" id="foreignKeysList"></div>
<button class="btn btn-secondary btn-sm" onclick="app.addForeignKey()" style="margin-top: 12px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/>
</svg>
Add Foreign Key
</button>
</div>
<div class="panel-section">
<div class="section-title">Indexes</div>
<div class="fields-list" id="indexesList"></div>
<button class="btn btn-secondary btn-sm" onclick="app.addIndex()" style="margin-top: 12px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Add Index
</button>
</div>
<div class="panel-section">
<button class="btn" onclick="app.saveTableProperties()">Save Changes</button>
<button class="btn btn-secondary btn-sm" onclick="app.duplicateTable()" style="margin-top: 8px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
Duplicate Table
</button>
<button class="btn btn-secondary btn-sm" onclick="app.deleteTable()" style="margin-top: 8px;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="3 6 5 6 21 6"/>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
</svg>
Delete Table
</button>
</div>
</div>
</div>
<div class="modal" id="sqlModal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">Export SQL Schema</div>
<button class="icon-btn" onclick="app.closeModal()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<div class="form-group">
<label class="form-label">Export Options</label>
<div class="checkbox-group">
<label class="checkbox-label">
<input type="checkbox" id="exportDropTables" checked>
Include DROP TABLE
</label>
<label class="checkbox-label">
<input type="checkbox" id="exportIfNotExists">
IF NOT EXISTS
</label>
<label class="checkbox-label">
<input type="checkbox" id="exportAutoIncrement" checked>
AUTO_INCREMENT values
</label>
<label class="checkbox-label">
<input type="checkbox" id="exportComments" checked>
Include comments
</label>
</div>
</div>
<div class="code-block" id="sqlOutput"></div>
<div class="btn-group">
<button class="btn" onclick="app.copySQLToClipboard()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
Copy to Clipboard
</button>
<button class="btn btn-secondary" onclick="app.downloadSQL()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Download SQL
</button>
</div>
</div>
</div>
<div class="modal" id="importModal">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title">Import SQL Schema</div>
<button class="icon-btn" onclick="app.closeModal()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<div class="form-group">
<label class="form-label">Choose Import Method</label>
<div class="btn-group">
<button class="btn btn-secondary" onclick="document.getElementById('fileInput').click()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
<polyline points="13 2 13 9 20 9"/>
</svg>
Upload SQL File
</button>
</div>
<input type="file" id="fileInput" accept=".sql" style="display: none;" onchange="app.handleFileUpload(event)">
</div>
<div class="form-group">
<label class="form-label">Or Paste SQL Code</label>
<textarea class="form-input" id="sqlInput" rows="15" placeholder="Paste your SQL CREATE TABLE statements here..."></textarea>
</div>
<div class="btn-group">
<button class="btn" onclick="app.processSQLImport()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="20 6 9 17 4 12"/>
</svg>
Import Schema
</button>
<button class="btn btn-secondary" onclick="app.closeModal()">Cancel</button>
</div>
</div>
</div>
<script>
const app = {
tables: [],
selectedTable: null,
nextTableId: 1,
init() {
this.setupEventListeners();
this.loadFromLocalStorage();
this.render();
},
setupEventListeners() {
const canvas = document.getElementById('canvas');
canvas.addEventListener('click', (e) => {
if (e.target === canvas) {
this.selectTable(null);
}
});
document.getElementById('tableName').addEventListener('input', () => this.updateSelectedTableProperty());
document.getElementById('tableComment').addEventListener('input', () => this.updateSelectedTableProperty());
document.getElementById('tableEngine').addEventListener('change', () => this.updateSelectedTableProperty());
document.getElementById('tableCharset').addEventListener('change', () => this.updateSelectedTableProperty());
document.getElementById('tableCollation').addEventListener('change', () => this.updateSelectedTableProperty());
document.getElementById('tableAutoIncrement').addEventListener('input', () => this.updateSelectedTableProperty());
document.getElementById('tableRowFormat').addEventListener('change', () => this.updateSelectedTableProperty());
},
addTable() {
const table = {
id: this.nextTableId++,
name: `table_${this.tables.length + 1}`,
comment: '',
engine: 'InnoDB',
charset: 'utf8mb4',
collation: 'utf8mb4_unicode_ci',
autoIncrement: 1,
rowFormat: 'Dynamic',
x: 100 + (this.tables.length * 50),
y: 100 + (this.tables.length * 50),
fields: [
{
name: 'id',
type: 'INT',
length: '11',
nullable: false,
autoIncrement: true,
primary: true,
unique: false,
index: false,
unsigned: true,
zerofill: false,
default: '',
comment: 'Primary key',
onUpdate: ''
}
],
foreignKeys: [],
indexes: []
};
this.tables.push(table);
this.selectTable(table);
this.render();
this.saveToLocalStorage();
},
duplicateTable() {
if (!this.selectedTable) return;
const duplicated = JSON.parse(JSON.stringify(this.selectedTable));
duplicated.id = this.nextTableId++;
duplicated.name = this.selectedTable.name + '_copy';
duplicated.x += 50;
duplicated.y += 50;
this.tables.push(duplicated);
this.selectTable(duplicated);
this.render();
this.saveToLocalStorage();
},
deleteTable() {
if (!this.selectedTable) return;
if (!confirm(`Delete table "${this.selectedTable.name}"?`)) return;
const index = this.tables.findIndex(t => t.id === this.selectedTable.id);
if (index > -1) {
this.tables.splice(index, 1);
this.selectTable(null);
this.render();
this.saveToLocalStorage();
}
},
selectTable(table) {
this.selectedTable = table;
this.renderProperties();
this.render();
},
updateSelectedTableProperty() {
if (!this.selectedTable) return;
this.selectedTable.name = document.getElementById('tableName').value;
this.selectedTable.comment = document.getElementById('tableComment').value;
this.selectedTable.engine = document.getElementById('tableEngine').value;
this.selectedTable.charset = document.getElementById('tableCharset').value;
this.selectedTable.collation = document.getElementById('tableCollation').value;
this.selectedTable.autoIncrement = parseInt(document.getElementById('tableAutoIncrement').value) || 1;
this.selectedTable.rowFormat = document.getElementById('tableRowFormat').value;
this.render();
this.saveToLocalStorage();
},
addField() {
if (!this.selectedTable) return;
const field = {
name: `field_${this.selectedTable.fields.length + 1}`,
type: 'VARCHAR',
length: '255',
nullable: true,
autoIncrement: false,
primary: false,
unique: false,
index: false,
unsigned: false,
zerofill: false,
default: '',
comment: '',
onUpdate: ''
};
this.selectedTable.fields.push(field);
this.renderProperties();
this.render(); // Update canvas
this.saveToLocalStorage();
},
deleteField(index) {
if (!this.selectedTable) return;
if (this.selectedTable.fields.length === 1) {
alert('Table must have at least one field');
return;
}
this.selectedTable.fields.splice(index, 1);
this.renderProperties();
this.render(); // Update canvas
this.saveToLocalStorage();
},
addForeignKey() {
if (!this.selectedTable) return;
const fk = {
name: `fk_${this.selectedTable.name}_${this.selectedTable.foreignKeys.length + 1}`,
field: this.selectedTable.fields[0]?.name || '',
referenceTable: '',
referenceField: '',
onDelete: 'RESTRICT',
onUpdate: 'RESTRICT'
};
this.selectedTable.foreignKeys.push(fk);
this.renderProperties();
this.render(); // Update canvas for connections
this.saveToLocalStorage();
},
deleteForeignKey(index) {
if (!this.selectedTable) return;
this.selectedTable.foreignKeys.splice(index, 1);
this.renderProperties();
this.render(); // Update canvas for connections
this.saveToLocalStorage();
},
addIndex() {
if (!this.selectedTable) return;
const idx = {
name: `idx_${this.selectedTable.name}_${this.selectedTable.indexes.length + 1}`,
fields: [this.selectedTable.fields[0]?.name || ''],
type: 'INDEX'
};
this.selectedTable.indexes.push(idx);
this.renderProperties();
this.render(); // Update canvas
this.saveToLocalStorage();
},
deleteIndex(index) {
if (!this.selectedTable) return;
this.selectedTable.indexes.splice(index, 1);
this.renderProperties();
this.render(); // Update canvas
this.saveToLocalStorage();
},
saveTableProperties() {
if (!this.selectedTable) return;
// Save table properties
this.selectedTable.name = document.getElementById('tableName').value;
this.selectedTable.comment = document.getElementById('tableComment').value;
this.selectedTable.engine = document.getElementById('tableEngine').value;
this.selectedTable.charset = document.getElementById('tableCharset').value;
this.selectedTable.collation = document.getElementById('tableCollation').value;
this.selectedTable.autoIncrement = parseInt(document.getElementById('tableAutoIncrement').value) || 1;
this.selectedTable.rowFormat = document.getElementById('tableRowFormat').value;
0