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; } };