UI Builder - Data Entry Form

Hi. I am very new to NodeRed, so thank you in advance for any support.
I am trying to build a data entry form using UI_Builder and have hit a brick wall, so I am just after a little advice. I have tried two methods with some success in each, so I would just like to know which method is correct and how I should proceed.

First attempt, using just the uibuilder node I modified the html code to create a data entry form, with all validation built in. this form is perfect and performs as expected, but I get no output from the node, Code at the bottom.

Second attempt: I have followed an online tutorial using the uib element with JSON and passing the values. I have got this to work, but I can figure out how to better customise the form, as it seems to be built into the element node.

Can anyone offer any advice how to progress, should I try to extract the data from my form or try to customise the working copy.

Thanks for the support.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AGV Downtime Log</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f0f8ff;
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
        }

        #agvFaultForm {
            background: linear-gradient(to right, #09163F, #052F64, #270447); 
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 600px;
            margin: auto;
            box-sizing: border-box;
        }

        h2 {
            text-align: center;
            color: white;
            margin-bottom: 20px;
            font-size: 32px;
            font-weight: bold;
            word-spacing: 10px;
            letter-spacing: 2px;
        }

        label {
            display: block;
            margin-bottom: 8px;
            color: white;
        }

        input, textarea, select {
            width: 100%;
            padding: 10px;
            margin-bottom: 15px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
            background-color: white;
            color: #09163F;
            font-size: 16px;
        }

        .form-row {
            display: flex;
            justify-content: space-between;
            gap: 15px;
        }

        .form-row .form-column {
            flex: 1;
        }

        .error-message {
            color: red;
            display: none;
        }

        input[type="submit"] {
            background-color: #9F2D99;
            color: white;
            border: none;
            cursor: pointer;
            padding: 12px;
            font-size: 18px;
            text-transform: uppercase;
            width: 100%;
            border-radius: 4px;
        }

        input[type="submit"]:hover {
            background-color: #D259CC;
        }
    </style>
</head>
<body>

