feat(ui): introduce toggle button and use it for actions
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 1 Mar 2024 14:33:22 +0000 (15:33 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 1 Mar 2024 14:33:22 +0000 (15:33 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
ui/web/src/components/actions/AddChargingStations.vue
ui/web/src/components/actions/SetSupervisionUrl.vue
ui/web/src/components/actions/StartTransaction.vue
ui/web/src/components/buttons/ToggleButton.vue [new file with mode: 0644]
ui/web/src/components/charging-stations/CSConnector.vue
ui/web/src/components/charging-stations/CSData.vue
ui/web/src/components/charging-stations/CSTable.vue
ui/web/src/composables/UIClient.ts
ui/web/src/composables/Utils.ts
ui/web/src/composables/index.ts
ui/web/src/views/ChargingStationsView.vue

index ff9f5f61472a5c988a7183baab9c728b6edcf10a..a0661a08d083060058d8546bdebcec9e77a860f7 100644 (file)
@@ -2,15 +2,15 @@
   <h1 id="action">Action</h1>
   <h2>Add Charging Stations</h2>
   <p>Template:</p>
-  <select
-    v-show="
-      Array.isArray(app?.appContext.config.globalProperties.$templates) &&
-      app?.appContext.config.globalProperties.$templates.length > 0
-    "
-    v-model="state.template"
-  >
+  <select v-model="state.template">
     <option disabled value="">Please select a template</option>
-    <option v-for="template in app?.appContext.config.globalProperties.$templates">
+    <option
+      v-for="template in app?.appContext.config.globalProperties.$templates"
+      v-show="
+        Array.isArray(app?.appContext.config.globalProperties.$templates) &&
+        app?.appContext.config.globalProperties.$templates.length > 0
+      "
+    >
       {{ template }}
     </option>
   </select>
@@ -95,7 +95,6 @@
   >
     Add Charging Stations
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
index ff2a4b809b446cb61e5d1b3118dcf4b931abe2bc..7118cbc68a17544224192db189f0026ba29379d4 100644 (file)
@@ -32,7 +32,6 @@
   >
     Set Supervision Url
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
index 22acbef3ed9fcab693e4b8e876776588c6b452ef..3c64ba7b78bd3608753a17fba240ca93f235a280 100644 (file)
@@ -26,7 +26,6 @@
   >
     Start Transaction
   </Button>
-  <Button id="action-button" @click="$router.push({ name: 'charging-stations' })">Cancel</Button>
 </template>
 
 <script setup lang="ts">
diff --git a/ui/web/src/components/buttons/ToggleButton.vue b/ui/web/src/components/buttons/ToggleButton.vue
new file mode 100644 (file)
index 0000000..b51c855
--- /dev/null
@@ -0,0 +1,56 @@
+<template>
+  <Button :class="{ on: state.status }" @click="click()">
+    <slot></slot>
+  </Button>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import Button from '@/components/buttons/Button.vue'
+import { getFromLocalStorage, setToLocalStorage } from '@/composables'
+
+const props = defineProps<{
+  id: string
+  status?: boolean
+  shared?: boolean
+  on?: () => void
+  off?: () => void
+}>()
+
+const id = props.shared === true ? `shared-toggle-button-${props.id}` : `toggle-button-${props.id}`
+
+const state = ref({
+  status: getFromLocalStorage<boolean>(id, props.status ?? false)
+})
+
+const click = (): void => {
+  if (props.shared) {
+    for (const key in localStorage) {
+      if (key !== id && key.startsWith('shared-toggle-button-')) {
+        setToLocalStorage<boolean>(key, false)
+        state.value.status = getFromLocalStorage<boolean>(key, false)
+      }
+    }
+  }
+  setToLocalStorage<boolean>(id, !getFromLocalStorage<boolean>(id, props.status ?? false))
+  state.value.status = getFromLocalStorage<boolean>(id, props.status ?? false)
+  // console.log(`----begin----`)
+  // for (const key in localStorage) {
+  //   if (key.startsWith('shared-toggle-button-')) {
+  //     console.log(key, getFromLocalStorage<boolean>(key, props.status ?? false))
+  //   }
+  // }
+  // console.log(`----end----`)
+  if (getFromLocalStorage<boolean>(id, props.status ?? false)) {
+    props.on?.()
+  } else {
+    props.off?.()
+  }
+}
+</script>
+
+<style>
+.on {
+  background-color: lightgrey;
+}
+</style>
index 7c31ae988e1f64b6363361ab7b870c44219b4b8e..d3e37d637e096ce456acc610c16da2b90762171f 100644 (file)
@@ -9,16 +9,25 @@
       {{ atgStatus?.start === true ? 'Yes' : 'No' }}
     </td>
     <td class="connectors-table__column">
-      <Button
-        @click="
-          $router.push({
-            name: 'start-transaction',
-            params: { hashId, chargingStationId, connectorId }
-          })
+      <ToggleButton
+        :id="`${hashId}-start-transaction`"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({
+              name: 'start-transaction',
+              params: { hashId, chargingStationId, connectorId }
+            })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
         "
       >
         Start Transaction
-      </Button>
+      </ToggleButton>
       <Button @click="stopTransaction()">Stop Transaction</Button>
       <Button @click="startAutomaticTransactionGenerator()">Start ATG</Button>
       <Button @click="stopAutomaticTransactionGenerator()">Stop ATG</Button>
@@ -31,6 +40,7 @@ import { getCurrentInstance } from 'vue'
 import { useToast } from 'vue-toast-notification'
 import Button from '@/components/buttons/Button.vue'
 import type { ConnectorStatus, Status } from '@/types'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
 
 const props = defineProps<{
   hashId: string
index 2462e6d6172daeba545bbec7f03add81eae16bf2..375fb3bc0e6f53d52ed679d89b340f7d6a69e34c 100644 (file)
@@ -1,40 +1,49 @@
 <template>
   <tr class="cs-table__row">
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.chargingStationId }}
+      {{ chargingStation.stationInfo.chargingStationId }}
     </td>
-    <td class="cs-table__column">{{ props.chargingStation.started === true ? 'Yes' : 'No' }}</td>
+    <td class="cs-table__column">{{ chargingStation.started === true ? 'Yes' : 'No' }}</td>
     <td class="cs-table__column">
       {{ getSupervisionUrl() }}
     </td>
     <td class="cs-table__column">{{ getWSState() }}</td>
     <td class="cs-table__column">
-      {{ props.chargingStation?.bootNotificationResponse?.status ?? 'Ø' }}
+      {{ chargingStation?.bootNotificationResponse?.status ?? 'Ø' }}
     </td>
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.templateName }}
+      {{ chargingStation.stationInfo.templateName }}
     </td>
