From e636f4e181f97f73f87d16c940b4d039e07558ed Mon Sep 17 00:00:00 2001 From: ryanwong Date: Fri, 15 Nov 2024 05:05:25 -0500 Subject: [PATCH] fix exporting models --- src/components/ModelModal.tsx | 163 +++++++++++++++++++++++------ src/components/ModelPanel.tsx | 15 +-- src/components/SettingsForm.tsx | 69 +++++++++--- src/services/TranslationService.ts | 85 +++++++++++++++ 4 files changed, 275 insertions(+), 57 deletions(-) create mode 100644 src/services/TranslationService.ts 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`} + /> + )} +
+ )} + ); -} \ 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" : ""}

- + +
+ +
); -} \ 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 {}; + } + } +}