DB2 vue separate component

Working on migrating to DB2 and this is a long slog. I have a basic 3X3 grid with seven buttons and although I am somewhat happy with this there was a issue trying to get the buttons to indicate pushed. When I push one on one row it deselects a button on the next row. So I stuffed the code into chat gpt and it spit out a new file and then wanted me to create a buttoncard component. Is this something that node red supports? Just for giggles I have attached my code.

<template>
  <v-container>
  
    <v-col style="height: 25px; width: 300px; background-color: black">
      <v-row>
        <v-card class="containerTop">Timer 1</v-card>
        <v-card class="containerTop"></v-card>
        <v-card class="containerTop">Reset</v-card>
      </v-row>
    </v-col>

    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn1">Timer1</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn2"></v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn3" :class="{ 'active': clicked === 3 }">Reset</v-btn>
        </v-card>
      </v-row>
    </v-col>


    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn4" :class="{ 'active': clicked === 4 }">Rnd</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn5" :class="{ 'active': clicked === 5 }">On</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn6" :class="{ 'active': clicked === 6 }">Off</v-btn>
        </v-card>
      </v-row>
    </v-col>

    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn7" :class="{ 'active': clicked === 7 }">24 Hr</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn8"></v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn9" :class="{ 'active': clicked === 9 }">Nite</v-btn>
        </v-card>
      </v-row>
    </v-col>
    
  </v-container>
</template>

<script>
  export default {
  data() {
    return {
      buttonText1: 'Timer1', // Initial button text
      buttonText2: '', // Initial button text
      buttonText3: 'Reset', // Initial button text
      clicked: 1, // Initial clicked state
    };
  },
  methods: {
    btn1() {
      this.clicked = 1;
    },
    btn2() {
    },
    btn3() {
      this.clicked = 3;
      this.send({ payload: "reset" }); // Example: Replace with your MQTT message send logic
    },
    btn4() {
    this.clicked = 4;
    this.send({ payload: "rnd" }); // Example: Replace with your MQTT message send logic
    },
    btn5() {
    this.clicked = 5;
    this.send({ payload: "on" }); // Example: Replace with your MQTT message send logic
    },
    btn6() {
    this.clicked = 6;
    this.send({ payload: "off" }); // Example: Replace with your MQTT message send logic
    },
    btn7() {
    this.clicked = 7;
    this.send({ payload: "24hr" }); // Example: Replace with your MQTT message send logic
    },
    btn8() {
    },
    btn9() {
    this.clicked = 9;
    this.send({ payload: "nite" }); // Example: Replace with your MQTT message send logic
    },
  },
  watch: {
    msg: {
      deep: true,
      handler(newVal) {
        if (newVal.payload === 1) {
          this.clicked = 1;
          this.buttonText1 = 'Timer1';
          this.buttonText2 = '';
          this.buttonText3 = 'Reset';
        } else if (newVal.payload === 2) {
          this.clicked = 2;
          this.buttonText1 = 'Timer1';
          this.buttonText2 = '';
          this.buttonText3 = 'Reset';
        } else if (newVal.payload === 3) {
          this.clicked = 3;
          this.buttonText1 = 'Timer1';
          this.buttonText2 = '';
          this.buttonText3 = 'Reset';
        }
      },
    },
  },
  created() {
    // Example: Fetch initial data from an API or subscribe to MQTT topics
    // this.$mqtt.subscribe('topic1');
    // this.$mqtt.subscribe('topic2');
    // this.$mqtt.subscribe('topic3');
  },
};
</script>