-    <td class="cs-table__column">{{ props.chargingStation.stationInfo.chargePointVendor }}</td>
-    <td class="cs-table__column">{{ props.chargingStation.stationInfo.chargePointModel }}</td>
+    <td class="cs-table__column">{{ chargingStation.stationInfo.chargePointVendor }}</td>
+    <td class="cs-table__column">{{ chargingStation.stationInfo.chargePointModel }}</td>
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.firmwareVersion ?? 'Ø' }}
+      {{ chargingStation.stationInfo.firmwareVersion ?? 'Ø' }}
     </td>
     <td class="cs-table__column">
       <Button @click="startChargingStation()">Start Charging Station</Button>
       <Button @click="stopChargingStation()">Stop Charging Station</Button>
-      <Button
-        @click="
-          $router.push({
-            name: 'set-supervision-url',
-            params: {
-              hashId: props.chargingStation.stationInfo.hashId,
-              chargingStationId: props.chargingStation.stationInfo.chargingStationId
-            }
-          })
+      <ToggleButton
+        :id="`${chargingStation.stationInfo.hashId}-set-supervision-url`"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({
+              name: 'set-supervision-url',
+              params: {
+                hashId: chargingStation.stationInfo.hashId,
+                chargingStationId: chargingStation.stationInfo.chargingStationId
+              }
+            })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
         "
       >
         Set Supervision Url
-      </Button>
+      </ToggleButton>
       <Button @click="openConnection()">Open Connection</Button>
       <Button @click="closeConnection()">Close Connection</Button>
       <Button @click="deleteChargingStation()">Delete Charging Station</Button>
@@ -55,8 +64,8 @@
           <!-- eslint-disable-next-line vue/valid-v-for -->
           <CSConnector
             v-for="(connector, index) in getConnectorStatuses()"
-            :hash-id="props.chargingStation.stationInfo.hashId"
-            :charging-station-id="props.chargingStation.stationInfo.chargingStationId"
+            :hash-id="chargingStation.stationInfo.hashId"
+            :charging-station-id="chargingStation.stationInfo.chargingStationId"
             :connector-id="index + 1"
             :connector="connector"
             :atg-status="getATGStatus(index + 1)"
