diff --git a/src/components/ModelModal.tsx b/src/components/ModelModal.tsx
index 0f82bc2..8887d6b 100644
--- a/src/components/ModelModal.tsx
+++ b/src/components/ModelModal.tsx
@@ -1,12 +1,20 @@
-import React, { useState, useEffect } from 'react';
-import { X, Plus, Trash } from 'lucide-react';
-import { useFlowStore } from '../store/flowStore';
+import React, { useState, useEffect } from "react";
+import { X, Plus, Trash } from "lucide-react";
+import { useFlowStore } from "../store/flowStore";
interface Field {
name: string;
type: string;
defaultValue: string;
validation: string;
+ validationOptions?: {
+ pattern?: string;
+ enum?: string[];
+ min?: number;
+ max?: number;
+ minLength?: number;
+ maxLength?: number;
+ };
mapping?: string;
}
@@ -21,35 +29,53 @@ interface ModelModalProps {
}
const fieldTypes = [
- 'string',
- 'number',
- 'boolean',
- 'date',
- 'mapping',
- 'array',
- 'object',
+ "primary key",
+ "string",
+ "long text",
+ "integer",
+ "double",
+ "big number",
+ "boolean",
+ "date",
+ "datetime",
+ "uuid",
+ "json",
+ "mapping",
];
const validationRules = [
- 'required',
- 'email',
- 'url',
- 'min',
- 'max',
- 'pattern',
- 'enum',
+ "required",
+ "email",
+ "url",
+ "min",
+ "max",
+ "pattern",
+ "enum",
+ "length",
+ "minLength",
+ "maxLength",
+ "positive",
+ "negative",
+ "integer",
+ "decimal",
+ "alphanumeric",
+ "uuid",
+ "json",
+ "date",
+ "phone",
];
-const initialNewField = {
- name: '',
- type: 'string',
- defaultValue: '',
- validation: '',
+const initialNewField: Field = {
+ name: "",
+ type: "string",
+ defaultValue: "",
+ validation: "",
+ validationOptions: {},
};
const createInitialModelData = () => ({
id: `model_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
- name: '',
+ name: "",
fields: [],
});
@@ -108,12 +134,9 @@ export function ModelModal({ isOpen, onClose, model }: ModelModalProps) {
- {model ? 'Edit Model' : 'Add New Model'}
+ {model ? "Edit Model" : "Add New Model"}
-
@@ -124,7 +147,9 @@ export function ModelModal({ isOpen, onClose, model }: ModelModalProps) {
setModelData({ ...modelData, name: e.target.value })}
+ onChange={(e) =>
+ setModelData({ ...modelData, name: e.target.value })
+ }
className="w-full p-2 border rounded"
placeholder="Enter model name"
/>
@@ -211,10 +236,10 @@ export function ModelModal({ isOpen, onClose, model }: ModelModalProps) {
- {newField.type === 'mapping' && (
+ {newField.type === "mapping" && (
setNewField({ ...newField, mapping: e.target.value })
}
@@ -223,6 +248,78 @@ export function ModelModal({ isOpen, onClose, model }: ModelModalProps) {
/>
)}
+ {newField.validation &&
+ [
+ "pattern",
+ "enum",
+ "min",
+ "max",
+ "minLength",
+ "maxLength",
+ ].includes(newField.validation) && (
+
+ {newField.validation === "pattern" && (
+
+ setNewField({
+ ...newField,
+ validationOptions: {
+ ...newField.validationOptions,
+ pattern: e.target.value,
+ },
+ })
+ }
+ className="flex-1 p-2 border rounded"
+ placeholder="Regular expression pattern"
+ />
+ )}
+ {newField.validation === "enum" && (
+
+ setNewField({
+ ...newField,
+ validationOptions: {
+ ...newField.validationOptions,
+ enum: e.target.value.split(","),
+ },
+ })
+ }
+ className="flex-1 p-2 border rounded"
+ placeholder="Comma-separated values"
+ />
+ )}
+ {["min", "max", "minLength", "maxLength"].includes(
+ newField.validation
+ ) && (
+
+ setNewField({
+ ...newField,
+ validationOptions: {
+ ...newField.validationOptions,
+ [newField.validation]: parseFloat(e.target.value),
+ },
+ })
+ }
+ className="flex-1 p-2 border rounded"
+ placeholder={`Enter ${newField.validation} value`}
+ />
+ )}
+
+ )}
+
- {model ? 'Update' : 'Save'} Model
+ {model ? "Update" : "Save"} Model
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ModelPanel.tsx b/src/components/ModelPanel.tsx
index 1374e2e..ad2b5a4 100644
--- a/src/components/ModelPanel.tsx
+++ b/src/components/ModelPanel.tsx
@@ -1,7 +1,7 @@
-import React, { useState } from 'react';
-import { Plus, Edit2 } from 'lucide-react';
-import { useFlowStore } from '../store/flowStore';
-import { ModelModal } from './ModelModal';
+import React, { useState } from "react";
+import { Plus, Edit2 } from "lucide-react";
+import { useFlowStore } from "../store/flowStore";
+import { ModelModal } from "./ModelModal";
export function ModelPanel() {
const [isModalOpen, setIsModalOpen] = useState(false);
@@ -36,10 +36,11 @@ export function ModelPanel() {
{model.name}
- {model.fields.length} field{model.fields.length !== 1 ? 's' : ''}
+ {model.fields.length} field
+ {model.fields.length !== 1 ? "s" : ""}
- {
e.stopPropagation();
@@ -63,4 +64,4 @@ export function ModelPanel() {
/>
);
-}
\ No newline at end of file
+}
diff --git a/src/components/SettingsForm.tsx b/src/components/SettingsForm.tsx
index b901138..34d3f6e 100644
--- a/src/components/SettingsForm.tsx
+++ b/src/components/SettingsForm.tsx
@@ -1,11 +1,14 @@
-import React from 'react';
-import { Save } from 'lucide-react';
-import { useFlowStore } from '../store/flowStore';
+import React from "react";
+import { Save } from "lucide-react";
+import { useFlowStore } from "../store/flowStore";
+import { TranslationService } from "../services/TranslationService";
export function SettingsForm() {
const { settings, updateSettings } = useFlowStore();
- const handleChange = (e: React.ChangeEvent) => {
+ const handleChange = (
+ e: React.ChangeEvent
+ ) => {
updateSettings({
...settings,
[e.target.name]: e.target.value,
@@ -16,6 +19,25 @@ export function SettingsForm() {
updateSettings(settings);
};
+ const handleExportConfiguration = () => {
+ const models = useFlowStore.getState().models;
+ const configuration = {
+ models: models.map((model) => TranslationService.translateModel(model)),
+ };
+
+ const blob = new Blob([JSON.stringify(configuration, null, 2)], {
+ type: "application/json",
+ });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = "model-configuration.json";
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ };
+
return (
@@ -23,7 +45,7 @@ export function SettingsForm() {
Database Type
-
+
-
-
+
+
);
-}
\ No newline at end of file
+}
diff --git a/src/services/TranslationService.ts b/src/services/TranslationService.ts
new file mode 100644
index 0000000..90d6b05
--- /dev/null
+++ b/src/services/TranslationService.ts
@@ -0,0 +1,85 @@
+interface ModelData {
+ id: string;
+ name: string;
+ fields: Field[];
+}
+
+interface Field {
+ name: string;
+ type: string;
+ defaultValue: string;
+ validation: string;
+ validationOptions?: {
+ pattern?: string;
+ enum?: string[];
+ min?: number;
+ max?: number;
+ minLength?: number;
+ maxLength?: number;
+ };
+ mapping?: string;
+}
+
+export class TranslationService {
+ static translateModel(model: ModelData): any {
+ return {
+ name: model.name,
+ fields: model.fields.map((field) => ({
+ name: field.name,
+ type: this.translateFieldType(field.type),
+ isPrimaryKey: field.type === "primary key",
+ validation: this.translateValidation(field),
+ defaultValue: field.defaultValue || null,
+ ...(field.mapping ? { mapping: this.parseMapping(field.mapping) } : {}),
+ })),
+ };
+ }
+
+ private static translateFieldType(type: string): string {
+ if (type === "primary key") {
+ return "INTEGER";
+ }
+
+ const typeMap: { [key: string]: string } = {
+ string: "STRING",
+ "long text": "TEXT",
+ integer: "INTEGER",
+ double: "DOUBLE",
+ "big number": "BIGINT",
+ boolean: "BOOLEAN",
+ date: "DATE",
+ datetime: "DATETIME",
+ uuid: "UUID",
+ json: "JSON",
+ mapping: "MAPPING",
+ };
+ return typeMap[type] || type.toUpperCase();
+ }
+
+ private static translateValidation(field: Field): any {
+ if (!field.validation) return null;
+
+ const validation: any = {
+ type: field.validation,
+ };
+
+ if (field.validationOptions) {
+ Object.assign(validation, field.validationOptions);
+ }
+
+ return validation;
+ }
+
+ private static parseMapping(mapping: string): Record
{
+ try {
+ return Object.fromEntries(
+ mapping.split(",").map((pair) => {
+ const [key, value] = pair.split(":");
+ return [key.trim(), value.trim()];
+ })
+ );
+ } catch (e) {
+ return {};
+ }
+ }
+}