<style scoped>
  .containerTop {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100px;
    height: 25px; /* Adjust height as needed */
    background-color: black;
    color: white;
    font-size: 12px;
    text-align: center;
    border-bottom: 2px solid #dd7f00;
    border-top: 3px solid #6689a1;
    border-left: 3px solid #6689a1;
    border-right: 3px solid #6689a1;
  }
  .container {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100px;
    height: 80px; /* Adjust height as needed */
    background-color: black;
    border-bottom: 3px solid #6689a1;
    border-left: 3px solid #6689a1;
    border-right: 3px solid #6689a1;
  }

  .box {
    width: 25px; /* Adjust box width as needed */
    height: 80px; /* Adjust box height as needed */
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .centered-button {
    padding: 10px 20px;
    color: white;
    font-size: 16px;

    cursor: pointer;
  }

  button {
    display: flex;
    width: 75px;
    padding-top: 2px;
    border-top: 5px;
    background-color: grey; /* Default background color */
    color: white;
    font-size: 12px;
    border: none;
    cursor: pointer;
  }

  .active {
    background-color: green; /* Changed background color when active */
  }

  .a1 {
    background-color: black;
    justify-content: center;
    text-align: center;
    height: 25px !important;
    width: 140px !important;
    margin-top: 5px;
    margin-bottom: 1px;
    margin-left: 5px;
    margin-right: 5px;
    font-size: 16px;
    color: #6689a1;
    padding-bottom: 4px;
    border-bottom: 2px solid #dd7f00;
    border-top: 2px solid #6689a1;
    border-left: 2px solid #6689a1;
    border-right: 2px solid #6689a1;
  }

  .a2 {
    background-color: grey;
    justify-content: center;
    align-items: center;
    text-align: center;
    height: 50px !important;
    width: 100px !important;
    margin-top: 1px;
    margin-bottom: 5px;
    margin-left: 5px;
    margin-right: 5px;
    font-size: 16px;
    color: #6689a1;
    padding-bottom: 2px;
    border-bottom: 2px solid #6689a1;
    border-top: 2px solid #6689a1;
    border-left: 2px solid #6689a1;
    border-right: 2px solid #6689a1;
  }
</style>

Node red? Or dashboard 2?

And what are you asking really? What part are you asking is supported? Did you try this code in a ui-template? At first glance it looks like it would work (very quick glance)

So yes it does work... sort of. When I click on a button in row 2 that button turns green. It should stay green unless I push another button in row 2. But if I push a button in row 3 then it turns off the button in row 2 and only high lights the button in row 3. Does that make sense?

Did you try the built in button group? Button ui-button-group | Node-RED Dashboard 2.0

One button group per row maybe?

yes, but what occurs is that I need three button groups to get the same functionality. Then when I put them on the dashboard there is a gap between the button groups. I am trying to get all the buttons stacked closer together, visually. Can't seem to accomplish that with the individual button groups. Then it became a man vs. machine to try to do it in a template and the machine is winning. This is more of making it look like what I want than doing it the easy way.
[edit]
I have a good working dashboard on my existing installation, and I like the way it looks. It's now trying to duplicate my existing stuff in DB2 and make it look the same. I'm in no hurry to update and that's a good thing as this is taking forever but I want to take the time and do it like I want rather than just slap something together. .

You can adjust the "Widget Gap" in the theme settings for any single dashboard page.
For example this is 2px and the three button groups are pretty close together.
image
It affects all widgets on that page.

It looks like you'll need some CSS jiggery-pokery to make all the buttons the same width.

Edit: Grr there's no "class" field in the button group config!

Clearly an oversight. If you raise an issue we'll get it added.

glad to be of help,

here's my final button group

<template>
  <v-container>

    <!-- Row 1: Timer number and reset -->
    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded >Timer 1</v-btn>
        </v-card>
        
        <v-card class="container">
          
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btnReset('reset')" :class="{ 'active': isActive['reset'] }">Reset</v-btn>
        </v-card>
      </v-row>
    </v-col>

    <!-- Row 2: rnd on off -->
    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btnRnd('rnd')" :class="{ 'active': isActive['rnd'] }">Rnd</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btnOn('on')" :class="{ 'active': isActive['on'] }">On</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btnOff('off')" :class="{ 'active': isActive['off'] }">Off</v-btn>
        </v-card>
      </v-row>
    </v-col>

    <!-- Row 3: 24hr nite -->
    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn24hr('24hr')" :class="{ 'active': isActive['24hr'] }">24Hr
          </v-btn>
        </v-card>
        <v-card class="container">

        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btnNite('nite')" :class="{ 'active': isActive['nite'] }">Nite
          </v-btn>
        </v-card>
      </v-row>
    </v-col>

  </v-container>
</template>

<script>
export default {
  data() {
    return {
      isActive: {

      },
      buttonText: {

      }
    };
  },

  watch: {
    msg: {
    deep: true,
    handler(newVal) {
      if (newVal.topic === 'Rnd' && newVal.payload === true) {
        this.isActive['rnd'] = true;
        this.isActive['on'] = false;
        this.isActive['off'] = false;		
      }
      if (newVal.topic === 'On' && newVal.payload === true) {
        this.isActive['rnd'] = false;
        this.isActive['on'] = true;
        this.isActive['off'] = false;		
      }
      if (newVal.topic === 'Off' && newVal.payload === true) {
        this.isActive['rnd'] = false;
        this.isActive['on'] = false;
        this.isActive['off'] = true;		
      }
      if (newVal.topic === '24hr' && newVal.payload === true) {
      this.isActive['24hr'] = true;
      this.isActive['nite'] = false;
      }
      if (newVal.topic === 'Nite' && newVal.payload === true) {
      this.isActive['24hr'] = false;
      this.isActive['nite'] = true;
      }
    },
    },
  },

  methods: {
     btnReset() {
      this.send({ payload: "reset" }); 
    },
     btnRnd() {
      this.send({ payload: "rnd" }); 
      this.isActive['rnd'] = true;
      this.isActive['on'] = false;
      this.isActive['off'] = false;
    },
     btnOn() {
      this.send({ payload: "on" });
      this.isActive['rnd'] = false;
      this.isActive['on'] = true;
      this.isActive['off'] = false;	  
    },
     btnOff() {
      this.send({ payload: "off" });
      this.isActive['rnd'] = false;
      this.isActive['on'] = false;
      this.isActive['off'] = true;	  
    },
     btn24hr() {
      this.send({ payload: "24hr" });
      this.isActive['24hr'] = true;
      this.isActive['nite'] = false;  
    },
     btnNite() {
      this.send({ payload: "nite" });
      this.isActive['24hr'] = false;
      this.isActive['nite'] = true;  
    },
  },
};
</script>

<style scoped>

.containerTop {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 25px; /* Adjust height as needed */
  background-color: black;
  color: white;
  font-size: 12px;
  text-align: center;
  border-bottom: 2px solid #dd7f00;
  border-top: 3px solid #6689a1;
  border-left: 3px solid #6689a1;
  border-right: 3px solid #6689a1;
}

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 80px; /* Adjust height as needed */
  background-color: black;
  border-top: 3px solid #6689a1;
  border-bottom: 3px solid #6689a1;
  border-left: 3px solid #6689a1;
  border-right: 3px solid #6689a1;
}