@@ -73,6 +82,7 @@ import { useToast } from 'vue-toast-notification'
 import CSConnector from '@/components/charging-stations/CSConnector.vue'
 import Button from '@/components/buttons/Button.vue'
 import type { ChargingStationData, ConnectorStatus, Status } from '@/types'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
 
 const props = defineProps<{
   chargingStation: ChargingStationData
index b098c0e5558a61728e92030164071df9471a39fd..17f7a0ec21a611783fc7eec84a5cc9c26f48a488 100644 (file)
@@ -21,7 +21,7 @@
     <tbody id="cs-table__body">
       <CSData
         v-for="chargingStation in chargingStations"
-        :key="chargingStation.stationInfo?.chargingStationId"
+        :key="chargingStation.stationInfo.hashId"
         :charging-station="chargingStation"
       />
     </tbody>
index d7a5eb3df6b7d270b733438171a27bdfb5f10faf..491a0b12cacb4c646cf76e72eb8a1ea998be19a9 100644 (file)
@@ -10,6 +10,7 @@ import {
   ResponseStatus,
   type UIServerConfigurationSection
 } from '@/types'
+import { randomUUID } from '@/composables'
 
 type ResponseHandler = {
   procedureName: ProcedureName
@@ -190,7 +191,7 @@ export class UIClient {
   ): Promise<ResponsePayload> {
     return new Promise<ResponsePayload>((resolve, reject) => {
       if (this.ws?.readyState === WebSocket.OPEN) {
-        const uuid = crypto.randomUUID()
+        const uuid = randomUUID()
         const msg = JSON.stringify([uuid, procedureName, payload])
         const sendTimeout = setTimeout(() => {
           this.responseHandlers.delete(uuid)
index fffd7a54966a54a2e092d4eef2a4d5360b45036b..8cd068b3dbdd2f46e074121287d4b279a534b52b 100644 (file)
@@ -41,3 +41,15 @@ export const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
   const item = localStorage.getItem(key)
   return item != null ? (JSON.parse(item) as T) : defaultValue
 }
+
+export const removeFromLocalStorage = (key: string): void => {
+  localStorage.removeItem(key)
+}
+
+export const getLocalStorage = (): Storage => {
+  return localStorage
+}
+
+export const randomUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
+  return crypto.randomUUID()
+}
index bc62dcb5abe4c9245c069b7f1c8e1c3d27ee71fb..278b82b419ff7a0124c572d72323ab5065823b3b 100644 (file)
@@ -1,2 +1,10 @@
 export { UIClient } from './UIClient'
-export { convertToBoolean, convertToInt, getFromLocalStorage, setToLocalStorage } from './Utils'
+export {
+  convertToBoolean,
+  convertToInt,
+  getFromLocalStorage,
+  getLocalStorage,
+  randomUUID,
+  removeFromLocalStorage,
+  setToLocalStorage
+} from './Utils'
index a19c14c32283c7d35591552b9486c6a8f048b85a..fa313d6ec54b002e7c676eca395a499b0f215410 100644 (file)
                 'open',
                 () => {
                   setToLocalStorage<number>('uiServerConfigurationIndex', state.uiServerIndex)
+                  for (const key in getLocalStorage()) {
+                    if (key.includes('toggle-button')) {
+                      removeFromLocalStorage(key)
+                    }
+                  }
                   $router.currentRoute.value.name !== 'charging-stations' &&
                     $router.push({ name: 'charging-stations' })
                 },
     <Container id="buttons-container">
       <Button @click="startSimulator()">Start Simulator</Button>
       <Button @click="stopSimulator()">Stop Simulator</Button>
-      <Button @click="$router.push({ name: 'add-charging-stations' })">
+      <ToggleButton
+        :id="'add-charging-stations'"
+        :shared="true"
+        :on="
+          () => {
+            $router.push({ name: 'add-charging-stations' })
+          }
+        "
+        :off="
+          () => {
+            $router.push({ name: 'charging-stations' })
+          }
+        "
+      >
         Add Charging Stations
-      </Button>
+      </ToggleButton>
       <ReloadButton
         id="reload-button"
         :loading="state.loading"
@@ -81,11 +99,14 @@ import type { ResponsePayload, UIServerConfigurationSection } from '@/types'
 import Container from '@/components/Container.vue'
 import ReloadButton from '@/components/buttons/ReloadButton.vue'
 import Button from '@/components/buttons/Button.vue'
-import { getFromLocalStorage, setToLocalStorage } from '@/composables'
-
-const randomUUID = (): `${string}-${string}-${string}-${string}-${string}` => {
-  return crypto.randomUUID()
-}
+import {
+  getFromLocalStorage,
+  getLocalStorage,
+  randomUUID,
+  removeFromLocalStorage,
+  setToLocalStorage
+} from '@/composables'
+import ToggleButton from '@/components/buttons/ToggleButton.vue'
 
 const app = getCurrentInstance()