<form id="agvFaultForm">
    <h2>AGV DOWNTIME LOG</h2>

    <!-- Row 1: Date & Time -->
    <div class="form-row">
        <div class="form-column">
            <label for="date">Date:</label>
            <input type="date" id="date" name="Date" required>
        </div>
        <div class="form-column">
            <label for="time">Time:</label>
            <input type="time" id="time" name="Time" required>
        </div>
    </div>

    <!-- Row 2: AGV Number & Program Number -->
    <div class="form-row">
        <div class="form-column">
            <label for="agvNumber">AGV Number (001-500):</label>
            <input type="number" id="agvNumber" name="AGV_Number" required min="1" max="500">
            <span class="error-message" id="agvError">AGV number must be between 1 and 500.</span>
        </div>
        <div class="form-column">
            <label for="programNumber">Program Number (00-99):</label>
            <input type="number" id="programNumber" name="Program_Number" required>
            <span class="error-message" id="programError">Program number must be numeric.</span>
        </div>
    </div>

    <!-- Row 3: Step & Junction -->
    <div class="form-row">
        <div class="form-column">
            <label for="step">Step (001-999):</label>
            <input type="number" id="step" name="Step" required>
        </div>
        <div class="form-column">
            <label for="junction">Junction (000-999):</label>
            <input type="number" id="junction" name="Junction">
        </div>
    </div>

    <!-- Row 4: Zone & Station -->
    <div class="form-row">
        <div class="form-column">
            <label for="zone">Zone (000-999):</label>
            <input type="number" id="zone" name="Zone">
        </div>
        <div class="form-column">
            <label for="station">Station (000-999):</label>
            <input type="number" id="station" name="Station">
        </div>
    </div>

    <!-- Row 5: Fault -->
    <div>
        <label for="fault">Fault:</label>
        <select id="fault" name="Fault" required>
            <option value="" disabled selected>Select Fault Type</option>
            <option value="Off_Track">Off Track</option>
            <option value="Step_Error">Step Error</option>
            <option value="Low_Battery">Low Battery</option>
            <option value="AGV_Facility">AGV Facility</option>
            <option value="Trailer_Facility">Trailer Facility</option>
            <option value="Traffic_Management">Traffic Management</option>
            <option value="WiFi_Dropout">WiFi Dropout</option>
            <option value="Auto_Charge_Facility">Auto Charge Facility</option>
            <option value="No_Fault_ID">No Fault ID</option>
        </select>
    </div>

    <!-- Row 6: Comment -->
    <div>
        <label for="comment">Comment:</label>
        <textarea id="comment" name="Comment" rows="3"></textarea>
    </div>

    <!-- Row 7: MTTR & DT -->
    <div class="form-row">
        <div class="form-column">
            <label for="mttr">MTTR:</label>
            <input type="number" id="mttr" name="MTTR" required value="0">
        </div>
        <div class="form-column">
            <label for="dt">DT:</label>
            <input type="number" id="dt" name="DT" required value="0">
        </div>
    </div>

    <!-- Row 8: Battery Voltage & User ID -->
    <div class="form-row">
        <div class="form-column">
            <label for="battVoltage">Battery Voltage (18.0-25.9):</label>
            <input type="number" step="0.1" id="battVoltage" name="BattVoltage" required min="18.0" max="25.9">
            <span class="error-message" id="battVoltageError">Battery voltage must be between 18.0 and 25.9.</span>
        </div>
        <div class="form-column">
            <label for="userID">User ID (UK#####):</label>
            <input type="text" id="userID" name="userID" required value="UK">
            <span class="error-message" id="userIDError">User ID must follow the format UK#####.</span>
        </div>
    </div>

    <!-- Submit Button -->
    <input type="submit" value="Submit">
</form>

<script src="./uibuilderfe.min.js"></script>  <!-- Include uibuilder front-end -->
<script>
    // Initialize uibuilder
    uibuilder.start();

    // Set default date and time to current values
    window.onload = function() {
        let currentDate = new Date();
        let date = currentDate.toISOString().split('T')[0];
        let time = currentDate.toTimeString().split(':');
        let formattedTime = `${time[0]}:${time[1]}`;
        
        document.getElementById('date').value = date;
        document.getElementById('time').value = formattedTime;
    }

    // Handle form submission
    document.getElementById('agvFaultForm').addEventListener('submit', function(e) {
        e.preventDefault();  // Prevent default form submission

        // Capture form data
        let formData = {
            Date: document.getElementById('date').value,
            Time: document.getElementById('time').value,
            AGV_Number: document.getElementById('agvNumber').value,
            Program_Number: document.getElementById('programNumber').value,
            Step: document.getElementById('step').value,
            Junction: document.getElementById('junction').value,
            Zone: document.getElementById('zone').value,
            Station: document.getElementById('station').value,
            BattVoltage: document.getElementById('battVoltage').value,
            Comment: document.getElementById('comment').value,
            MTTR: document.getElementById('mttr').value,
            DT: document.getElementById('dt').value,
            userID: document.getElementById('userID').value,
            Fault: document.getElementById('fault').value
        };

        // Send the form data to Node-RED via uibuilder
        uibuilder.send(formData);
    });
</script>

</body>
</html>

you dont remove the basic html code given in the example, just add the html at the bottom, and it should work.

[{"id":"3dd3b6ebcddf8962","type":"uibuilder","z":"60879e0147c7a351","name":"","topic":"","url":"test1","okToGo":true,"fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"reload":false,"sourceFolder":"src","deployedVersion":"7.0.4","showMsgUib":false,"title":"","descr":"","editurl":"vscode://vscode-remote/ssh-remote+10.180.18.18c:\\Users\\OEEHo\\NR-CLock\\uibuilder/test1/?windowId=_blank","x":610,"y":325,"wires":[[],["f39814a716d4ecf1"]]},{"id":"31a204f17a801fb2","type":"inject","z":"60879e0147c7a351","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":335,"y":425,"wires":[["53639268307b39a0"]]},{"id":"53639268307b39a0","type":"change","z":"60879e0147c7a351","name":"index.html","rules":[{"t":"set","p":"fname","pt":"msg","to":"index.html","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":425,"wires":[["75d0bd1b710fecd3"]]},{"id":"75d0bd1b710fecd3","type":"template","z":"60879e0147c7a351","name":"","field":"payload","fieldType":"msg","format":"html","syntax":"mustache","template":"    <!doctype html>\n<html lang=\"en\"><head>\n\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <link rel=\"icon\" href=\"../uibuilder/images/node-blue.ico\">\n\n    <title>Examples for smanjunath211 - Node-RED uibuilder</title>\n    <meta name=\"description\" content=\"Node-RED uibuilder - Examples for smanjunath211\">\n\n    <!-- Your own CSS (defaults to loading uibuilders css)-->\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->\n    <script src=\"https://cdn.jsdelivr.net/npm/apexcharts\"></script>\n    <script defer src=\"../uibuilder/uibuilder.iife.min.js\"></script>\n    <script defer src=\"./index.js\">/* OPTIONAL: Put your custom code in that */</script>\n    <!-- #endregion -->\n\n</head><body class=\"uib\">\n    \n<h4 class=\"with-subtitle\"> \n</h4>\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>AGV Downtime Log</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            background-color: #f0f8ff;\n            margin: 0;\n            padding: 20px;\n            display: flex;\n            justify-content: center;\n        }\n\n        #agvFaultForm {\n            background: linear-gradient(to right, #09163F, #052F64, #270447); \n            border-radius: 8px;\n            padding: 20px;\n            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);\n            width: 100%;\n            max-width: 600px;\n            margin: auto;\n            box-sizing: border-box;\n        }\n\n        h2 {\n            text-align: center;\n            color: white;\n            margin-bottom: 20px;\n            font-size: 32px;\n            font-weight: bold;\n            word-spacing: 10px;\n            letter-spacing: 2px;\n        }\n\n        label {\n            display: block;\n            margin-bottom: 8px;\n            color: white;\n        }\n\n        input, textarea, select {\n            width: 100%;\n            padding: 10px;\n            margin-bottom: 15px;\n            border: 1px solid #ccc;\n            border-radius: 4px;\n            box-sizing: border-box;\n            background-color: white;\n            color: #09163F;\n            font-size: 16px;\n        }\n\n        .form-row {\n            display: flex;\n            justify-content: space-between;\n            gap: 15px;\n        }\n\n        .form-row .form-column {\n            flex: 1;\n        }\n\n        .error-message {\n            color: red;\n            display: none;\n        }\n\n        input[type=\"submit\"] {\n            background-color: #9F2D99;\n            color: white;\n            border: none;\n            cursor: pointer;\n            padding: 12px;\n            font-size: 18px;\n            text-transform: uppercase;\n            width: 100%;\n            border-radius: 4px;\n        }\n\n        input[type=\"submit\"]:hover {\n            background-color: #D259CC;\n        }\n    </style>\n</head>\n<body>\n\n<form id=\"agvFaultForm\">\n    <h2>AGV DOWNTIME LOG</h2>\n\n    <!-- Row 1: Date & Time -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"date\">Date:</label>\n            <input type=\"date\" id=\"date\" name=\"Date\" required>\n        </div>\n        <div class=\"form-column\">\n            <label for=\"time\">Time:</label>\n            <input type=\"time\" id=\"time\" name=\"Time\" required>\n        </div>\n    </div>\n\n    <!-- Row 2: AGV Number & Program Number -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"agvNumber\">AGV Number (001-500):</label>\n            <input type=\"number\" id=\"agvNumber\" name=\"AGV_Number\" required min=\"1\" max=\"500\">\n            <span class=\"error-message\" id=\"agvError\">AGV number must be between 1 and 500.</span>\n        </div>\n        <div class=\"form-column\">\n            <label for=\"programNumber\">Program Number (00-99):</label>\n            <input type=\"number\" id=\"programNumber\" name=\"Program_Number\" required>\n            <span class=\"error-message\" id=\"programError\">Program number must be numeric.</span>\n        </div>\n    </div>\n\n    <!-- Row 3: Step & Junction -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"step\">Step (001-999):</label>\n            <input type=\"number\" id=\"step\" name=\"Step\" required>\n        </div>\n        <div class=\"form-column\">\n            <label for=\"junction\">Junction (000-999):</label>\n            <input type=\"number\" id=\"junction\" name=\"Junction\">\n        </div>\n    </div>\n\n    <!-- Row 4: Zone & Station -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"zone\">Zone (000-999):</label>\n            <input type=\"number\" id=\"zone\" name=\"Zone\">\n        </div>\n        <div class=\"form-column\">\n            <label for=\"station\">Station (000-999):</label>\n            <input type=\"number\" id=\"station\" name=\"Station\">\n        </div>\n    </div>\n\n    <!-- Row 5: Fault -->\n    <div>\n        <label for=\"fault\">Fault:</label>\n        <select id=\"fault\" name=\"Fault\" required>\n            <option value=\"\" disabled selected>Select Fault Type</option>\n            <option value=\"Off_Track\">Off Track</option>\n            <option value=\"Step_Error\">Step Error</option>\n            <option value=\"Low_Battery\">Low Battery</option>\n            <option value=\"AGV_Facility\">AGV Facility</option>\n            <option value=\"Trailer_Facility\">Trailer Facility</option>\n            <option value=\"Traffic_Management\">Traffic Management</option>\n            <option value=\"WiFi_Dropout\">WiFi Dropout</option>\n            <option value=\"Auto_Charge_Facility\">Auto Charge Facility</option>\n            <option value=\"No_Fault_ID\">No Fault ID</option>\n        </select>\n    </div>\n\n    <!-- Row 6: Comment -->\n    <div>\n        <label for=\"comment\">Comment:</label>\n        <textarea id=\"comment\" name=\"Comment\" rows=\"3\"></textarea>\n    </div>\n\n    <!-- Row 7: MTTR & DT -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"mttr\">MTTR:</label>\n            <input type=\"number\" id=\"mttr\" name=\"MTTR\" required value=\"0\">\n        </div>\n        <div class=\"form-column\">\n            <label for=\"dt\">DT:</label>\n            <input type=\"number\" id=\"dt\" name=\"DT\" required value=\"0\">\n        </div>\n    </div>\n\n    <!-- Row 8: Battery Voltage & User ID -->\n    <div class=\"form-row\">\n        <div class=\"form-column\">\n            <label for=\"battVoltage\">Battery Voltage (18.0-25.9):</label>\n            <input type=\"number\" step=\"0.1\" id=\"battVoltage\" name=\"BattVoltage\" required min=\"18.0\" max=\"25.9\">\n            <span class=\"error-message\" id=\"battVoltageError\">Battery voltage must be between 18.0 and 25.9.</span>\n        </div>\n        <div class=\"form-column\">\n            <label for=\"userID\">User ID (UK#####):</label>\n            <input type=\"text\" id=\"userID\" name=\"userID\" required value=\"UK\">\n            <span class=\"error-message\" id=\"userIDError\">User ID must follow the format UK#####.</span>\n        </div>\n    </div>\n\n    <!-- Submit Button -->\n    <input type=\"submit\" value=\"Submit\">\n</form>\n\n<script src=\"./uibuilderfe.min.js\"></script>  <!-- Include uibuilder front-end -->\n<script>\n    // Initialize uibuilder\n    uibuilder.start();\n\n    // Set default date and time to current values\n    window.onload = function() {\n        let currentDate = new Date();\n        let date = currentDate.toISOString().split('T')[0];\n        let time = currentDate.toTimeString().split(':');\n        let formattedTime = `${time[0]}:${time[1]}`;\n        \n        document.getElementById('date').value = date;\n        document.getElementById('time').value = formattedTime;\n    }\n\n    // Handle form submission\n    document.getElementById('agvFaultForm').addEventListener('submit', function(e) {\n        e.preventDefault();  // Prevent default form submission\n\n        // Capture form data\n        let formData = {\n            Date: document.getElementById('date').value,\n            Time: document.getElementById('time').value,\n            AGV_Number: document.getElementById('agvNumber').value,\n            Program_Number: document.getElementById('programNumber').value,\n            Step: document.getElementById('step').value,\n            Junction: document.getElementById('junction').value,\n            Zone: document.getElementById('zone').value,\n            Station: document.getElementById('station').value,\n            BattVoltage: document.getElementById('battVoltage').value,\n            Comment: document.getElementById('comment').value,\n            MTTR: document.getElementById('mttr').value,\n            DT: document.getElementById('dt').value,\n            userID: document.getElementById('userID').value,\n            Fault: document.getElementById('fault').value\n        };\n\n        // Send the form data to Node-RED via uibuilder\n        uibuilder.send(formData);\n    });\n</script>\n\n</body>\n</html>","output":"str","x":685,"y":425,"wires":[["df9cc5e5ab0c0dcc"]]},{"id":"df9cc5e5ab0c0dcc","type":"uib-save","z":"60879e0147c7a351","url":"test1","uibId":"3dd3b6ebcddf8962","folder":"src","fname":"","createFolder":false,"reload":false,"usePageName":false,"encoding":"utf8","mode":438,"name":"","topic":"","x":825,"y":425,"wires":[]},{"id":"f39814a716d4ecf1","type":"debug","z":"60879e0147c7a351","name":"debug 2727","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":780,"y":300,"wires":[]}]
1 Like

Hi Gavin, thanks for trying UIBUILDER.

As Manjunath has pointed out, you need at least the uibuilder client library from the default template html file. Simply loading it will execute it as the page loads and it creates the link back to Node-RED.

1 Like

Thankyou, I will give it a try and feed back.

Hi. I now have two versions of my code. In version 1 the form looks perfect and operates how I expect with all validation, handling and changing formats based on data, but no link back to NodeRed. In version 2 I have managed to get the link working, but I loose functionality in the form?
Can anyone offer any advice on where I am going wrong please, I am still very new to NodeRed/uibuilder and think i might be missing something obvious.
Thanks

version 1 - Form looking as I would like.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AGV Downtime Log</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #1C1C1C;
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
        }

        #agvFaultForm {
            background: linear-gradient(to right, #09163F, #052F64, #270447); 
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.8);
            width: 100%;
            max-width: 600px;
            margin: auto;
            box-sizing: border-box;
        }

        h2 {
            text-align: center;
            color: white;
            margin-bottom: 20px;
            font-size: 32px;
            font-weight: bold;
            word-spacing: 10px;
            letter-spacing: 2px;
        }

        label {
            display: block;
            text-align: center;
            margin-bottom: 5px;
            color: white;
        }

        input, textarea, select {
            width: 100%;
            text-align: center;
            padding: 10px;
            margin-bottom: 20px;
            border: 2px solid red; /* Default border color is red */
            border-radius: 4px;
            box-sizing: border-box;
            background: #333333;
            box-shadow: inset 0 4px 6px rgba(0, 0, 0, 0.4),
                        inset 0 -4px 6px rgba(255, 255, 255, 0.1);
            color: white;
            font-size: 16px;
            transition: all 0.3s ease;
        }

        input.valid, textarea.valid, select.valid {
            border-color: cyan; /* Change border color to cyan when valid */
        }

        input.invalid, textarea.invalid, select.invalid {
            border-color: red; /* Change border color to red when invalid */
        }

        input:focus, textarea:focus, select:focus {
            outline: none; /* Remove the default focus outline */
            background-color: #444444; /* Slightly change background color on focus */
        }

        input:hover, textarea:hover, select:hover {
            box-shadow: 0 0 15px 3px cyan; /* Cyan glow on hover */
        }

        .form-row {
            display: flex;
            justify-content: space-between;
            gap: 15px;
        }

        .form-row .form-column {
            flex: 1;
        }

        input[type="submit"] {
            background-color: red; /* Default background color is red */
            color: white;
            border: none;
            cursor: not-allowed;
            padding: 10px;
            font-size: 25px;
            text-transform: uppercase;
            width: 100%;
            border-radius: 6px;
            transition: all 0.3s ease;
        }

        input[type="submit"].valid {
            background-color: cyan;
            color: black;
            cursor: pointer;
        }

        input[type="submit"]:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        @media (min-width: 768px) {
            body {
                padding: 40px;
            }
        }
    </style>
</head>
<body>

<form id="agvFaultForm">
    <h2>AGV DOWNTIME LOG</h2>

    <!-- Row 1: Date & Time -->
    <div class="form-row">
        <div class="form-column">
            <label for="date">Date:</label>
            <input type="date" id="date" name="Date" required>
        </div>
        <div class="form-column">
            <label for="time">Time:</label>
            <input type="time" id="time" name="Time" required>
        </div>
    </div>

    <!-- Row 2: AGV Number & Program Number -->
    <div class="form-row">
        <div class="form-column">
            <label for="agvNumber">AGV Number (001-500):</label>
            <input type="number" id="agvNumber" name="AGV_Number" min="1" max="500" required>
        </div>
        <div class="form-column">
            <label for="programNumber">Program Number (01-99):</label>
            <input type="number" id="programNumber" name="Program_Number" min="1" max="99" required>
        </div>
    </div>

    <!-- Row 3: Step & Junction -->
    <div class="form-row">
        <div class="form-column">
            <label for="step">Step (001-999):</label>
            <input type="number" id="step" name="Step" min="1" max="999" required>
        </div>
        <div class="form-column">
            <label for="junction">Junction (000-999):</label>
            <input type="number" id="junction" name="Junction" value="0" min="0" max="999" required>
        </div>
    </div>

    <!-- Row 4: Zone & Station -->
    <div class="form-row">
        <div class="form-column">
            <label for="zone">Zone (000-999):</label>
            <input type="number" id="zone" name="Zone" value="0" min="0" max="999" required>
        </div>
        <div class="form-column">
            <label for="station">Station (000-999):</label>
            <input type="number" id="station" name="Station" value="0" min="0" max="999" required>
        </div>
    </div>

    <!-- Row 5: Fault -->
    <div>
        <label for="fault">Fault:</label>
        <select id="fault" name="Fault" required>
            <option value="" disabled selected>Select Fault Type</option>
            <option value="Off_Track">Off Track</option>
            <option value="Step_Error">Step Error</option>
            <option value="Low_Battery">Low Battery</option>
            <option value="AGV_Facility">AGV Facility</option>
            <option value="Trailer_Facility">Trailer Facility</option>
            <option value="Traffic_Management">Traffic Management</option>
            <option value="WiFi_Dropout">WiFi Dropout</option>
            <option value="Auto_Charge_Facility">Auto Charge Facility</option>
            <option value="No_Fault_ID">No Fault ID</option>
        </select>
    </div>

    <!-- Row 6: Comment -->
    <div>
        <label for="comment">Comment:</label>
        <textarea id="comment" name="Comment" rows="2" required></textarea>
    </div>

    <!-- Row 7: MTTR & DT -->
    <div class="form-row">
        <div class="form-column">
            <label for="mttr">MTTR:</label>
            <input type="number" id="mttr" name="MTTR" min="1" required>
        </div>
        <div class="form-column">
            <label for="dt">DT:</label>
            <input type="number" id="dt" name="DT" min="0" required>
        </div>
    </div>

    <!-- Row 8: Battery Voltage & User ID -->
    <div class="form-row">
        <div class="form-column">
            <label for="battVoltage">Battery Voltage (18.0-25.9):</label>
            <input type="number" id="battVoltage" name="BattVoltage" min="18.0" max="25.9" step="0.1" required>
        </div>
        <div class="form-column">
            <label for="userID">User ID (UK#####):</label>
            <input type="text" id="userID" name="userID" pattern="^UK\d{5}$" title="User ID must follow the format UK followed by 5 digits" required>
        </div>
    </div>

    <!-- Submit Button -->
    <input type="submit" value="Submit" disabled>
</form>

<script>
    // Helper function to check input validity and update borders
    function validateInput(input) {
        if (input.checkValidity()) {
            input.classList.add('valid');
            input.classList.remove('invalid');
        } else {
            input.classList.add('invalid');
            input.classList.remove('valid');
        }
    }

    // Function to check if all fields are valid
    function checkFormValidity() {
        const form = document.getElementById('agvFaultForm');
        const submitButton = document.querySelector('input[type="submit"]');
        const allInputs = form.querySelectorAll('input, select, textarea');

        let allValid = true;

        allInputs.forEach(input => {
            validateInput(input);
            if (!input.checkValidity()) {
                allValid = false;
            }
        });

        if (allValid) {
            submitButton.disabled = false;
            submitButton.classList.add('valid');
        } else {
            submitButton.disabled = true;
            submitButton.classList.remove('valid');
        }
    }

    // Add input event listeners to form fields for validation
    document.querySelectorAll('input, select, textarea').forEach(input => {
        input.addEventListener('input', function () {
            validateInput(input);
            checkFormValidity();
        });
    });

    // Set default date and time
    window.onload = function() {
        let currentDate = new Date();
        let date = currentDate.toISOString().split('T')[0];
        let time = currentDate.toTimeString().split(':');
        let formattedTime = `${time[0]}:${time[1]}`;

        document.getElementById('date').value = date;
        document.getElementById('time').value = formattedTime;
    };

</script>

</body>
</html>

Version 2 with link, but lost formatting

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AGV Downtime Log</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #1C1C1C;
            margin: 0;
            padding: 20px;
            display: flex;
            justify-content: center;
        }

        #agvFaultForm {
            background: linear-gradient(to right, #09163F, #052F64, #270447); 
            border-radius: 8px;
            padding: 20px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.8);
            width: 100%;
            max-width: 600px;
            margin: auto;
            box-sizing: border-box;
        }

        h2 {
            text-align: center;
            color: white;
            margin-bottom: 20px;
            font-size: 32px;
            font-weight: bold;
            word-spacing: 10px;
            letter-spacing: 2px;
        }

        label {
            display: block;
            text-align: center;
            margin-bottom: 5px;
            color: white;
        }

        input, textarea, select {
            width: 100%;
            text-align: center;
            padding: 10px;
            margin-bottom: 20px;
            border: 2px solid red; /* Default border color is red */
            border-radius: 4px;
            box-sizing: border-box;
            background: #333333;
            box-shadow: inset 0 4px 6px rgba(0, 0, 0, 0.4),
                        inset 0 -4px 6px rgba(255, 255, 255, 0.1);
            color: white;
            font-size: 16px;
            transition: all 0.3s ease;
        }

        input.valid, textarea.valid, select.valid {
            border-color: cyan; /* Change border color to cyan when valid */
        }

        input.invalid, textarea.invalid, select.invalid {
            border-color: red; /* Change border color to red when invalid */
        }

        input:focus, textarea:focus, select:focus {
            outline: none; /* Remove the default focus outline */
            background-color: #444444; /* Slightly change background color on focus */
        }

        input:hover, textarea:hover, select:hover {
            box-shadow: 0 0 15px 3px cyan; /* Cyan glow on hover */
        }

        .form-row {
            display: flex;
            justify-content: space-between;
            gap: 15px;
        }

        .form-row .form-column {
            flex: 1;
        }

        input[type="submit"] {
            background-color: red; /* Default background color is red */
            color: white;
            border: none;
            cursor: not-allowed;
            padding: 10px;
            font-size: 25px;
            text-transform: uppercase;
            width: 100%;
            border-radius: 6px;
            transition: all 0.3s ease;
        }

        input[type="submit"].valid {
            background-color: cyan;
            color: black;
            cursor: pointer;
        }

        input[type="submit"]:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        @media (min-width: 768px) {
            body {
                padding: 40px;
            }
        }
    </style>
    <!-- Include uibuilder script for Node-RED -->
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
</head>
<body>

