Inspired by:
and
My use case: I don't know how many devices are active (each project has different amount of clients) I just listen for incoming MQTT reports and present latest state of data.
The main purpose was to play around with flex-grid and dynamically show newly appearing devices.
Just wanted to share, hoping it might save somebody few hours of googling and chatGPT'ing
P.S I really like dialogs to be able to show that extra bit of information
Reference links:
- https://codepen.io/LeoKi12/pen/zLKmBz
- Vuetify Example Pen (codepen.io)
- https://codepen.io/luizarusso/pen/MWYbZVP - really nice example how to combine multiple stuff
[{"id":"d29341aed8bcf076","type":"inject","z":"f9678504054d3675","name":"Test","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"plc\":\"F001\",\"status\":[{\"title\":\"PLC connection status\",\"icon\":\"server\",\"name\":\"PLC state\",\"state\":\"on\"},{\"title\":\"Camera connection status\",\"icon\":\"camera\",\"name\":\"Camera state\",\"state\":\"off\"}],\"data\":{\"plc\":{\"lastHeartBeat\":\"2024-08-02 13:00:00.123\"},\"camera\":{\"lastHeartBeat\":\"2024-08-01 12:00:00.123\"}}},{\"plc\":\"F002\",\"status\":[{\"title\":\"PLC connection status\",\"icon\":\"server\",\"name\":\"PLC state\",\"state\":\"on\"},{\"title\":\"Camera connection status\",\"icon\":\"camera\",\"name\":\"Camera state\",\"state\":\"on\"}],\"data\":{\"plc\":{\"lastHeartBeat\":\"2024-08-02 13:00:00.123\"},\"camera\":{\"lastHeartBeat\":\"2024-08-01 12:00:00.123\"}}},{\"plc\":\"F003\"},{\"plc\":\"F004\"},{\"plc\":\"F005\"},{\"plc\":\"F006\"},{\"plc\":\"F007\"},{\"plc\":\"F008\"}]","payloadType":"json","x":130,"y":200,"wires":[["9e88cf9f9c542282","a9eea5ec13ab29cc"]]},{"id":"a9eea5ec13ab29cc","type":"ui-template","z":"f9678504054d3675","group":"dc2f0f89e050476e","page":"","ui":"","name":"SystemOverview","order":0,"width":"12","height":"1","head":"","format":"<template>\n <v-row align-items=\"start\">\n <v-col v-for=\"(item, index) in items\" :key=\"item.id\">\n <v-card class=\"mx-auto\" min-width=\"300px\" max-width=\"100%\">\n <v-card-title>\n <h4 class=\"headline mb-0\" v-text=\"item.plc\"></h4>\n <v-btn class=\"ml-auto\" color=\"blue-grey-lighten-5\" icon=\"mdi-information-variant\" @click=\"showInfo(item)\"></v-btn>\n </v-card-title>\n <v-card-text class=\"text--primary\">\n\n <v-treeview v-model=\"tree\" :items=\"item.status\">\n <template v-slot:append=\"{ item}\">\n <v-icon :style=\"{color: getColor(item.state)}\"> {{ getIcon(item.icon) }} </v-icon>\n </template>\n </v-treeview>\n\n <v-dialog v-model=\"dialog\" max-width=\"22%\">\n <v-card>\n <v-card-title>\n {{plcName}} data\n </v-card-title>\n <v-card-text>\n <pre style=\"overflow:auto\">{{ infoData }} </pre>\n </v-card-text>\n <v-card-actions>\n <v-btn color=\"black\" text @click=\"dialog = false\">Close</v-btn>\n </v-card-actions>\n </v-card>\n </v-dialog>\n\n </v-card-text>\n </v-card>\n </v-col>\n </v-row>\n\n</template>\n\n<script>\nexport default {\n data: () => ({\n fd: [],\n plcName: '',\n selectedFile: null,\n dialog: false,\n infoData: {},\n initiallyOpen: ['public'],\n icons: {\n server: 'mdi-check-network',\n camera: 'mdi-check-network',\n },\n tree: [],\n items: [],\n editedIndex: -1,\n }),\n\n methods: {\n getIcon(icon) {\n return this.icons[icon] || 'mdi-connection';\n },\n getColor(state) {\n if (state === 'on') {\n return 'green';\n } else if (state === 'off') {\n return 'red';\n } else {\n return 'grey'; // default color\n }\n },\n showInfo(data) {\n this.plcName = data.plc\n this.infoData = data.data || 'No data available';\n this.dialog = true;\n }\n },\n\n watch: {\n msg: function(){\n const items = this.msg.payload\n this.items = items\n },\n dialog(val) {\n val || this.close();\n },\n },\n};\n</script>\n\n<style>\n .v-col {\n flex: 0 0 100px; /* Ensure the minimum width of each column */\n }\n .v-card-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n</style>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":330,"y":200,"wires":[[]]},{"id":"dc2f0f89e050476e","type":"ui-group","name":"System overview","page":"9f326ef91194bc5d","width":"12","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false"},{"id":"9f326ef91194bc5d","type":"ui-page","name":"Overview","ui":"65cf368b48fdb5f6","path":"/Overview","icon":"home","layout":"grid","theme":"d1f899a55e3aa9ab","order":4,"className":"","visible":"true","disabled":"false"},{"id":"65cf368b48fdb5f6","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control","ui-file-input"],"showPathInSidebar":false,"showPageTitle":true,"navigationStyle":"default","titleBarStyle":"default"},{"id":"d1f899a55e3aa9ab","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#c2c5c7","bgPage":"#f2f2f2","groupBg":"#ffffff","groupOutline":"#e0e0e0"},"sizes":{"pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]