This commit is contained in:
Guy Ben-Aharon
2024-08-19 13:31:55 +03:00
parent fb805d2938
commit fbf6015efa
6 changed files with 308 additions and 111 deletions
+22 -22
View File
@@ -15,23 +15,23 @@ export interface ChartDBContext {
relationships: DBRelationship[];
// General operations
updateDiagramId: (id: string) => void;
updateDiagramName: (name: string) => void;
updateDiagramId: (id: string) => Promise<void>;
updateDiagramName: (name: string) => Promise<void>;
loadDiagram: (diagramId: string) => Promise<Diagram | undefined>;
// Database type operations
updateDatabaseType: (databaseType: DatabaseType) => void;
updateDatabaseType: (databaseType: DatabaseType) => Promise<void>;
// Table operations
createTable: () => DBTable;
addTable: (table: DBTable) => void;
createTable: () => Promise<DBTable>;
addTable: (table: DBTable) => Promise<void>;
getTable: (id: string) => DBTable | null;
removeTable: (id: string) => void;
updateTable: (id: string, table: Partial<DBTable>) => void;
updateTables: (tables: PartialExcept<DBTable, 'id'>[]) => void;
removeTable: (id: string) => Promise<void>;
updateTable: (id: string, table: Partial<DBTable>) => Promise<void>;
updateTables: (tables: PartialExcept<DBTable, 'id'>[]) => Promise<void>;
updateTablesState: (
updateFn: (tables: DBTable[]) => PartialExcept<DBTable, 'id'>[]
) => void;
) => Promise<void>;
// Field operations
getField: (tableId: string, fieldId: string) => DBField | null;
@@ -39,21 +39,21 @@ export interface ChartDBContext {
tableId: string,
fieldId: string,
field: Partial<DBField>
) => void;
removeField: (tableId: string, fieldId: string) => void;
createField: (tableId: string) => DBField;
addField: (tableId: string, field: DBField) => void;
) => Promise<void>;
removeField: (tableId: string, fieldId: string) => Promise<void>;
createField: (tableId: string) => Promise<DBField>;
addField: (tableId: string, field: DBField) => Promise<void>;
// Index operations
createIndex: (tableId: string) => DBIndex;
addIndex: (tableId: string, index: DBIndex) => void;
createIndex: (tableId: string) => Promise<DBIndex>;
addIndex: (tableId: string, index: DBIndex) => Promise<void>;
getIndex: (tableId: string, indexId: string) => DBIndex | null;
removeIndex: (tableId: string, indexId: string) => void;
removeIndex: (tableId: string, indexId: string) => Promise<void>;
updateIndex: (
tableId: string,
indexId: string,
index: Partial<DBIndex>
) => void;
) => Promise<void>;
// Relationship operations
createRelationship: (params: {
@@ -61,15 +61,15 @@ export interface ChartDBContext {
targetTableId: string;
sourceFieldId: string;
targetFieldId: string;
}) => DBRelationship;
addRelationship: (relationship: DBRelationship) => void;
}) => Promise<DBRelationship>;
addRelationship: (relationship: DBRelationship) => Promise<void>;
getRelationship: (id: string) => DBRelationship | null;
removeRelationship: (id: string) => void;
removeRelationships: (...ids: string[]) => void;
removeRelationship: (id: string) => Promise<void>;
removeRelationships: (...ids: string[]) => Promise<void>;
updateRelationship: (
id: string,
relationship: Partial<DBRelationship>
) => void;
) => Promise<void>;
}
export const chartDBContext = createContext<ChartDBContext>({
+240 -54
View File
@@ -1,7 +1,7 @@
import React from 'react';
import React, { useCallback } from 'react';
import { DBTable } from '@/lib/domain/db-table';
import { generateId, randomHSLA } from '@/lib/utils';
import { chartDBContext } from './chartdb-context';
import { ChartDBContext, chartDBContext } from './chartdb-context';
import { DatabaseType } from '@/lib/domain/database-type';
import { DBField } from '@/lib/domain/db-field';
import { DBIndex } from '@/lib/domain/db-index';
@@ -12,7 +12,7 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [diagramId, setDiagramId] = React.useState('');
const { getDiagram } = useData();
const db = useData();
const [diagramName, setDiagramName] = React.useState('New Diagram');
const [databaseType, setDatabaseType] = React.useState<DatabaseType>(
DatabaseType.GENERIC
@@ -22,15 +22,35 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
[]
);
const updateDatabaseType = setDatabaseType;
const updateDiagramId = setDiagramId;
const updateDiagramName = setDiagramName;
const addTable = (table: DBTable) => {
setTables((tables) => [...tables, table]);
const updateDatabaseType: ChartDBContext['updateDatabaseType'] = async (
databaseType
) => {
setDatabaseType(databaseType);
await db.updateDiagram({
id: diagramId,
attributes: {
databaseType,
},
});
};
const updateDiagramId: ChartDBContext['updateDiagramId'] = async (id) => {
const prevId = diagramId;
setDiagramId(id);
await db.updateDiagram({ id: prevId, attributes: { id } });
};
const updateDiagramName: ChartDBContext['updateDiagramName'] = async (
name
) => {
setDiagramName(name);
await db.updateDiagram({ id: diagramId, attributes: { name } });
};
const createTable = () => {
const addTable: ChartDBContext['addTable'] = (table: DBTable) => {
setTables((tables) => [...tables, table]);
return db.addTable({ diagramId, table });
};
const createTable: ChartDBContext['createTable'] = async () => {
const table: DBTable = {
id: generateId(),
name: `table_${tables.length + 1}`,
@@ -51,36 +71,51 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
color: randomHSLA(),
createdAt: Date.now(),
};
addTable(table);
await addTable(table);
return table;
};
const getTable = (id: string) =>
const getTable: ChartDBContext['getTable'] = (id: string) =>
tables.find((table) => table.id === id) ?? null;
const removeTable = (id: string) => {
const removeTable: ChartDBContext['removeTable'] = async (id: string) => {
setTables((tables) => tables.filter((table) => table.id !== id));
await db.deleteTable({ diagramId, id });
};
const updateTable = (id: string, table: Partial<DBTable>) => {
const updateTable: ChartDBContext['updateTable'] = async (
id: string,
table: Partial<DBTable>
) => {
setTables((tables) =>
tables.map((t) => (t.id === id ? { ...t, ...table } : t))
);
await db.updateTable({ id, attributes: table });
};
const updateTables = (tables: PartialExcept<DBTable, 'id'>[]) => {
const updateTables: ChartDBContext['updateTables'] = async (
tables: PartialExcept<DBTable, 'id'>[]
) => {
setTables((currentTables) =>
currentTables.map((table) => {
const updatedTable = tables.find((t) => t.id === table.id);
return updatedTable ? { ...table, ...updatedTable } : table;
})
);
const promises = [];
for (const table of tables) {
promises.push(db.updateTable({ id: table.id, attributes: table }));
}
await Promise.all(promises);
};
const updateTablesState = (
const updateTablesState: ChartDBContext['updateTablesState'] = async (
updateFn: (tables: DBTable[]) => PartialExcept<DBTable, 'id'>[]
) => {
const updatedTables = updateFn(tables);
setTables((prevTables) => {
const updatedTables = updateFn(prevTables);
return prevTables
@@ -96,9 +131,29 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
updatedTables.some((t) => t.id === prevTable.id)
);
});
const promises = [];
for (const updatedTable of updatedTables) {
promises.push(
db.updateTable({
id: updatedTable.id,
attributes: updatedTable,
})
);
}
const tablesToDelete = tables.filter(
(table) => !updatedTables.some((t) => t.id === table.id)
);
for (const table of tablesToDelete) {
promises.push(db.deleteTable({ diagramId, id: table.id }));
}
await Promise.all(promises);
};
const updateField = (
const updateField: ChartDBContext['updateField'] = async (
tableId: string,
fieldId: string,
field: Partial<DBField>
@@ -115,9 +170,27 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const table = await db.getTable({ diagramId, id: tableId });
if (!table) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...table,
fields: table.fields.map((f) =>
f.id === fieldId ? { ...f, ...field } : f
),
},
});
};
const removeField = (tableId: string, fieldId: string) => {
const removeField: ChartDBContext['removeField'] = async (
tableId: string,
fieldId: string
) => {
setTables((tables) =>
tables.map((table) =>
table.id === tableId
@@ -128,9 +201,25 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const table = await db.getTable({ diagramId, id: tableId });
if (!table) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...table,
fields: table.fields.filter((f) => f.id !== fieldId),
},
});
};
const addField = (tableId: string, field: DBField) => {
const addField: ChartDBContext['addField'] = async (
tableId: string,
field: DBField
) => {
setTables((tables) =>
tables.map((table) =>
table.id === tableId
@@ -138,14 +227,33 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const table = await db.getTable({ diagramId, id: tableId });
if (!table) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...table,
fields: [...table.fields, field],
},
});
};
const getField = (tableId: string, fieldId: string) => {
const getField: ChartDBContext['getField'] = (
tableId: string,
fieldId: string
) => {
const table = getTable(tableId);
return table?.fields.find((f) => f.id === fieldId) ?? null;
};
const createField = (tableId: string) => {
const createField: ChartDBContext['createField'] = async (
tableId: string
) => {
const table = getTable(tableId);
const field: DBField = {
id: generateId(),
@@ -157,12 +265,15 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
createdAt: Date.now(),
};
addField(tableId, field);
await addField(tableId, field);
return field;
};
const addIndex = (tableId: string, index: DBIndex) => {
const addIndex: ChartDBContext['addIndex'] = async (
tableId: string,
index: DBIndex
) => {
setTables((tables) =>
tables.map((table) =>
table.id === tableId
@@ -170,9 +281,25 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const dbTable = await db.getTable({ diagramId, id: tableId });
if (!dbTable) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...dbTable,
indexes: [...dbTable.indexes, index],
},
});
};
const removeIndex = (tableId: string, indexId: string) => {
const removeIndex: ChartDBContext['removeIndex'] = async (
tableId: string,
indexId: string
) => {
setTables((tables) =>
tables.map((table) =>
table.id === tableId
@@ -185,9 +312,28 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const dbTable = await db.getTable({
diagramId,
id: tableId,
});
if (!dbTable) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...dbTable,
indexes: dbTable.indexes.filter((i) => i.id !== indexId),
},
});
};
const createIndex = (tableId: string) => {
const createIndex: ChartDBContext['createIndex'] = async (
tableId: string
) => {
const table = getTable(tableId);
const index: DBIndex = {
id: generateId(),
@@ -197,17 +343,20 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
createdAt: Date.now(),
};
addIndex(tableId, index);
await addIndex(tableId, index);
return index;
};
const getIndex = (tableId: string, indexId: string) => {
const getIndex: ChartDBContext['getIndex'] = (
tableId: string,
indexId: string
) => {
const table = getTable(tableId);
return table?.indexes.find((i) => i.id === indexId) ?? null;
};
const updateIndex = (
const updateIndex: ChartDBContext['updateIndex'] = async (
tableId: string,
indexId: string,
index: Partial<DBIndex>
@@ -224,22 +373,37 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
: table
)
);
const dbTable = await db.getTable({ diagramId, id: tableId });
if (!dbTable) {
return;
}
await db.updateTable({
id: tableId,
attributes: {
...dbTable,
indexes: dbTable.indexes.map((i) =>
i.id === indexId ? { ...i, ...index } : i
),
},
});
};
const addRelationship = (relationship: DBRelationship) => {
const addRelationship: ChartDBContext['addRelationship'] = async (
relationship: DBRelationship
) => {
setRelationships((relationships) => [...relationships, relationship]);
await db.addRelationship({ diagramId, relationship });
};
const createRelationship = ({
const createRelationship: ChartDBContext['createRelationship'] = async ({
sourceTableId,
targetTableId,
sourceFieldId,
targetFieldId,
}: {
sourceTableId: string;
targetTableId: string;
sourceFieldId: string;
targetFieldId: string;
}) => {
const sourceTableName = getTable(sourceTableId)?.name ?? '';
const targetTableName = getTable(targetTableId)?.name ?? '';
@@ -254,29 +418,39 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
createdAt: Date.now(),
};
addRelationship(relationship);
await addRelationship(relationship);
return relationship;
};
const getRelationship = (id: string) =>
const getRelationship: ChartDBContext['getRelationship'] = (id: string) =>
relationships.find((relationship) => relationship.id === id) ?? null;
const removeRelationship = (id: string) => {
const removeRelationship: ChartDBContext['removeRelationship'] = async (
id: string
) => {
setRelationships((relationships) =>
relationships.filter((relationship) => relationship.id !== id)
);
await db.deleteRelationship({ diagramId, id });
};
const removeRelationships = (...ids: string[]) => {
const removeRelationships: ChartDBContext['removeRelationships'] = async (
...ids: string[]
) => {
setRelationships((relationships) =>
relationships.filter(
(relationship) => !ids.includes(relationship.id)
)
);
await Promise.all(
ids.map((id) => db.deleteRelationship({ diagramId, id }))
);
};
const updateRelationship = (
const updateRelationship: ChartDBContext['updateRelationship'] = async (
id: string,
relationship: Partial<DBRelationship>
) => {
@@ -285,24 +459,36 @@ export const ChartDBProvider: React.FC<React.PropsWithChildren> = ({
r.id === id ? { ...r, ...relationship } : r
)
);
await db.updateRelationship({ id, attributes: relationship });
};
const loadDiagram = async (diagramId: string) => {
const diagram = await getDiagram(diagramId, {
includeRelationships: true,
includeTables: true,
});
const loadDiagram: ChartDBContext['loadDiagram'] = useCallback(
async (diagramId: string) => {
const diagram = await db.getDiagram(diagramId, {
includeRelationships: true,
includeTables: true,
});
if (diagram) {
setDiagramId(diagram.id);
setDiagramName(diagram.name);
setDatabaseType(diagram.databaseType);
setTables(diagram.tables);
setRelationships(diagram.relationships);
}
if (diagram) {
setDiagramId(diagram.id);
setDiagramName(diagram.name);
setDatabaseType(diagram.databaseType);
setTables(diagram.tables);
setRelationships(diagram.relationships);
}
return diagram;
};
return diagram;
},
[
db,
setDiagramId,
setDiagramName,
setDatabaseType,
setTables,
setRelationships,
]
);
return (
<chartDBContext.Provider
@@ -19,7 +19,9 @@ export const ConfigProvider: React.FC<React.PropsWithChildren> = ({
loadConfig();
}, [getConfig]);
const updateConfig = async (config: Partial<ChartDBConfig>) => {
const updateConfig: ConfigContext['updateConfig'] = async (
config: Partial<ChartDBConfig>
) => {
await updateDataConfig(config);
setConfig((prevConfig) =>
prevConfig
+3 -3
View File
@@ -22,7 +22,7 @@ export interface DataContext {
) => Promise<Diagram | undefined>;
updateDiagram: (params: {
id: string;
diagram: Partial<Diagram>;
attributes: Partial<Diagram>;
}) => Promise<void>;
deleteDiagram: (id: string) => Promise<void>;
@@ -34,7 +34,7 @@ export interface DataContext {
}) => Promise<DBTable | undefined>;
updateTable: (params: {
id: string;
table: Partial<DBTable>;
attributes: Partial<DBTable>;
}) => Promise<void>;
deleteTable: (params: { diagramId: string; id: string }) => Promise<void>;
listTables: (diagramId: string) => Promise<DBTable[]>;
@@ -50,7 +50,7 @@ export interface DataContext {
}) => Promise<DBRelationship | undefined>;
updateRelationship: (params: {
id: string;
relationship: Partial<DBRelationship>;
attributes: Partial<DBRelationship>;
}) => Promise<void>;
deleteRelationship: (params: {
diagramId: string;
+38 -29
View File
@@ -1,5 +1,5 @@
import React from 'react';
import { dataContext } from './data-context';
import { DataContext, dataContext } from './data-context';
import Dexie, { type EntityTable } from 'dexie';
import { Diagram } from '@/lib/domain/diagram';
import { DBTable } from '@/lib/domain/db-table';
@@ -49,23 +49,33 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
}
});
const getConfig = async (): Promise<ChartDBConfig | undefined> => {
const getConfig: DataContext['getConfig'] = async (): Promise<
ChartDBConfig | undefined
> => {
return await db.config.get(1);
};
const updateConfig = async (config: Partial<ChartDBConfig>) => {
const updateConfig: DataContext['updateConfig'] = async (
config: Partial<ChartDBConfig>
) => {
await db.config.update(1, config);
};
const addDiagram = async ({ diagram }: { diagram: Diagram }) => {
const addDiagram: DataContext['addDiagram'] = async ({
diagram,
}: {
diagram: Diagram;
}) => {
await db.diagrams.add(diagram);
};
const listDiagrams = async (): Promise<Diagram[]> => {
const listDiagrams: DataContext['listDiagrams'] = async (): Promise<
Diagram[]
> => {
return await db.diagrams.toArray();
};
const getDiagram = async (
const getDiagram: DataContext['getDiagram'] = async (
id: string,
options: {
includeTables?: boolean;
@@ -89,17 +99,17 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
return diagram;
};
const updateDiagram = async ({
diagram,
const updateDiagram: DataContext['updateDiagram'] = async ({
id,
attributes,
}: {
id: string;
diagram: Partial<Diagram>;
attributes: Partial<Diagram>;
}) => {
await db.diagrams.update(id, diagram);
await db.diagrams.update(id, attributes);
};
const deleteDiagram = async (id: string) => {
const deleteDiagram: DataContext['deleteDiagram'] = async (id: string) => {
await Promise.all([
db.diagrams.delete(id),
db.db_tables.where('diagramId').equals(id).delete(),
@@ -107,7 +117,7 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
]);
};
const addTable = async ({
const addTable: DataContext['addTable'] = async ({
diagramId,
table,
}: {
@@ -120,7 +130,7 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
});
};
const getTable = async ({
const getTable: DataContext['getTable'] = async ({
id,
diagramId,
}: {
@@ -130,17 +140,14 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
return await db.db_tables.get({ id, diagramId });
};
const updateTable = async ({
const updateTable: DataContext['updateTable'] = async ({
id,
table,
}: {
id: string;
table: Partial<DBTable>;
attributes,
}) => {
await db.db_tables.update(id, table);
await db.db_tables.update(id, attributes);
};
const deleteTable = async ({
const deleteTable: DataContext['deleteTable'] = async ({
id,
diagramId,
}: {
@@ -150,14 +157,16 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
await db.db_tables.where({ id, diagramId }).delete();
};
const listTables = async (diagramId: string): Promise<DBTable[]> => {
const listTables: DataContext['listTables'] = async (
diagramId: string
): Promise<DBTable[]> => {
return await db.db_tables
.where('diagramId')
.equals(diagramId)
.toArray();
};
const addRelationship = async ({
const addRelationship: DataContext['addRelationship'] = async ({
diagramId,
relationship,
}: {
@@ -170,7 +179,7 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
});
};
const getRelationship = async ({
const getRelationship: DataContext['getRelationship'] = async ({
id,
diagramId,
}: {
@@ -180,17 +189,17 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
return await db.db_relationships.get({ id, diagramId });
};
const updateRelationship = async ({
const updateRelationship: DataContext['updateRelationship'] = async ({
id,
relationship,
attributes,
}: {
id: string;
relationship: Partial<DBRelationship>;
attributes: Partial<DBRelationship>;
}) => {
await db.db_relationships.update(id, relationship);
await db.db_relationships.update(id, attributes);
};
const deleteRelationship = async ({
const deleteRelationship: DataContext['deleteRelationship'] = async ({
id,
diagramId,
}: {
@@ -200,7 +209,7 @@ export const DataProvider: React.FC<React.PropsWithChildren> = ({
await db.db_relationships.where({ id, diagramId }).delete();
};
const listRelationships = async (
const listRelationships: DataContext['listRelationships'] = async (
diagramId: string
): Promise<DBRelationship[]> => {
return await db.db_relationships
+2 -2
View File
@@ -54,8 +54,8 @@ export const Canvas: React.FC<CanvasProps> = () => {
}, [tables, setNodes]);
const onConnect = useCallback(
(params: AddEdgeParams) => {
const relationship = createRelationship({
async (params: AddEdgeParams) => {
const relationship = await createRelationship({
sourceTableId: params.source,
targetTableId: params.target,
sourceFieldId: params.sourceHandle?.split('_')?.pop() ?? '',