<form id="agvFaultForm">
    <h2>AGV DOWNTIME LOG</h2>

    <!-- Row 1: Date & Time -->
    <div class="form-row">
        <div class="form-column">
            <label for="date">Date:</label>
            <input type="date" id="date" name="Date" required>
        </div>
        <div class="form-column">
            <label for="time">Time:</label>
            <input type="time" id="time" name="Time" required>
        </div>
    </div>

    <!-- Row 2: AGV Number & Program Number -->
    <div class="form-row">
        <div class="form-column">
            <label for="agvNumber">AGV Number (001-500):</label>
            <input type="number" id="agvNumber" name="AGV_Number" min="1" max="500" required>
        </div>
        <div class="form-column">
            <label for="programNumber">Program Number (01-99):</label>
            <input type="number" id="programNumber" name="Program_Number" min="1" max="99" required>
        </div>
    </div>

    <!-- Row 3: Step & Junction -->
    <div class="form-row">
        <div class="form-column">
            <label for="step">Step (001-999):</label>
            <input type="number" id="step" name="Step" min="1" max="999" required>
        </div>
        <div class="form-column">
            <label for="junction">Junction (000-999):</label>
            <input type="number" id="junction" name="Junction" value="0" min="0" max="999" required>
        </div>
    </div>

    <!-- Row 4: Zone & Station -->
    <div class="form-row">
        <div class="form-column">
            <label for="zone">Zone (000-999):</label>
            <input type="number" id="zone" name="Zone" value="0" min="0" max="999" required>
        </div>
        <div class="form-column">
            <label for="station">Station (000-999):</label>
            <input type="number" id="station" name="Station" value="0" min="0" max="999" required>
        </div>
    </div>

    <!-- Row 5: Fault -->
    <div>
        <label for="fault">Fault:</label>
        <select id="fault" name="Fault" required>
            <option value="" disabled selected>Select Fault Type</option>
            <option value="Off_Track">Off Track</option>
            <option value="Step_Error">Step Error</option>
            <option value="Low_Battery">Low Battery</option>
            <option value="AGV_Facility">AGV Facility</option>
            <option value="Trailer_Facility">Trailer Facility</option>
            <option value="Traffic_Management">Traffic Management</option>
            <option value="WiFi_Dropout">WiFi Dropout</option>
            <option value="Auto_Charge_Facility">Auto Charge Facility</option>
            <option value="No_Fault_ID">No Fault ID</option>
        </select>
    </div>

    <!-- Row 6: Comment -->
    <div>
        <label for="comment">Comment:</label>
        <textarea id="comment" name="Comment" rows="2" required></textarea>
    </div>

    <!-- Row 7: MTTR & DT -->
    <div class="form-row">
        <div class="form-column">
            <label for="mttr">MTTR:</label>
            <input type="number" id="mttr" name="MTTR" min="1" required>
        </div>
        <div class="form-column">
            <label for="dt">DT:</label>
            <input type="number" id="dt" name="DT" min="0" required>
        </div>
    </div>

    <!-- Row 8: Battery Voltage & User ID -->
    <div class="form-row">
        <div class="form-column">
            <label for="battVoltage">Battery Voltage (18.0-25.9):</label>
            <input type="number" id="battVoltage" name="BattVoltage" min="18.0" max="25.9" step="0.1" required>
        </div>
        <div class="form-column">
            <label for="userID">User ID (UK#####):</label>
            <input type="text" id="userID" name="userID" pattern="^UK\d{5}$" title="User ID must follow the format UK followed by 5 digits" required>
        </div>
    </div>

    <!-- Submit Button -->
    <input type="submit" value="Submit" disabled>
</form>

<script>
    // Initialize uibuilder and establish the connection with Node-RED
    uibuilder.start();

    // Helper function to check input validity and update borders
    function validateInput(input) {
        if (input.checkValidity()) {
            input.classList.add('valid');
            input.classList.remove('invalid');
        } else {
            input.classList.add('invalid');
            input.classList.remove('valid');
        }
    }

    // Function to check if all fields are valid
    function checkFormValidity() {
        const form = document.getElementById('agvFaultForm');
        const submitButton = document.querySelector('input[type="submit"]');
        const allInputs = form.querySelectorAll('input, select, textarea');

        let allValid = true;

        allInputs.forEach(input => {
            validateInput(input);
            if (!input.checkValidity()) {
                allValid = false;
            }
        });

        if (allValid) {
            submitButton.disabled = false;
            submitButton.classList.add('valid');
        } else {
            submitButton.disabled = true;
            submitButton.classList.remove('valid');
        }
    }

    // Set default date and time
    window.onload = function() {
        let currentDate = new Date();
        let date = currentDate.toISOString().split('T')[0];
        let time = currentDate.toTimeString().split(':');
        let formattedTime = `${time[0]}:${time[1]}`;

        document.getElementById('date').value = date;
        document.getElementById('time').value = formattedTime;
    };

    // Add event listeners for inputs directly when the script is loaded
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelectorAll('input, select, textarea').forEach(input => {
            input.addEventListener('input', function () {
                validateInput(input);
                checkFormValidity();
            });
        });
    });

    // Handle form submission and send data to Node-RED via uibuilder
    document.getElementById('agvFaultForm').addEventListener('submit', function(e) {
        e.preventDefault();

        let formData = {
            Date: document.getElementById('date').value,
            Time: document.getElementById('time').value,
            AGV_Number: document.getElementById('agvNumber').value,
            Program_Number: document.getElementById('programNumber').value,
            Step: document.getElementById('step').value,
            Junction: document.getElementById('junction').value,
            Zone: document.getElementById('zone').value,
            Station: document.getElementById('station').value,
            BattVoltage: document.getElementById('battVoltage').value,
            Comment: document.getElementById('comment').value,
            MTTR: document.getElementById('mttr').value,
            DT: document.getElementById('dt').value,
            userID: document.getElementById('userID').value,
            Fault: document.getElementById('fault').value
        };

        // Send form data to Node-RED using uibuilder
        uibuilder.send(formData);
    });
</script>

</body>
</html>

I think this is because you are mixing two different forms of loading scripts.

If you note how the uibuilder library is loaded, it uses the defer attribute. This is effectively the same as putting the script at the end of your body but results in much cleaner HTML. In your 2nd case, I think the scripts are loading in the wrong order.

If you refer back to the default template than uibuilder helpfully gives you :wink: - you will see that the uibuilder templates come with a minimum of 3 files. One for HTML (that also controls everything on the browser side), one for CSS and one for JavaScript (that is commented out in the default HTML because the uibuilder client sometimes makes it redundant now).

I strongly recommend that you keep your styles in index.css and your javascript in index.js. Not only will this mean that you can actually see what your HTML file is doing but you should find it easier to manage the different code types.

I also strongly recommend setting up a VSCode editor environment that lets you edit your front-end code since that is vastly more powerful and configurable than Node-RED's instance of the Monaco editor which is what is used on the uibuilder node's Files tab.

Thankyou so much for the support. After separating the code into the three sections, all functions perform as expected. My next step is to send the data to MSSQL :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.