code editor implemented

This commit is contained in:
KarimTamani
2025-06-06 21:16:39 +01:00
parent b9a996ba7a
commit 2c7765350d
9 changed files with 170 additions and 42 deletions
+3
View File
@@ -10,6 +10,7 @@
"preview": "vite preview"
},
"dependencies": {
"@codemirror/lang-sql": "^6.9.0",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@heroui/react": "^2.7.6",
@@ -20,6 +21,7 @@
"@radix-ui/react-tooltip": "^1.2.3",
"@react-aria/visually-hidden": "3.8.21",
"@react-types/shared": "3.28.0",
"@uiw/react-codemirror": "^4.23.12",
"@xyflow/react": "^12.6.3",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.43.1",
@@ -35,6 +37,7 @@
"react-dom": "18.3.1",
"react-i18next": "^15.5.1",
"react-router-dom": "6.23.0",
"sql-formatter": "^15.6.3",
"tailwind-merge": "^3.2.0",
"tailwind-variants": "0.3.0",
"tailwindcss": "3.4.16",
+5 -3
View File
@@ -6,6 +6,7 @@ import { useEffect, useState } from "react";
import { Parser } from "node-sql-parser";
import { DatabaseType } from "@/lib/schemas/database-schema";
import { format } from 'sql-formatter';
const parser = new Parser();
@@ -16,13 +17,14 @@ export const useRenderSql = (database: DatabaseType) => {
useEffect(() => {
const dbAst: any = DatabaseToAst(database, data_types);
const formattedSqlCode = format(parser.sqlify(dbAst), { language: 'sql' }); // or 'mysql', 'postgresql', etc.
setSql(
parser.sqlify(dbAst)
formattedSqlCode
);
}, [database]) ;
}, [database]);
return sql ;
return sql;
}
+9 -5
View File
@@ -2,7 +2,7 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { tables } from './table-schema';
import { data_types, DataType } from './data-type-schema';
import { InferInsertModel, InferSelectModel, relations } from 'drizzle-orm';
import { relationships } from './relationship-schema';
import { relationships, RelationshipType } from './relationship-schema';
import { field_indices } from './field_index-schema';
import { indices } from './index-schema';
@@ -32,8 +32,8 @@ export const fieldsRelations = relations(fields, ({ one, many }) => ({
fields: [fields.typeId],
references: [data_types.id]
}),
sourceRelations: one(relationships),
targetRelations: one(relationships),
sourceRelations: many(relationships),
targetRelations: many(relationships),
fieldIndices: many(field_indices),
indices: many(indices)
@@ -42,8 +42,12 @@ export const fieldsRelations = relations(fields, ({ one, many }) => ({
export interface FieldType extends InferSelectModel<typeof fields> {
sequence: number,
type: DataType
type: DataType,
sourceRelations?: RelationshipType[],
targetRelations?: RelationshipType[]
};
export interface FieldInsertType extends InferInsertModel<typeof fields> { };
export interface FieldInsertType extends InferInsertModel<typeof fields> {
};
+3 -2
View File
@@ -4,7 +4,7 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { tables } from './table-schema';
import { InferInsertModel, InferSelectModel, relations } from 'drizzle-orm';
import { field_indices, FieldIndexInsertType, FieldIndexType } from './field_index-schema';
import { fields } from './field-schema';
import { fields, FieldType } from './field-schema';
@@ -32,7 +32,8 @@ export const indicesRlationships = relations(indices, ({ one, many }) => ({
export interface IndexType extends InferSelectModel<typeof indices> {
fieldIndices: FieldIndexType[]
fieldIndices: FieldIndexType[] ;
fields : FieldType[] ;
};
export interface IndexInsertType extends InferInsertModel<typeof indices> {
fieldIndices? : FieldIndexInsertType[]
+5 -2
View File
@@ -1,7 +1,7 @@
import { InferInsertModel, InferSelectModel, relations } from 'drizzle-orm';
import { sqliteTable, text, real, integer } from 'drizzle-orm/sqlite-core';
import { fields, FieldType } from './field-schema';
import { relationships } from './relationship-schema';
import { relationships, RelationshipType } from './relationship-schema';
import { databases } from './database-schema';
import { IndexType, indices } from './index-schema';
@@ -43,11 +43,14 @@ export const tablesRelations = relations(tables, ({ many, one }) => ({
export interface TableType extends InferSelectModel<typeof tables> {
fields: FieldType[];
indices : IndexType[] ;
sourceRelations? :RelationshipType[] ;
targetRelations? : RelationshipType[] ;
};
export interface TableInsertType extends InferInsertModel<typeof tables> {
fields?: FieldType[];
};
@@ -1,27 +1,67 @@
import { useRenderSql } from "@/hooks/user-render-sql";
import { useDatabase } from "@/providers/database-provider/database-provider";
import React from "react";
import React, { useEffect, useMemo } from "react";
import CodeMirror from '@uiw/react-codemirror';
import { sql } from '@codemirror/lang-sql';
import { oneDark } from '@codemirror/theme-one-dark';
import { useTheme } from "next-themes";
import { Parser } from "node-sql-parser";
const parser = new Parser();
const code = `
-- Addresses table with a foreign key to users
CREATE TABLE addresses (
id INTEGER PRIMARY KEY,
user_id INT NOT NULL,
street VARCHAR(255),
city VARCHAR(100),
state VARCHAR(100),
postal_code VARCHAR(20),
country VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Indexes to improve lookup performance
CREATE UNIQUE INDEX idx_addresses_user_id ON addresses(user_id);
CREATE INDEX idx_addresses_city ON addresses(city , country);
`
const SqlPreview: React.FC = ({ }) => {
const { database } = useDatabase();
const sqlCode = useRenderSql(database);
const {resolvedTheme} = useTheme();
useEffect(() => {
const ast = parser.astify(code )
console.log (ast)
}, [])
const SqlPreview : React.FC = ({}) => {
const {database} = useDatabase() ;
const sql = useRenderSql(database) ;
return(
<p>
{sql
return (
<div className="flex w-full h-full ">
{
<CodeMirror
value={sqlCode}
className="flex flex-1 w-full"
extensions={[sql()]}
theme={resolvedTheme == "light" ? undefined : oneDark}
/>
}
</p>
</div>
)
}
}
export default React.memo(SqlPreview) ;
export default React.memo(SqlPreview);
@@ -79,15 +79,10 @@ const TablesController: React.FC<Props> = ({ }) => {
}, [nameRef, allTables]);
const toggleSqlPreview = useCallback(() => {
setShowSqlPreview(preview => !preview);
}, [])
return (
<div className="w-full h-full flex flex-col gap-2">
<div className="flex items-center justify-between gap-4 py-1">
@@ -179,7 +174,11 @@ const TablesController: React.FC<Props> = ({ }) => {
</div>
}
{
showSqlPreview && <SqlPreview/>
showSqlPreview &&
<div className=" flex-1 overflow-auto ">
<SqlPreview />
</div>
}
</div>
)
+5
View File
@@ -173,4 +173,9 @@ tbody[role="rowgroup"] tr td:last-child {
tbody[role="rowgroup"] tr td:first-child {
border-radius: 4px 0px 0px 4px !important;
}
.cm-editor {
min-width : 100% !important ;
}
+82 -11
View File
@@ -3,38 +3,94 @@
import { DataType } from "@/lib/schemas/data-type-schema";
import { DatabaseType } from "@/lib/schemas/database-schema";
import { FieldType } from "@/lib/schemas/field-schema";
import { FieldIndexType } from "@/lib/schemas/field_index-schema";
import { IndexType } from "@/lib/schemas/index-schema";
import { RelationshipType } from "@/lib/schemas/relationship-schema";
import { TableType } from "@/lib/schemas/table-schema";
export const DatabaseToAst = (database: DatabaseType , data_types : DataType[]) => {
export const DatabaseToAst = (database: DatabaseType, data_types: DataType[]) => {
let dbAst: any = [];
if ( ! data_types || data_types.length == 0)
return dbAst ;
if (!data_types || data_types.length == 0)
return dbAst;
for (const table of database.tables) {
dbAst.push(TableToAst(table , data_types ))
dbAst.push(TableToAst({
...table,
sourceRelations: database.relationships.filter((relationship: RelationshipType) => relationship.sourceTableId == table.id),
targetRelations: database.relationships.filter((relationship: RelationshipType) => relationship.targetTableId == table.id),
}, data_types));
if (table.indices && table.indices.length > 0)
for (const index of table.indices)
dbAst.push(IndexToAst({
...index,
fields: index.fieldIndices.map((fieldIndex: FieldIndexType) => table.fields.find((field: FieldType) => field.id == fieldIndex.fieldId)) as FieldType[]
}, table));
};
return dbAst;
}
export const TableToAst = (table: TableType , data_types : DataType[]) => {
export const TableToAst = (table: TableType, data_types: DataType[]) => {
const constraints: any[] = table.sourceRelations ? table.sourceRelations?.map((relationship: RelationshipType) => {
return {
constraint_type: "FOREIGN KEY",
resource: "constraint",
definition: [
{
type: "column_ref",
column: relationship.sourceField.name,
}
],
reference_definition: {
on_action: [
{
"type": "on delete",
"value": {
"type": "origin",
"value": "cascade"
}
}
],
keyword: "references",
table: [{
table: relationship.targetTable.name,
}],
definition: [
{
type: "column_ref",
column: relationship.targetField.name,
}
],
}
}
}) : [];
const filed_definitions = table.fields.map((field: FieldType) => FieldToAst({
...field,
type: data_types.find((dataType: DataType) => dataType.id == field.typeId) as DataType,
sourceRelations: table.sourceRelations?.filter((relationship: RelationshipType) => relationship.sourceFieldId == field.id),
targetRelations: table.targetRelations?.filter((relationship: RelationshipType) => relationship.targetFieldId == field.id),
}));
return {
keyword: "table",
type: "create",
table: [{
table: table.name
}],
create_definitions: table.fields.map((field: FieldType) => FieldToAst({
...field ,
type : data_types.find((dataType : DataType) => dataType.id == field.typeId) as DataType
} ))
create_definitions: [...filed_definitions, ...constraints]
}
}
export const FieldToAst = (field: FieldType ) => {
export const FieldToAst = (field: FieldType) => {
return {
column: {
type: "column_ref",
@@ -51,7 +107,7 @@ export const FieldToAst = (field: FieldType ) => {
value: field.nullable ? "null" : "not null",
},
definition: {
dataType: field.type?.name?.toLocaleUpperCase() ,
dataType: field.type?.name?.toLocaleUpperCase(),
},
primary_key: field.isPrimary ? "primary key" : null,
@@ -60,3 +116,18 @@ export const FieldToAst = (field: FieldType ) => {
}
export const IndexToAst = (index: IndexType, table: TableType) => {
return {
index: index.name,
type: "create",
table: { table: table.name },
keyword: "index",
index_type: index.unique ? "unique" : null,
index_columns: index.fields.map((field: FieldType) => ({
column: field.name,
type: "column_ref"
}))
}
}