diff --git a/frontends/nextjs/src/components/atoms/display/Label.tsx b/frontends/nextjs/src/components/atoms/display/Label.tsx index efb8e4ce0..c5795be68 100644 --- a/frontends/nextjs/src/components/atoms/display/Label.tsx +++ b/frontends/nextjs/src/components/atoms/display/Label.tsx @@ -1,44 +1,35 @@ 'use client' -import { Typography } from '@mui/material' +import { Label as FakemuiLabel } from '@/fakemui' import { forwardRef, LabelHTMLAttributes } from 'react' /** * Props for the Label component - * @extends {LabelHTMLAttributes} Inherits HTML label element attributes + * Wrapper around fakemui Label to maintain API compatibility */ export interface LabelProps extends LabelHTMLAttributes { /** Whether to display a required indicator (*) */ required?: boolean /** Whether to style the label as an error state */ error?: boolean + /** MUI sx prop - converted to className for compatibility */ + sx?: any } const Label = forwardRef( - ({ children, required, error, ...props }, ref) => { + ({ children, required, error, sx, className, ...props }, ref) => { + // Combine className with any sx-based classes + const combinedClassName = [ + className, + sx?.className, + error ? 'label--error' : '', + ].filter(Boolean).join(' ') + return ( - + + {required && *} + ) } ) diff --git a/frontends/nextjs/src/components/atoms/display/Link.tsx b/frontends/nextjs/src/components/atoms/display/Link.tsx index bfc2e0e00..dad707776 100644 --- a/frontends/nextjs/src/components/atoms/display/Link.tsx +++ b/frontends/nextjs/src/components/atoms/display/Link.tsx @@ -1,55 +1,59 @@ 'use client' -import { Link as MuiLink, LinkProps as MuiLinkProps } from '@mui/material' +import { Link as FakemuiLink } from '@/fakemui' import NextLink, { LinkProps as NextLinkProps } from 'next/link' import { forwardRef } from 'react' -export interface LinkProps extends Omit { +/** + * Props for the Link component + * Wrapper around fakemui Link with Next.js integration + */ +export interface LinkProps extends Omit, 'href'> { + /** Link href (Next.js or external URL) */ href: NextLinkProps['href'] + /** Whether this is an external link */ external?: boolean + /** Link underline style */ + underline?: 'none' | 'hover' | 'always' + /** MUI sx prop - converted to className for compatibility */ + sx?: any + /** MUI component prop (ignored for compatibility) */ + component?: any } const Link = forwardRef( - ({ href, external, children, sx, ...props }, ref) => { + ({ href, external, children, underline = 'hover', sx, className, component, ...props }, ref) => { + // Combine className with any sx-based classes + const combinedClassName = [className, sx?.className].filter(Boolean).join(' ') + if (external) { return ( - {children} - + ) } + // For internal links, wrap fakemui Link with Next.js Link return ( - - {children} - + + + {children} + + ) } ) diff --git a/frontends/nextjs/src/components/atoms/display/Text.tsx b/frontends/nextjs/src/components/atoms/display/Text.tsx index 5a45e69cc..1813b399a 100644 --- a/frontends/nextjs/src/components/atoms/display/Text.tsx +++ b/frontends/nextjs/src/components/atoms/display/Text.tsx @@ -1,6 +1,6 @@ 'use client' -import { Typography, TypographyProps } from '@mui/material' +import { Text as FakemuiText, Typography } from '@/fakemui' import { forwardRef } from 'react' export type TextVariant = @@ -20,44 +20,77 @@ export type TextVariant = export type TextWeight = 'light' | 'regular' | 'medium' | 'semibold' | 'bold' export type TextAlign = 'left' | 'center' | 'right' | 'justify' -export interface TextProps extends Omit { +/** + * Props for the Text component + * Wrapper around fakemui Text/Typography to maintain API compatibility + */ +export interface TextProps extends React.HTMLAttributes { + /** Typography variant (MUI compatibility) */ variant?: TextVariant + /** Font weight */ weight?: TextWeight + /** Text alignment */ align?: TextAlign + /** Muted/secondary text style */ muted?: boolean + /** Truncate text with ellipsis */ truncate?: boolean + /** MUI sx prop - converted to className for compatibility */ + sx?: any + /** MUI component prop - specify HTML element */ + component?: React.ElementType } const weightMap = { - light: 300, - regular: 400, - medium: 500, - semibold: 600, - bold: 700, + light: 'font-light', + regular: 'font-normal', + medium: 'font-medium', + semibold: 'font-semibold', + bold: 'font-bold', } const Text = forwardRef( ( - { variant = 'body1', weight = 'regular', align = 'left', muted, truncate, sx, ...props }, + { variant = 'body1', weight = 'regular', align = 'left', muted, truncate, sx, className, component, ...props }, ref ) => { + // For heading variants, use Typography + if (variant && ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(variant)) { + const combinedClassName = [ + className, + sx?.className, + weightMap[weight], + muted ? 'text-secondary' : '', + truncate ? 'truncate' : '', + `text-${align}`, + ].filter(Boolean).join(' ') + + return ( + + ) + } + + // For body/caption variants, use fakemui Text + const combinedClassName = [ + className, + sx?.className, + weightMap[weight], + `text-${align}`, + ].filter(Boolean).join(' ') + return ( - ) diff --git a/packages/dashboard/seed/scripts/stats/card.lua b/packages/dashboard/seed/scripts/stats/card.lua index 3cc9e6f58..1bf024809 100644 --- a/packages/dashboard/seed/scripts/stats/card.lua +++ b/packages/dashboard/seed/scripts/stats/card.lua @@ -7,6 +7,41 @@ local M = {} ---@param props StatCardProps ---@return UIComponent function M.create(props) + -- Build children array with optional icon + local contentChildren = {} + + -- Add icon if provided + if props.icon then + table.insert(contentChildren, { + type = "Icon", + props = { name = props.icon, size = "large", className = "mb-2" } + }) + end + + -- Add label + table.insert(contentChildren, { + type = "Typography", + props = { variant = "overline", text = props.label, className = "text-muted-foreground" } + }) + + -- Add value + table.insert(contentChildren, { + type = "Typography", + props = { variant = "h4", text = tostring(props.value) } + }) + + -- Add change indicator if provided + if props.change then + table.insert(contentChildren, { + type = "Typography", + props = { + variant = "caption", + text = props.change, + className = props.positive and "text-green-500" or "text-red-500" + } + }) + end + return { type = "Card", props = { className = props.className }, @@ -14,14 +49,7 @@ function M.create(props) { type = "CardContent", props = { className = "p-6" }, - children = { - { type = "Typography", props = { variant = "overline", text = props.label, className = "text-muted-foreground" } }, - { type = "Typography", props = { variant = "h4", text = tostring(props.value) } }, - props.change and { - type = "Typography", - props = { variant = "caption", text = props.change, className = props.positive and "text-green-500" or "text-red-500" } - } or nil - } + children = contentChildren } } } diff --git a/packages/dashboard/seed/scripts/stats/types.lua b/packages/dashboard/seed/scripts/stats/types.lua index 4d8030502..48a432471 100644 --- a/packages/dashboard/seed/scripts/stats/types.lua +++ b/packages/dashboard/seed/scripts/stats/types.lua @@ -8,6 +8,7 @@ ---@class StatCardProps ---@field label string ---@field value number|string +---@field icon? string Icon name from fakemui icons ---@field change? string ---@field positive? boolean ---@field className? string diff --git a/packages/form_builder/seed/scripts/icons.lua b/packages/form_builder/seed/scripts/icons.lua new file mode 100644 index 000000000..02cf97c75 --- /dev/null +++ b/packages/form_builder/seed/scripts/icons.lua @@ -0,0 +1,73 @@ +-- Icon mappings for form builder components +-- This module provides icon names that work with fakemui icons + +---@class FormBuilderIcons +local M = {} + +---Common form icon names mapped to fakemui icons +M.icons = { + -- Validation states + CHECK_CIRCLE = "CheckCircle", + CHECK_CIRCLE_OUTLINE = "CheckCircleOutline", + ERROR = "CircleX", + ERROR_OUTLINE = "ErrorOutline", + WARNING = "Warning", + WARNING_AMBER = "WarningAmber", + INFO = "Info", + INFO_OUTLINED = "InfoOutlined", + + -- Field types + TEXT_FIELDS = "TextFields", + EMAIL = "Email", + LOCK = "Lock", + CALENDAR = "Calendar", + DATE_RANGE = "DateRange", + ACCESS_TIME = "AccessTime", + CHECKBOX = "Checkbox", + RADIO = "Radio", + TOGGLE_ON = "ToggleOn", + + -- Actions + ADD = "Add", + REMOVE = "Remove", + EDIT = "Edit", + DELETE = "Delete", + SAVE = "Save", + CLEAR = "Clear", + CLOSE = "Close", + + -- Form controls + VISIBILITY = "Visibility", + VISIBILITY_OFF = "VisibilityOff", + SEARCH = "Search", + FILTER_LIST = "FilterList", + + -- File & media + ATTACH_FILE = "AttachFile", + INSERT_PHOTO = "InsertPhoto", + UPLOAD = "Upload", + FILE_UPLOAD = "UploadSimple", + + -- Rich text + FORMAT_BOLD = "FormatBold", + FORMAT_ITALIC = "FormatItalic", + FORMAT_UNDERLINE = "FormatUnderline", + FORMAT_LIST_BULLETED = "FormatListBulleted", + FORMAT_LIST_NUMBERED = "FormatListNumbered", + INSERT_LINK = "InsertLink", + + -- Form structure + FORM = "Form", + ARTICLE = "Article", + DESCRIPTION = "Description", + LABEL = "LocalOffer", +} + +---Get icon name for a form element +---@param key string Icon key (e.g., "CHECK_CIRCLE") +---@return string icon_name The fakemui icon name +function M.get(key) + return M.icons[key] or "Form" +end + +return M diff --git a/packages/smtp_config/seed/components.json b/packages/smtp_config/seed/components.json new file mode 100644 index 000000000..60b074253 --- /dev/null +++ b/packages/smtp_config/seed/components.json @@ -0,0 +1 @@ +[] diff --git a/packages/smtp_config/seed/metadata.json b/packages/smtp_config/seed/metadata.json new file mode 100644 index 000000000..6eca418a8 --- /dev/null +++ b/packages/smtp_config/seed/metadata.json @@ -0,0 +1,32 @@ +{ + "packageId": "smtp_config", + "name": "SMTP Config", + "version": "1.0.0", + "description": "SMTP configuration editor for email settings", + "icon": "static_content/icon.svg", + "author": "MetaBuilder", + "category": "config", + "dependencies": [], + "devDependencies": ["lua_test"], + "exports": { + "components": [ + "SMTPConfigEditor", + "SMTPTestPanel" + ], + "scripts": [ + "smtp", + "validate" + ] + }, + "tests": { + "scripts": [ + "tests/smtp.test.lua", + "tests/validate.test.lua" + ], + "cases": [ + "tests/smtp.cases.json", + "tests/validate.cases.json" + ] + }, + "minLevel": 5 +} diff --git a/packages/smtp_config/seed/scripts/config.json b/packages/smtp_config/seed/scripts/config.json new file mode 100644 index 000000000..abe1d6de3 --- /dev/null +++ b/packages/smtp_config/seed/scripts/config.json @@ -0,0 +1,61 @@ +{ + "defaults": { + "host": "smtp.example.com", + "port": 587, + "username": "", + "password": "", + "fromEmail": "noreply@metabuilder.com", + "fromName": "MetaBuilder System", + "secure": true + }, + "fields": [ + { + "name": "host", + "label": "SMTP Host", + "type": "text", + "placeholder": "smtp.example.com", + "required": true + }, + { + "name": "port", + "label": "SMTP Port", + "type": "number", + "placeholder": "587", + "required": true + }, + { + "name": "username", + "label": "Username", + "type": "text", + "placeholder": "your-username", + "required": true + }, + { + "name": "password", + "label": "Password", + "type": "password", + "placeholder": "your-password", + "required": true + }, + { + "name": "fromEmail", + "label": "From Email", + "type": "email", + "placeholder": "noreply@metabuilder.com", + "required": true + }, + { + "name": "fromName", + "label": "From Name", + "type": "text", + "placeholder": "MetaBuilder System", + "required": true + }, + { + "name": "secure", + "label": "Use Secure Connection (TLS/SSL)", + "type": "boolean", + "required": false + } + ] +} diff --git a/packages/smtp_config/seed/scripts/smtp/init.lua b/packages/smtp_config/seed/scripts/smtp/init.lua new file mode 100644 index 000000000..b94c53407 --- /dev/null +++ b/packages/smtp_config/seed/scripts/smtp/init.lua @@ -0,0 +1,94 @@ +-- SMTP configuration module +require("smtp.types") +local json = require("json") + +---@class SMTPModule +local M = {} + +-- Load SMTP configuration data from JSON +---@type SMTPConfigData +M.configData = json.load("config.json") + +---Get default SMTP configuration +---@return SMTPConfig +function M.getDefaults() + return M.configData.defaults +end + +---Get all field definitions +---@return SMTPField[] +function M.getFields() + return M.configData.fields +end + +---Get a specific field definition +---@param name string +---@return SMTPField|nil +function M.getField(name) + for _, field in ipairs(M.configData.fields) do + if field.name == name then + return field + end + end + return nil +end + +---Validate SMTP configuration +---@param config SMTPConfig +---@return ValidationResult +function M.validate(config) + local errors = {} + + -- Validate host + if not config.host or config.host == "" then + errors.host = "SMTP host is required" + end + + -- Validate port + if not config.port or config.port < 1 or config.port > 65535 then + errors.port = "Port must be between 1 and 65535" + end + + -- Validate username + if not config.username or config.username == "" then + errors.username = "Username is required" + end + + -- Validate password + if not config.password or config.password == "" then + errors.password = "Password is required" + end + + -- Validate fromEmail + if not config.fromEmail or config.fromEmail == "" then + errors.fromEmail = "From email is required" + elseif not string.match(config.fromEmail, "^[^%s@]+@[^%s@]+%.[^%s@]+$") then + errors.fromEmail = "Invalid email format" + end + + -- Validate fromName + if not config.fromName or config.fromName == "" then + errors.fromName = "From name is required" + end + + return { + valid = next(errors) == nil, + errors = errors + } +end + +---Create a default configuration +---@return SMTPConfig +function M.createDefault() + return { + host = M.configData.defaults.host, + port = M.configData.defaults.port, + username = M.configData.defaults.username, + password = M.configData.defaults.password, + fromEmail = M.configData.defaults.fromEmail, + fromName = M.configData.defaults.fromName, + secure = M.configData.defaults.secure + } +end + +return M diff --git a/packages/smtp_config/seed/scripts/smtp/types.lua b/packages/smtp_config/seed/scripts/smtp/types.lua new file mode 100644 index 000000000..c5339ae49 --- /dev/null +++ b/packages/smtp_config/seed/scripts/smtp/types.lua @@ -0,0 +1,34 @@ +-- Type definitions for SMTP configuration + +---@class SMTPConfig +---@field host string +---@field port number +---@field username string +---@field password string +---@field fromEmail string +---@field fromName string +---@field secure boolean + +---@class SMTPField +---@field name string +---@field label string +---@field type "text"|"number"|"email"|"password"|"boolean" +---@field placeholder? string +---@field required boolean + +---@class SMTPConfigData +---@field defaults SMTPConfig +---@field fields SMTPField[] + +---@class SMTPEditorProps +---@field config? SMTPConfig +---@field testEmail? string + +---@class ValidationResult +---@field valid boolean +---@field errors table + +---@class UIComponent +---@field type string +---@field props? table +---@field children? UIComponent[] diff --git a/packages/workflow_editor/seed/scripts/editor/add_step.lua b/packages/workflow_editor/seed/scripts/editor/add_step.lua new file mode 100644 index 000000000..f8f804db2 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/editor/add_step.lua @@ -0,0 +1,18 @@ +-- Add a workflow step +require("editor.types") + +local M = {} + +---@param step_type string +---@param config table? +---@return WorkflowStep +function M.add_step(step_type, config) + return { + type = "workflow_step", + step_type = step_type, + config = config or {}, + position = { x = 0, y = 0 } + } +end + +return M diff --git a/packages/workflow_editor/seed/scripts/editor/connect_steps.lua b/packages/workflow_editor/seed/scripts/editor/connect_steps.lua new file mode 100644 index 000000000..95dcf6705 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/editor/connect_steps.lua @@ -0,0 +1,19 @@ +-- Connect two workflow steps +require("editor.types") + +local M = {} + +---@param from_id string +---@param to_id string +---@param condition string? +---@return Connection +function M.connect_steps(from_id, to_id, condition) + return { + type = "connection", + from = from_id, + to = to_id, + condition = condition + } +end + +return M diff --git a/packages/workflow_editor/seed/scripts/editor/render.lua b/packages/workflow_editor/seed/scripts/editor/render.lua new file mode 100644 index 000000000..2ffdade3a --- /dev/null +++ b/packages/workflow_editor/seed/scripts/editor/render.lua @@ -0,0 +1,19 @@ +-- Render workflow editor component +require("editor.types") + +local M = {} + +---@param workflow Workflow? +---@return WorkflowEditor +function M.render(workflow) + return { + type = "workflow_editor", + props = { + id = workflow and workflow.id, + name = workflow and workflow.name or "New Workflow", + steps = workflow and workflow.steps or {} + } + } +end + +return M diff --git a/packages/workflow_editor/seed/scripts/editor/types.lua b/packages/workflow_editor/seed/scripts/editor/types.lua new file mode 100644 index 000000000..bd7bfa0ec --- /dev/null +++ b/packages/workflow_editor/seed/scripts/editor/types.lua @@ -0,0 +1,33 @@ +-- Type definitions for workflow editor + +---@class Workflow +---@field id string? +---@field name string? +---@field steps table[]? + +---@class Position +---@field x number +---@field y number + +---@class WorkflowStep +---@field type string +---@field step_type string +---@field config table +---@field position Position + +---@class Connection +---@field type string +---@field from string +---@field to string +---@field condition string? + +---@class WorkflowEditorProps +---@field id string? +---@field name string +---@field steps table[] + +---@class WorkflowEditor +---@field type string +---@field props WorkflowEditorProps + +return {} diff --git a/packages/workflow_editor/seed/scripts/icons.lua b/packages/workflow_editor/seed/scripts/icons.lua new file mode 100644 index 000000000..1e4bd2300 --- /dev/null +++ b/packages/workflow_editor/seed/scripts/icons.lua @@ -0,0 +1,72 @@ +-- Icon mappings for workflow editor components +-- This module provides icon names that work with fakemui icons + +---@class WorkflowIcons +local M = {} + +---Common workflow icon names mapped to fakemui icons +M.icons = { + -- Workflow elements + WORKFLOW = "Workflow", + GIT_BRANCH = "GitBranch", + CALL_SPLIT = "CallSplit", + ACCOUNT_TREE = "AccountTree", + + -- Node types + PLAY = "Play", + PLAY_ARROW = "PlayArrow", + STOP = "Stop", + PAUSE = "Pause", + CHECK_CIRCLE = "CheckCircle", + CIRCLE_X = "CircleX", + + -- Actions + ADD = "Add", + ADD_CIRCLE = "AddCircle", + REMOVE = "Remove", + REMOVE_CIRCLE = "RemoveCircle", + EDIT = "Edit", + DELETE = "Delete", + + -- Flow control + ARROW_RIGHT = "ArrowRight", + ARROW_DOWN = "ArrowDown", + CALL_SPLIT = "CallSplit", + NAVIGATE_NEXT = "NavigateNext", + + -- Status + CHECK = "Check", + DONE = "Done", + ERROR = "CircleX", + WARNING = "Warning", + INFO = "Info", + PENDING = "Clock", + + -- Tools + BUILD = "Build", + CODE = "Code", + TERMINAL = "Terminal", + SETTINGS = "Settings", + + -- Save & Export + SAVE = "Save", + DOWNLOAD = "Download", + UPLOAD = "Upload", + EXPORT = "Export", + + -- View controls + ZOOM_IN = "ZoomIn", + ZOOM_OUT = "ZoomOut", + FULLSCREEN = "Fullscreen", + FULLSCREEN_EXIT = "FullscreenExit", + CENTER_FOCUS = "CenterFocusStrong", +} + +---Get icon name for a workflow element +---@param key string Icon key (e.g., "WORKFLOW") +---@return string icon_name The fakemui icon name +function M.get(key) + return M.icons[key] or "Workflow" +end + +return M