diff --git a/src/components/EditorCanvas/Table.jsx b/src/components/EditorCanvas/Table.jsx
index 4509de91d..5ac9c1303 100644
--- a/src/components/EditorCanvas/Table.jsx
+++ b/src/components/EditorCanvas/Table.jsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { useState, useEffect } from "react";
import {
Tab,
ObjectType,
@@ -22,7 +22,7 @@ import i18n from "../../i18n/i18n";
export default function Table(props) {
const [hoveredField, setHoveredField] = useState(-1);
- const { database } = useDiagram();
+ const { database, updateTable } = useDiagram();
const {
tableData,
onPointerDown,
@@ -37,6 +37,39 @@ export default function Table(props) {
const { t } = useTranslation();
const { selectedElement, setSelectedElement } = useSelect();
+ useEffect(() => {
+ // Check if we need to update the table name
+ const desiredTableCase = settings.upperCaseFields ? tableData.name.toUpperCase() : tableData.name.toLowerCase();
+ const tableNameNeedsUpdate = tableData.name !== desiredTableCase;
+
+ // Check if any field names need to be updated
+ const fieldsNeedUpdate = tableData.fields.some(field => {
+ const desiredFieldCase = settings.upperCaseFields ? field.name.toUpperCase() : field.name.toLowerCase();
+ return field.name !== desiredFieldCase;
+ });
+
+ // Only update if there are actual changes needed
+ if (tableNameNeedsUpdate || fieldsNeedUpdate) {
+ // Create updated fields with correct case
+ const updatedFields = tableData.fields.map(field => ({
+ ...field,
+ name: settings.upperCaseFields ? field.name.toUpperCase() : field.name.toLowerCase()
+ }));
+
+ // Update both table name and fields
+ updateTable(tableData.id, {
+ name: settings.upperCaseFields ? tableData.name.toUpperCase() : tableData.name.toLowerCase(),
+ fields: updatedFields
+ });
+ }
+ }, [
+ settings.upperCaseFields,
+ tableData.fields,
+ tableData.id,
+ tableData.name,
+ updateTable
+ ]);
+
const height =
tableData.fields.length * tableFieldHeight + tableHeaderHeight + 7;
const openEditor = () => {
@@ -358,37 +391,37 @@ export default function Table(props) {
/>
) : (
- {fieldData.primary &&
-
}
- {fieldData.foreignK &&
-
}
diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx
index 8a90fdce1..9ec4ad76f 100644
--- a/src/components/EditorHeader/ControlPanel.jsx
+++ b/src/components/EditorHeader/ControlPanel.jsx
@@ -1343,6 +1343,9 @@ export default function ControlPanel({
table_width: {
function: () => setModal(MODAL.TABLE_WIDTH),
},
+ defaults : {
+ function: () => setModal(MODAL.DEFAULTS),
+ },
language: {
function: () => setModal(MODAL.LANGUAGE),
},
diff --git a/src/components/EditorHeader/Modal/Defaults.jsx b/src/components/EditorHeader/Modal/Defaults.jsx
new file mode 100644
index 000000000..29162c1a0
--- /dev/null
+++ b/src/components/EditorHeader/Modal/Defaults.jsx
@@ -0,0 +1,153 @@
+import { useState, useEffect } from "react";
+import { Form, Input, Button, Toast, Divider } from "@douyinfe/semi-ui";
+import { useSettings, useDiagram } from "../../../hooks";
+import { useTranslation } from "react-i18next";
+import { dbToTypes } from "../../../data/datatypes";
+
+export default function Defaults() {
+ const { t } = useTranslation();
+ const { settings, setSettings } = useSettings();
+ const { database } = useDiagram();
+
+
+ const [editingFKNamingTemplate, setEditingFKNamingTemplate] = useState(settings.fkConstraintNaming?.template || "");
+ const [editingIndexNamingTemplate, setEditingIndexNamingTemplate] = useState(settings.indexNaming?.template || "");
+
+ useEffect(() => {
+ if (settings) {
+ if (settings.fkConstraintNaming?.template) {
+ setEditingFKNamingTemplate(settings.fkConstraintNaming.template);
+ }
+ if (settings.indexNaming?.template) {
+ setEditingIndexNamingTemplate(settings.indexNaming.template);
+ }
+ }
+ }, [settings]);
+
+ const handleChange = (field, value) => {
+ setSettings(prev => ({
+ ...prev, [field]: value
+ }));
+ };
+
+ const handleSaveNamingConventions = () => {
+ setSettings(prevSettings => ({
+ ...prevSettings,
+ fkConstraintNaming: {
+ ...prevSettings.fkConstraintNaming,
+ template: editingFKNamingTemplate,
+ },
+ indexNaming: {
+ ...prevSettings.indexNaming,
+ template: editingIndexNamingTemplate,
+ },
+ }));
+ Toast.success(t("naming_conventions_saved_successfully"));
+ };
+
+ const previewFKName = (template) => {
+ const exampleTable1 = "orders";
+ const exampleField1 = "id";
+ const exampleTable2 = "customers";
+ const exampleField2 = "customer_id";
+ let preview = template;
+ preview = preview.replace(/{table1}/g, exampleTable1);
+ preview = preview.replace(/{field1}/g, exampleField1);
+ preview = preview.replace(/{table2}/g, exampleTable2);
+ preview = preview.replace(/{field2}/g, exampleField2);
+ return preview;
+ };
+
+ const previewIndexName = (template) => {
+ const exampleTable = "products";
+ const exampleIndexType = "unique";
+ const exampleFields = "name,code";
+ let preview = template;
+ preview = preview.replace(/{table}/g, exampleTable);
+ preview = preview.replace(/{indexType}/g, exampleIndexType);
+ preview = preview.replace(/{fields}/g, exampleFields);
+ return preview;
+ };
+
+ return (
+
+
+ handleChange('defaultFieldType', value)}
+ >
+ {Object.keys(dbToTypes[database]).map(type => (
+
+ {type}
+
+ ))}
+
+
+ handleChange('upperCaseFields', checked)}
+ />
+
+ handleChange('defaultNotNull', checked)}
+ />
+
+
+
+
+
{t("naming_conventions")}
+
+
+
+
setEditingFKNamingTemplate(value)}
+ placeholder="{table1}_{table2}_{field1}_fk"
+ title={t("fk_template_info")}
+ style={{ width: '100%' }}
+ />
+
+ {t("fk_variables_available")} `{table1}`, `{table2}`, `{field1}`, `{field2}`
+
+
+ {t("preview")}: {previewFKName(editingFKNamingTemplate)}
+
+
+
+
+
+
setEditingIndexNamingTemplate(value)}
+ placeholder="{table}_{indexType}_{fields}_idx"
+ title={t("index_template_info")}
+ style={{ width: '100%' }}
+ />
+
+ {t("index_template_variables_available")} `{table}`, `{indexType}` (e.g., unique), `{fields}` (comma-separated)
+
+
+ {t("preview")}: {previewIndexName(editingIndexNamingTemplate)}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/EditorHeader/Modal/Modal.jsx b/src/components/EditorHeader/Modal/Modal.jsx
index 8db36c466..2c675c6c2 100644
--- a/src/components/EditorHeader/Modal/Modal.jsx
+++ b/src/components/EditorHeader/Modal/Modal.jsx
@@ -34,6 +34,7 @@ import SetTableWidth from "./SetTableWidth";
import Language from "./Language";
import Share from "./Share";
import Code from "./Code";
+import Defaults from "./Defaults";
import { useTranslation } from "react-i18next";
import { importSQL } from "../../../utils/importSQL";
import { databases } from "../../../data/databases";
@@ -315,6 +316,8 @@ export default function Modal({
}
case MODAL.TABLE_WIDTH:
return
;
+ case MODAL.DEFAULTS:
+ return
case MODAL.LANGUAGE:
return
;
case MODAL.SHARE:
diff --git a/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx
index 3cfc4516b..dd97752ca 100644
--- a/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx
+++ b/src/components/EditorSidePanel/RelationshipsTab/RelationshipInfo.jsx
@@ -22,7 +22,7 @@ import { useDiagram, useUndoRedo } from "../../../hooks";
import i18n from "../../../i18n/i18n";
import { useTranslation } from "react-i18next";
import { useState } from "react";
-
+import {useSettings} from "../../../hooks";
const columns = [
{
title: i18n.t("primary"),
@@ -35,55 +35,81 @@ const columns = [
];
export default function RelationshipInfo({ data }) {
+ const { settings } = useSettings();
const { setUndoStack, setRedoStack } = useUndoRedo();
const { tables, setRelationships, deleteRelationship, updateRelationship } =
useDiagram();
const { t } = useTranslation();
const [editField, setEditField] = useState({});
-
+ const generateFKName=(template,tableName,fieldName,table2Name,field2Name = '')=>{
+ let name=template;
+ name = name.replace(/{table1}/g, tableName || '');
+ name = name.replace(/{table2}/g, table2Name || '');
+ name = name.replace(/{field1}/g, fieldName || '');
+ name = name.replace(/{field2}/g, field2Name || '');
+
+ return name;
+ };
const swapKeys = () => {
+ const current = {
+ startTableId: data.startTableId,
+ endTableId: data.endTableId,
+ startFieldId: data.startFieldId,
+ endFieldId: data.endFieldId,
+ name: data.name,
+ isCustomName: data.isCustomName,
+ };
+
+ const swapped = {
+ startTableId: current.endTableId,
+ startFieldId: current.endFieldId,
+ endTableId: current.startTableId,
+ endFieldId: current.startFieldId,
+ };
+
setUndoStack((prev) => [
...prev,
{
action: Action.EDIT,
element: ObjectType.RELATIONSHIP,
rid: data.id,
- undo: {
- startTableId: data.startTableId,
- startFieldId: data.startFieldId,
- endTableId: data.endTableId,
- endFieldId: data.endFieldId,
- },
- redo: {
- startTableId: data.endTableId,
- startFieldId: data.endFieldId,
- endTableId: data.startTableId,
- endFieldId: data.startFieldId,
- },
+ undo: current,
+
+ redo: swapped,
message: t("edit_relationship", {
- refName: data.name,
+ refName: current.name,
extra: "[swap keys]",
}),
},
]);
setRedoStack([]);
setRelationships((prev) =>
- prev.map((e, idx) =>
- idx === data.id
- ? {
- ...e,
- name: `fk_${tables[e.endTableId].name}_${
- tables[e.endTableId].fields[e.endFieldId].name
- }_${tables[e.startTableId].name}`,
- startTableId: e.endTableId,
- startFieldId: e.endFieldId,
- endTableId: e.startTableId,
- endFieldId: e.startFieldId,
- }
- : e,
- ),
- );
- };
+ prev.map((e, idx) => {
+ if (idx === data.id) {
+ const isCustom = e.isCustomName;
+ const nameAfterSwap = isCustom
+ ? e.name
+ : generateFKName(
+ settings.fkConstraintNaming.template,
+ tables[e.endTableId]?.name,
+ tables[e.endTableId]?.fields?.[e.endFieldId]?.name,
+ tables[e.startTableId]?.name,
+ tables[e.startTableId]?.fields?.[e.startFieldId]?.name
+ );
+
+ return {
+ ...e,
+ name: nameAfterSwap,
+ startTableId: e.endTableId,
+ startFieldId: e.endFieldId,
+ endTableId: e.startTableId,
+ endFieldId: e.startFieldId,
+ };
+ }
+ return e;
+ })
+);
+};
const changeCardinality = (value) => {
setUndoStack((prev) => [
@@ -143,6 +169,7 @@ export default function RelationshipInfo({ data }) {
onFocus={(e) => setEditField({ name: e.target.value })}
onBlur={(e) => {
if (e.target.value === editField.name) return;
+ updateRelationship(data.id, { name: e.target.value, isCustomName: true });
setUndoStack((prev) => [
...prev,
{
@@ -150,8 +177,8 @@ export default function RelationshipInfo({ data }) {
element: ObjectType.RELATIONSHIP,
component: "self",
rid: data.id,
- undo: editField,
- redo: { name: e.target.value },
+ undo: { name: editField.name, isCustomName: data.isCustomName },
+ redo: { name: e.target.value, isCustomName: true },
message: t("edit_relationship", {
refName: e.target.value,
extra: "[name]",
@@ -165,11 +192,11 @@ export default function RelationshipInfo({ data }) {
{t("primary")}:
- {tables[data.endTableId].name}
+ {tables?.[data.endTableId]?.name ?? ""}
{t("foreign")}:
- {tables[data.startTableId].name}
+ {tables?.[data.startTableId]?.name ?? ""}
@@ -25,10 +26,15 @@ export default function TableField({ data, tid, index }) {
value={data.name}
validateStatus={data.name.trim() === "" ? "error" : "default"}
placeholder="Name"
- onChange={(value) => updateField(tid, index, { name: value })}
+ onChange={(value) => updateField(tid, index, {
+ name: settings.upperCaseFields ? value.toUpperCase() : value.toLowerCase()
+ })}
onFocus={(e) => setEditField({ name: e.target.value })}
onBlur={(e) => {
if (e.target.value === editField.name) return;
+ const transformedValue = settings.upperCaseFields
+ ? e.target.value.toUpperCase()
+ : e.target.value.toLowerCase();
setUndoStack((prev) => [
...prev,
{
@@ -38,7 +44,7 @@ export default function TableField({ data, tid, index }) {
tid: tid,
fid: index,
undo: editField,
- redo: { name: e.target.value },
+ redo: { name: transformedValue },
message: t("edit_table", {
tableName: tables[tid].name,
extra: "[field]",
@@ -176,10 +182,10 @@ export default function TableField({ data, tid, index }) {
const stateNull=newStatePK?true: !data.notNull;
const mustSetNotNull = !data.primary && !data.notNull;
const changes = { primary: !data.primary };
-
+
const undo= { primary: data.primary , notNull : data.notNull };
const redo= { primary: newStatePK , notNull:stateNull };
-
+
if (mustSetNotNull) {
undo.notNull = data.notNull;
redo.notNull = true;
@@ -221,4 +227,4 @@ export default function TableField({ data, tid, index }) {
);
-}
\ No newline at end of file
+}
diff --git a/src/components/EditorSidePanel/TablesTab/TableInfo.jsx b/src/components/EditorSidePanel/TablesTab/TableInfo.jsx
index e9fd7de0e..1c7334c60 100644
--- a/src/components/EditorSidePanel/TablesTab/TableInfo.jsx
+++ b/src/components/EditorSidePanel/TablesTab/TableInfo.jsx
@@ -8,7 +8,7 @@ import {
Popover,
} from "@douyinfe/semi-ui";
import { IconDeleteStroked } from "@douyinfe/semi-icons";
-import { useDiagram, useUndoRedo } from "../../../hooks";
+import { useDiagram, useUndoRedo, useSettings } from "../../../hooks";
import { Action, ObjectType, defaultBlue } from "../../../data/constants";
import ColorPalette from "../../ColorPicker";
import TableField from "./TableField";
@@ -26,6 +26,7 @@ export default function TableInfo({ data }) {
useDiagram();
const { setUndoStack, setRedoStack } = useUndoRedo();
const [editField, setEditField] = useState({});
+ const { settings } = useSettings();
const [drag, setDrag] = useState({
draggingElementIndex: null,
draggingOverIndexList: [],
@@ -40,10 +41,15 @@ export default function TableInfo({ data }) {
validateStatus={data.name.trim() === "" ? "error" : "default"}
placeholder={t("name")}
className="ms-2"
- onChange={(value) => updateTable(data.id, { name: value })}
+ onChange={(value) => updateTable(data.id, {
+ name: settings.upperCaseFields ? value.toUpperCase() : value.toLowerCase()
+ })}
onFocus={(e) => setEditField({ name: e.target.value })}
onBlur={(e) => {
if (e.target.value === editField.name) return;
+ const transformedValue = settings.upperCaseFields
+ ? e.target.value.toUpperCase()
+ : e.target.value.toLowerCase();
setUndoStack((prev) => [
...prev,
{
@@ -52,9 +58,9 @@ export default function TableInfo({ data }) {
component: "self",
tid: data.id,
undo: editField,
- redo: { name: e.target.value },
+ redo: { name: transformedValue },
message: t("edit_table", {
- tableName: e.target.value,
+ tableName: transformedValue,
extra: "[name]",
}),
},
@@ -338,19 +344,61 @@ export default function TableInfo({ data }) {
...data.fields,
{
name: "",
- type: "",
+ type: settings.defaultFieldType,
default: "",
check: "",
primary: false,
unique: false,
- notNull: false,
+ notNull: settings.defaultNotNull,
increment: false,
comment: "",
- foreignK: false,
+ foreignK: false,
id: data.fields.length,
},
],
});
+
+ const incr =
+ data.increment && !!dbToTypes[database][settings.defaultFieldType].canIncrement;
+
+ if (settings.defaultFieldType === "ENUM" || settings.defaultFieldType === "SET") {
+ updateField(data.id, data.fields.length, {
+ type: settings.defaultFieldType,
+ default: "",
+ values: data.values ? [...data.values] : [],
+ increment: incr,
+ });
+ } else if (
+ dbToTypes[database][settings.defaultFieldType].isSized ||
+ dbToTypes[database][settings.defaultFieldType].hasPrecision
+ ) {
+ updateField(data.id, data.fields.length, {
+ type: settings.defaultFieldType,
+ size: dbToTypes[database][settings.defaultFieldType].defaultSize,
+ increment: incr,
+ });
+ } else if (!dbToTypes[database][settings.defaultFieldType].hasDefault || incr) {
+ updateField(data.id, data.fields.length, {
+ type: settings.defaultFieldType,
+ increment: incr,
+ default: "",
+ size: "",
+ values: [],
+ });
+ } else if (dbToTypes[database][settings.defaultFieldType].hasCheck) {
+ updateField(data.id, data.fields.length, {
+ type: settings.defaultFieldType,
+ check: "",
+ increment: incr,
+ });
+ } else {
+ updateField(data.id, data.fields.length, {
+ type: settings.defaultFieldType,
+ increment: incr,
+ size: "",
+ values: [],
+ });
+ }
}}
block
>
diff --git a/src/context/DiagramContext.jsx b/src/context/DiagramContext.jsx
index ed9159842..bea2b9464 100644
--- a/src/context/DiagramContext.jsx
+++ b/src/context/DiagramContext.jsx
@@ -1,12 +1,21 @@
import { createContext, useState } from "react";
import { Action, DB, ObjectType, defaultBlue } from "../data/constants";
-import { useTransform, useUndoRedo, useSelect } from "../hooks";
+import { useTransform, useUndoRedo, useSelect,useSettings } from "../hooks";
import { Toast } from "@douyinfe/semi-ui";
import { useTranslation } from "react-i18next";
export const DiagramContext = createContext(null);
export default function DiagramContextProvider({ children }) {
+ const { settings } = useSettings();
+ const generateFKName = (template, table1Name, field1Name, table2Name,field2Name) => {
+ let name = template;
+ name = name.replace(/{table1}/g, table1Name || '');
+ name = name.replace(/{table2}/g, table2Name || '');
+ name = name.replace(/{field1}/g, field1Name || '');
+ name = name.replace(/{field2}/g, field2Name || '');
+ return name;
+ };
const { t } = useTranslation();
const [database, setDatabase] = useState(DB.GENERIC);
const [tables, setTables] = useState([]);
@@ -27,13 +36,13 @@ export default function DiagramContextProvider({ children }) {
...prev,
{
id: prev.length,
- name: `table_${prev.length}`,
+ name: settings.upperCaseFields ? `TABLE_${prev.length}` : `table_${prev.length}`,
x: transform.pan.x,
y: transform.pan.y,
fields: [
{
- name: "id",
- type: database === DB.GENERIC ? "INT" : "INTEGER",
+ name: settings.upperCaseFields ? "ID" : "id",
+ type: database === DB.GENERIC ? "INT" : database === DB.ORACLE ? "NUMBER" : "INTEGER",
default: "",
check: "",
primary: true,
@@ -78,7 +87,7 @@ export default function DiagramContextProvider({ children }) {
{
action: Action.DELETE,
element: ObjectType.TABLE,
- data: { table: tables[id], relationship: rels },
+ data: { table: tables[id], relationship: rels },
message: t("delete_table", { tableName: tables[id].name }),
},
]);
@@ -137,16 +146,16 @@ export default function DiagramContextProvider({ children }) {
const deleteField = (field, tid, addToHistory = true) => {
const currentTable = tables[tid];
-
+
// If table has a composite pk
const pkFieldIds = currentTable.fields.filter(f => f.primary).map(f => f.id);
const isPartOfCompositePK = field.primary && pkFieldIds.length > 1;
-
+
// If the field is a composite FK
const isPartOfCompositeFK = (() => {
if (!field.foreignK) return false;
const fkTableId = field.foreignKey.tableId;
-
+
// Get all the fields Fk pointing to the same tables
const relatedFKs = tables[tid].fields.filter(
f =>
@@ -155,28 +164,28 @@ export default function DiagramContextProvider({ children }) {
);
return relatedFKs.length > 1;
})();
-
+
// get associated relationships
let affectedRelationships = relationships.filter(
(r) =>
(r.startTableId === tid && r.startFieldId === field.id) ||
(r.endTableId === tid && r.endFieldId === field.id)
);
-
+
// If PKs is composite, get all its relationships
if (isPartOfCompositePK) {
affectedRelationships = relationships.filter(
(r) => r.startTableId === tid && pkFieldIds.includes(r.startFieldId)
);
}
-
+
// If FKs is composite, get all its relationships
if (isPartOfCompositeFK && field.foreignK) {
const fkTableId = field.foreignKey.tableId;
const relatedFKFieldIds = tables[tid].fields
.filter(f => f.foreignK && f.foreignKey.tableId === fkTableId)
.map(f => f.id);
-
+
affectedRelationships = relationships.filter(
(r) => r.endTableId === tid && relatedFKFieldIds.includes(r.endFieldId)
);
@@ -216,7 +225,7 @@ export default function DiagramContextProvider({ children }) {
]);
setRedoStack([]);
}
-
+
// Delete relationships
setRelationships((prev) => {
const affectedRelIds = new Set(affectedRelationships.map((r) => r.id));
@@ -240,9 +249,9 @@ export default function DiagramContextProvider({ children }) {
return temp;
});
-
+
const updatedTables = [...tables];
-
+
// Delete FKs in child tables if a composite PK is deleted
if (isPartOfCompositePK) {
affectedRelationships.forEach((rel) => {
@@ -258,7 +267,7 @@ export default function DiagramContextProvider({ children }) {
.map((f, i) => ({ ...f, id: i }));
});
}
-
+
// Delete FKs in child tables if a composite FK is deleted
if (isPartOfCompositeFK && field.foreignK) {
const fkTableId = field.foreignKey.tableId;
@@ -273,7 +282,7 @@ export default function DiagramContextProvider({ children }) {
)
.map((f, i) => ({ ...f, id: i }));
}
-
+
// Delete any FK references in other tables
updatedTables.forEach((table) => {
table.fields = table.fields.filter(
@@ -285,20 +294,41 @@ export default function DiagramContextProvider({ children }) {
)
);
});
-
+
// Delete the field from the table
updatedTables[tid].fields = updatedTables[tid].fields
.filter((f) => f.id !== field.id)
.map((f, i) => ({ ...f, id: i }));
-
+
// Update the tables state
updatedTables.forEach((table) => updateTable(table.id, { fields: table.fields }));
- };
+ };
const addRelationship = (data, addToHistory = true) => {
+ const startTableName = tables[data.startTableId]?.name;
+ const startFieldName = tables[data.startTableId]?.fields[data.startFieldId]?.name;
+ const endTableName = tables[data.endTableId]?.name;
+ const endFieldName = tables[data.endTableId]?.fields[data.endFieldId]?.name;
+
+ const generatedDefaultName = generateFKName(
+ settings.fkConstraintNaming.template,
+ startTableName,
+ startFieldName,
+ endTableName,
+ endFieldName
+ );
+ let relationshipToAdd;
+ setRelationships((prev) => {
+ relationshipToAdd = { // Asigna el objeto final aquí
+ ...data,
+ id: prev.length,
+ isCustomName: data.isCustomName !== undefined ? data.isCustomName : false,
+ name: data.isCustomName ? data.name : generatedDefaultName,
+ };
+ return [...prev, relationshipToAdd];
+ });
+
if (addToHistory) {
- // First, update the relationships
- setRelationships((prev) => [...prev, data]);
// After that, update the component undo stack
setUndoStack((prevUndo) => [
@@ -306,34 +336,97 @@ export default function DiagramContextProvider({ children }) {
{
action: Action.ADD,
element: ObjectType.RELATIONSHIP,
- data: data,
+ data: relationshipToAdd,
message: t("add_relationship"),
},
]);
setRedoStack([]);
+ const childTable = tables[data.endTableId];
+ const parentTable = tables[data.startTableId];
+ const parentField = parentTable?.fields?.find(f => f.id === data.startFieldId);
+ if (childTable && parentTable && parentField) {
+ // Verificar si el campo FK ya existe para evitar duplicados (importante si se llama varias veces)
+ const fkExists = childTable.fields.some(
+ (f) =>
+ f.foreignK &&
+ f.foreignKey?.tableId === data.startTableId &&
+ f.foreignKey?.fieldId === data.startFieldId
+ );
+
+ if (!fkExists) {
+ const newFKField = {
+ id: childTable.fields.length,
+ name: relationshipToAdd.name,
+ type: parentField.type,
+ default: "",
+ check: "",
+ primary: false,
+ unique: false,
+ notNull: false, // Puedes ajustar esto según tus defaults
+ increment: false,
+ comment: "",
+ foreignK: true,
+ foreignKey: {
+ tableId: data.startTableId,
+ fieldId: data.startFieldId,
+ },
+ };
+ updateTable(data.endTableId, {
+ fields: [...childTable.fields, newFKField].map((f, i) => ({ ...f, id: i })), // Re-mapear IDs
+ });
+ }
+ }
} else {
+ const relationshipToAddNoHistory = {
+ id: data.id !== undefined ? data.id : relationships.length,
+ isCustomName: data.isCustomName || false,
+ name: data.isCustomName ? data.name : generatedDefaultName,
+ };
setRelationships((prev) => {
- const temp = prev.slice();
- temp.splice(data.id, 0, data);
- return temp.map((t, i) => ({ ...t, id: i }));
+ const temp = [...prev];
+ if (data.id !== undefined && data.id <= prev.length) {
+ temp.splice(data.id, 0, relationshipToAddNoHistory);
+ } else {
+ temp.push(relationshipToAddNoHistory);
+ }
+ return temp.map((rel, i) => ({ ...rel, id: i }));
});
+ const childTable = tables[data.endTableId];
+ const parentTable = tables[data.startTableId];
+ const parentField = parentTable?.fields?.find(f => f.id === data.startFieldId);
- const tableIndex = data.endTableId;
- if(tables[tableIndex]){
-
- const currentFields = tables[tableIndex].fields;
- const fieldToInsert = data.endField[0];
- const exists = currentFields.some((field) => field.id === fieldToInsert.id);
-
- if (!exists){
- const newFieldsArray = [
- ...currentFields.slice(0, data.endFieldId),
- ...data.endField.map((field) => ({...field})),
- ...currentFields.slice(data.endFieldId),
- ];
- updateTable(tableIndex, {
- fields: newFieldsArray,
- });
+ if (childTable && parentTable && parentField) {
+ const fkExists = childTable.fields.some(
+ (f) =>
+ f.foreignK &&
+ f.foreignKey?.tableId === data.startTableId &&
+ f.foreignKey?.fieldId === data.startFieldId
+ );
+
+ if (!fkExists) {
+ // Usa el nombre que la relación ya tiene (data.name) si es personalizado,
+ // o el generado por defecto.
+ const fkFieldName = data.name || generatedDefaultName;
+ const newFKField = {
+ id: childTable.fields.length, // Nuevo ID para el campo
+ name: fkFieldName,
+ type: parentField.type,
+ default: "",
+ check: "",
+ primary: false,
+ unique: false,
+ notNull: false,
+ increment: false,
+ comment: "",
+ foreignK: true,
+ foreignKey: {
+ tableId: data.startTableId,
+ fieldId: data.startFieldId,
+ },
+ };
+ updateTable(data.endTableId, {
+ fields: [...childTable.fields, newFKField],
+ });
}
}
}
@@ -381,10 +474,13 @@ export default function DiagramContextProvider({ children }) {
};
const updateRelationship = (id, updatedValues) => {
- setRelationships((prev) =>
- prev.map((t) => (t.id === id ? { ...t, ...updatedValues } : t)),
+ setRelationships((prev) =>{
+ const updated = prev.map((rel) =>
+ rel.id === id ? { ...rel, ...updatedValues } : rel
);
- };
+ return[...updated];
+ });
+};
return (
{
const settings = localStorage.getItem("settings");
if (settings) {
- setSettings(JSON.parse(settings));
+ const parsedSettings = JSON.parse(settings);
+ setSettings(prevSettings => ({// eslint-disable-line no-unused-vars
+ ...defaultSettings,
+ ...parsedSettings,
+ fkConstraintNaming: {
+ ...defaultSettings.fkConstraintNaming,
+ ...(parsedSettings.fkConstraintNaming || {})
+ },
+ indexNaming: {
+ ...defaultSettings.indexNaming,
+ ...(parsedSettings.indexNaming || {})
+ },
+ defaultNewTableFieldProps: {
+ ...defaultSettings.defaultNewTableFieldProps,
+ ...(parsedSettings.defaultNewTableFieldProps || {})
+ }
+ }));
+
}
}, []);
diff --git a/src/data/constants.js b/src/data/constants.js
index e824160a9..00d672001 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -92,6 +92,7 @@ export const MODAL = {
TABLE_WIDTH: 9,
LANGUAGE: 10,
SHARE: 11,
+ DEFAULTS: 12,
};
export const STATUS = {
diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js
index 028136cc9..f67da36a2 100644
--- a/src/i18n/locales/en.js
+++ b/src/i18n/locales/en.js
@@ -248,6 +248,11 @@ const en = {
share_info:
"* Sharing this link will not create a live real-time collaboration session.",
show_relationship_labels: "Show relationship labels",
+ defaults: "Defaults",
+ field_defaults: "Field Defaults",
+ default_field_type: "Default Field Type",
+ uppercase_fields: "Uppercase Field Names",
+ default_not_null: "Default Not Null",
},
};
diff --git a/src/utils/modalData.js b/src/utils/modalData.js
index f4fbf45b0..589bbb056 100644
--- a/src/utils/modalData.js
+++ b/src/utils/modalData.js
@@ -21,6 +21,8 @@ export const getModalTitle = (modal) => {
return i18n.t("create_new_diagram");
case MODAL.TABLE_WIDTH:
return i18n.t("table_width");
+ case MODAL.DEFAULTS:
+ return i18n.t("defaults");
case MODAL.LANGUAGE:
return i18n.t("language");
case MODAL.SHARE:
@@ -38,6 +40,8 @@ export const getModalWidth = (modal) => {
return 740;
default:
return 600;
+ case MODAL.DEFAULTS:
+ return 500;
}
};