Cron-plus: not showing entries in config?

Thanks :wine_glass:

In the meantime, just to whet your appetite, here is some starter code for uibuilder - not much done yet because I keep getting side-tracked as usual - but a start anyway:

Flow:

[{"id":"e6afd4b0.91d068","type":"debug","z":"63281c77.40a064","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":940,"y":2900,"wires":[]},{"id":"db553021.e64ac","type":"uibuilder","z":"63281c77.40a064","name":"","topic":"","url":"cron","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":390,"y":2920,"wires":[["478c4836.d0fa98","2482e719.803068"],[]]},{"id":"755458a1.0e2c28","type":"function","z":"63281c77.40a064","name":"describe cmd","func":"msg.payload = {\n    \"command\": \"describe\",\n    \"expression\": msg.payload\n}\nreturn msg;","outputs":1,"noerr":0,"x":430,"y":3020,"wires":[["29d47dae.6be632","bfda6f87.ffbb1"]]},{"id":"29d47dae.6be632","type":"cronplus","z":"63281c77.40a064","name":"","outputField":"payload","timeZone":"","options":[],"x":620,"y":3020,"wires":[["7c319937.5dc158","5487012f.84a5e"]]},{"id":"478c4836.d0fa98","type":"switch","z":"63281c77.40a064","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"describe","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":560,"y":2900,"wires":[["a768cfd6.d5677","58877c4f.ce50f4"],[]]},{"id":"2dfd1241.3fee8e","type":"link in","z":"63281c77.40a064","name":"describe","links":["a768cfd6.d5677"],"x":315,"y":3020,"wires":[["755458a1.0e2c28"]]},{"id":"a768cfd6.d5677","type":"link out","z":"63281c77.40a064","name":"describe-out","links":["2dfd1241.3fee8e"],"x":675,"y":2860,"wires":[]},{"id":"2482e719.803068","type":"debug","z":"63281c77.40a064","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":570,"y":2820,"wires":[]},{"id":"58877c4f.ce50f4","type":"debug","z":"63281c77.40a064","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":2860,"wires":[]},{"id":"bfda6f87.ffbb1","type":"debug","z":"63281c77.40a064","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":630,"y":3080,"wires":[]},{"id":"7c319937.5dc158","type":"debug","z":"63281c77.40a064","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":770,"y":2980,"wires":[]},{"id":"5487012f.84a5e","type":"link out","z":"63281c77.40a064","name":"cron-out","links":["6f114bd6.d008d4"],"x":780,"y":3040,"wires":[]},{"id":"6f114bd6.d008d4","type":"link in","z":"63281c77.40a064","name":"uib-cron-in","links":["5487012f.84a5e","dee69c83.8a411"],"x":295,"y":2920,"wires":[["db553021.e64ac"]]}]

index.html

<!doctype html><html lang="en"><head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>uibuilder cron-plus example</title>
    <meta name="description" content="Node-RED UI Builder - cron-plus example">
    <link rel="icon" href="./images/node-blue.ico">

    <link rel="preload" as="font" href="../ui/fonts/fontawesome-webfont.woff2" type="font/woff2" crossorigin="anonymous">
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="../vendor/font-awesome/css/font-awesome.min.css" />
    
    <link rel="stylesheet" href="./index.css" media="all">
</head><body>

    <script type="text/x-template" id="cron-input-template">
        <div>
                <b-form inline>
                    <b-form-input
                        id="input-name"
                        v-model="inputName"
                        type="text"
                        required autofocus
                        placeholder="Name for this job"
                    ></b-form-input>
                    <b-form-input
                        id="input-expression"
                        v-model="inputExpression"
                        type="text"
                        required
                        placeholder="CRON expression"
                    ></b-form-input>
                    <b-form-input
                        id="input-payload"
                        v-model="inputPayload"
                        type="text"
                        required
                        placeholder="Output payload"
                    ></b-form-input>
                    <b-button>Add</b-button>
                    <b-button>Refresh</b-button>
                </b-form>
                <b-card>
                    Expression Description ... <br>
                    <b>{{expressionDescription}}</b>
                    <h5>Next</h5>
                    <div>Date: {{nextDate}}</div>
                    <div>Description: {{nextDescription}}</div>
                </b-card>
                <!-- <b-card><pre>{{testOutput}}</pre></b-card> -->
        </div>
    </script>
    <script type="text/x-template" id="cron-schedules-template">
        <div>

        </div>
    </script>
    <script type="text/x-template" id="cron-help-template">
        <div>
            <h5 v-b-toggle.collapse-1 tabindex="0" role="button" class="pointer">
                &#x21f5;
                Help
            </h5>
            <b-collapse id="collapse-1" class="mt-2">
                <b-card>
                    <pre>
*  *  *  *  *  *  *    Field              Allowed values    Special symbols
|  |  |  |  |  |  |    -----------------  ---------------   ---------------
`--|--|--|--|--|--|->  Second (optional)  0-59              * / , -
   `--|--|--|--|--|->  Minute             0-59              * / , -
      `--|--|--|--|->  Hour               0-23              * / , -
         `--|--|--|->  Day of Month       1-31              * / , - L W
            `--|--|->  Month              1-12 or JAN-DEC   * / , -
               `--|->  Day of Week        0-7 or SUN-SAT    * / , - L #
                  `->  Year (optional)    1970-2099         * / , -
                    </pre>
                    <ul>
                        <li>* = All values</li>
                        <li>, = List of expressions, e.g. 4,5,15.</li>
                        <li>/ = Increments of a range. e.g. */3 (every 3 min/hours/etc).</li>
                        <li>- = Range, e.g. 20019-2024.</li>
                        <li>L = Last day of month, or with day of week, last of those days of the month. e.g. WedL, last Wed of the month.</li>
                        <li>W = Nearest weekday (Mon-Fri) of given day of month.</li>
                        <li># = Nth of month. e.g. Tue#3 is the 3rd Tue of the month.</li>
                    </ul>
                    <b-table-lite :items="cronEgs" small hover striped caption-top head-variant="dark">
                        <template v-slot:table-caption>Example CRON Expressions</template>
                    </b-table-lite>
                </b-card>
            </b-collapse>
        </div>
    </script>

    <div id="app" v-cloak>
        <b-container id="app_container">

            <h1>
                UIbuilder - Cron-Plus front-end
            </h1>
            <p>
                This is a <a href="https://github.com/TotallyInformation/node-red-contrib-uibuilder">uibuilder</a> example of allowing end-users to build cron schedules with the help of node-red-contrib-cron-plus.
            </p>

            <cron-input></cron-input>
            <cron-schedules></cron-schedules>
            <cron-help></cron-help>

        </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script> <!-- dev version with component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <script src="./index.js"></script>

</body></html>

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
  Copyright (c) 2019 Julian Knight (Totally Information)

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/
'use strict'

/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */

// Global Template Components
Vue.component('cron-input', {
    props: [],
    template: '#cron-input-template',
    data: function() { return {
        inputName: '',
        inputPayload: '',
        inputExpression: '0 0 * * * *', // If you update this, change the description too
        expressionDescription: 'Every hour',
        nextDate: '',
        nextDescription: '',
        testOutput: '',
    }},
    computed: {
        // inputDescription: function() {
        //     console.log(this.inputName + '/' + this.inputExpression + '/' + this.inputPayload)

        //     return this.inputName + '/' + this.inputExpression + '/' + this.inputPayload
        // },
    },
    methods: {
    },
    watch: {
        /** Whenever inputExpression changes, ask cron-plus for the description */
        inputExpression: function() {
            //console.log(this.inputExpression)
            uibuilder.send({
                'topic': 'describe',
                'payload': this.inputExpression
            })
        },
    },
    mounted: function() {
        var that = this

        uibuilder.onChange('msg', function(msg){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)

            if ( msg.topic === 'describe' )  {
                that.expressionDescription = msg.payload.result.description
                that.nextDate = msg.payload.result.nextDate
                that.nextDescription = msg.payload.result.nextDescription
                console.log('DESCRIBE:', msg.payload)
            } else if ( msg.topic === 'test' )  {
                console.log('test', msg.payload)
                that.testOutput = msg.payload
            }
        })
    }
})
Vue.component('cron-schedules', {
    props: [],
    template: '#cron-schedules-template',
    data: function() { return {
    }},
    computed: {
    },
    methods: {
    },
})
Vue.component('cron-help', {
    props: [],
    template: '#cron-help-template',
    data: function() { return {
        cronEgs: [
            {'expression': '* * * * * *', 'description': 'Every Second',},
            {'expression': '0 * * * * *', 'description': 'Every minute',},
            {'expression': '0 */10 * * * *', 'description': 'Every 10 minutes',},
            {'expression': '0 */20 1 * * *', 'description': 'Every 20 minutes, between 01:00 AM and 01:59 AM',},
            {'expression': '0 15,30,45 * * * *', 'description': 'At 15, 30, and 45 minutes past the hour',},
            {'expression': '0 0 12 * * *', 'description': 'Every day at noon - 12pm',},
            {'expression': '0 0 2 29 FEB * 2020-2040', 'description': 'At 02:00 AM, on day 29 of the month, only in February, every 4 years, 2020 through 2040',},
            {'expression': '0 0 7 * * MON#1 *', 'description': 'At 07:00 AM, on the first Monday of the month',},
            {'expression': '0 0 12 * JAN,FEB,MAR,APR *', 'description': 'Every day at noon in January, February, March and April',},
            {'expression': '* * 1W * *', 'description': 'Every minute, on the first weekday of the month',},
            {'expression': '* * * * Tue#', 'description': 'Every minute, on the third Tuesday of the month',},
            {'expression': '0 12 * * ', 'description': 'At 12:00 PM, on the last Monday of the month',},
        ],
    }},
    computed: {
    },
    methods: {
    },
})


// eslint-disable-next-line no-unused-vars
var app1 = new Vue({
    el: '#app',
    data: {
    }, // --- End of data --- //
    computed: {
    }, // --- End of computed --- //
    methods: {
    }, // --- End of methods --- //

    // Available hooks: init,mounted,updated,destroyed
    mounted: function(){
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names
         */
        uibuilder.start()

        var vueApp = this

        uibuilder.onChange('msg', function(msg){
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
            vueApp.msgRecvd = msg

        })

    } // --- End of mounted hook --- //

}) // --- End of app1 --- //

// EOF

index.css

/* Cloak elements on initial load to hide the possible display of {{ ... }} 
 * Add to the app tag or to specific tags
 * To display "loading...", change to the following:
 *    [v-cloak] > * { display:none }
 *    [v-cloak]::before { content: "loadingā€¦" }
 */
[v-cloak] { display: none; }

.pointer { cursor: pointer; }

Code is split so that the main app is kept minimal and there are 3 components. These could be moved to external .vue files and dynamically loaded if needed.

1 Like