.box {
  width: 25px; /* Adjust box width as needed */
  height: 80px; /* Adjust box height as needed */
  display: flex;
  justify-content: center;
  align-items: center;
}

.centered-button {
  padding: 10px 20px;
  color: white;
  font-size: 16px;
  cursor: pointer;
}

.button {
  display: flex;
  width: 75px;
  padding-top: 2px;
  border-top: 5px;
  background-color: grey; /* Default background color */
  color: white;
  font-size: 12px;
  border: none;
  cursor: pointer;
}

.active {
  background-color: green; /* Changed background color when active */
}

.a1 {
  background-color: black;
  justify-content: center;
  text-align: center;
  height: 25px !important;
  width: 140px !important;
  margin-top: 5px;
  margin-bottom: 1px;
  margin-left: 5px;
  margin-right: 5px;
  font-size: 16px;
  color: #6689a1;
  padding-bottom: 4px;
  border-bottom: 2px solid #dd7f00;
  border-top: 2px solid #6689a1;
  border-left: 2px solid #6689a1;
  border-right: 2px solid #6689a1;
}

.a2 {
  background-color: grey;
  justify-content: center;
  align-items: center;
  text-align: center;
  height: 50px !important;
  width: 100px !important;
  margin-top: 1px;
  margin-bottom: 5px;
  margin-left: 5px;
  margin-right: 5px;
  font-size: 16px;
  color: #6689a1;
  padding-bottom: 2px;
  border-bottom: 2px solid #6689a1;
  border-top: 2px solid #6689a1;
  border-left: 2px solid #6689a1;
  border-right: 2px solid #6689a1;
}

</style>

I have modified the code you posted and it now does what I think you were after. It is a bit rough & ready and could be streamlined but it works

<template>
  <v-container>
  
    <v-col style="height: 25px; width: 300px; background-color: black">
      <v-row>
        <v-card class="containerTop">Timer 1</v-card>
        <v-card class="containerTop"></v-card>
        <v-card class="containerTop">Reset</v-card>
      </v-row>
    </v-col>

    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn1">Timer1</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn2"></v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn3" :class="{ 'active': buttonGroup[0] == 3 }">Reset</v-btn>
        </v-card>
      </v-row>
    </v-col>


    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn4" :class="{ 'active': buttonGroup[1] === 1 }">Rnd</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn5" :class="{ 'active': buttonGroup[1] === 2 }">On</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn6" :class="{ 'active': buttonGroup[1] === 3 }">Off</v-btn>
        </v-card>
      </v-row>
    </v-col>

    <v-col style="height: 80px; width: 300px; background-color: black">
      <v-row>
        <v-card class="container">
          <v-btn rounded @click="btn7" :class="{ 'active': buttonGroup[2] === 1 }">24 Hr</v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn8"></v-btn>
        </v-card>
        <v-card class="container">
          <v-btn rounded @click="btn9" :class="{ 'active': buttonGroup[2] === 3 }">Nite</v-btn>
        </v-card>
      </v-row>
    </v-col>
    
  </v-container>

</template>

<script>
  export default {
   data: () => {
    return {
      buttonText1: 'Timer1',        // Initial button text
      buttonText2: '',              // Initial button text
      buttonText3: 'Reset',         // Initial button text
      clicked: 1,                   // Initial clicked state
      buttonGroup: [1, 0, 0],
      
    }

  },

  methods: {
    btn1() {
      this.buttonGroup[0] = 1
    },

    btn2() {
    },

    btn3() {
      this.buttonGroup[0] = 3
      this.send({ payload: "reset" }); // Example: Replace with your MQTT message send logic
    },

    btn4() {
      this.buttonGroup[1] = 1
      this.send({ payload: "rnd" }); // Example: Replace with your MQTT message send logic
    },

    btn5() {
      this.buttonGroup[1] = 2
      this.send({ payload: "on" }); // Example: Replace with your MQTT message send logic
    },

    btn6() {
      this.buttonGroup[1] = 3
      this.send({ payload: "off" }); // Example: Replace with your MQTT message send logic
    },

    btn7() {
      this.buttonGroup[2] = 1
      this.send({ payload: "24hr" }); // Example: Replace with your MQTT message send logic
    },

    btn8() {
    },

    btn9() {
      this.buttonGroup[2] = 3
      this.send({ payload: "nite" }); // Example: Replace with your MQTT message send logic
    },
    
  },

  watch: {
    msg: {
      deep: true,
      handler(newVal) {
        if (newVal.payload === 1) {
          this.buttonGroup[0] = 1
          this.buttonText1 = 'Timer1'
          this.buttonText2 = ''
          this.buttonText3 = 'Reset'

        } else if (newVal.payload === 2) {
          this.buttonGroup[0] = 2
          this.buttonText1 = 'Timer1'
          this.buttonText2 = ''
          this.buttonText3 = 'Reset'

        } else if (newVal.payload === 3) {
          this.buttonGroup[0] = 3
          this.buttonText1 = 'Timer1'
          this.buttonText2 = ''
          this.buttonText3 = 'Reset'

        }
        
      },

    },

  },

  mounted() {
    // Example: Fetch initial data from an API or subscribe to MQTT topics
    // this.$mqtt.subscribe('topic1');
    // this.$mqtt.subscribe('topic2');
    // this.$mqtt.subscribe('topic3');
  },

}

</script>

<style scoped>
  .containerTop {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100px;
    height: 25px; /* Adjust height as needed */
    background-color: black;
    color: white;
    font-size: 12px;
    text-align: center;
    border-bottom: 2px solid #dd7f00;
    border-top: 3px solid #6689a1;
    border-left: 3px solid #6689a1;
    border-right: 3px solid #6689a1;
  }
  .container {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100px;
    height: 80px; /* Adjust height as needed */
    background-color: black;
    border-bottom: 3px solid #6689a1;
    border-left: 3px solid #6689a1;
    border-right: 3px solid #6689a1;
  }

  .box {
    width: 25px; /* Adjust box width as needed */
    height: 80px; /* Adjust box height as needed */
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .centered-button {
    padding: 10px 20px;
    color: white;
    font-size: 16px;

    cursor: pointer;
  }

  button {
    display: flex;
    width: 75px;
    padding-top: 2px;
    border-top: 5px;
    background-color: grey; /* Default background color */
    color: white;
    font-size: 12px;
    border: none;
    cursor: pointer;
  }

  .active {
    background-color: green; /* Changed background color when active */
  }

  .a1 {
    background-color: black;
    justify-content: center;
    text-align: center;
    height: 25px !important;
    width: 140px !important;
    margin-top: 5px;
    margin-bottom: 1px;
    margin-left: 5px;
    margin-right: 5px;
    font-size: 16px;
    color: #6689a1;
    padding-bottom: 4px;
    border-bottom: 2px solid #dd7f00;
    border-top: 2px solid #6689a1;
    border-left: 2px solid #6689a1;
    border-right: 2px solid #6689a1;
  }

  .a2 {
    background-color: grey;
    justify-content: center;
    align-items: center;
    text-align: center;
    height: 50px !important;
    width: 100px !important;
    margin-top: 1px;
    margin-bottom: 5px;
    margin-left: 5px;
    margin-right: 5px;
    font-size: 16px;
    color: #6689a1;
    padding-bottom: 2px;
    border-bottom: 2px solid #6689a1;
    border-top: 2px solid #6689a1;
    border-left: 2px solid #6689a1;
    border-right: 2px solid #6689a1;
  }
</style>