feat(ui): add right action bar and use it to start transaction
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 17 Feb 2024 21:41:38 +0000 (22:41 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 17 Feb 2024 21:41:38 +0000 (22:41 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
ui/web/src/App.vue
ui/web/src/components/actions/StartTransaction.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/main.ts
ui/web/src/router/index.ts
ui/web/src/types/ChargingStationType.ts
ui/web/src/types/UIProtocol.ts
ui/web/src/views/ChargingStationsView.vue

index b3fc6d1c4c313122a54143f3d42f3d08ecbd842b..1f2dcf85b10dfd101d9b5647150c6861c164da5c 100644 (file)
@@ -1,7 +1,14 @@
 <template>
   <router-view />
+  <Container id="action">
+    <router-view name="action" />
+  </Container>
 </template>
 
+<script setup lang="ts">
+import Container from '@/components/Container.vue'
+</script>
+
 <style>
 #app {
   height: fit-content;
   font-family: Avenir, Helvetica, Arial, sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
+  display: flex;
+  flex-direction: row;
   color: black;
   background-color: white;
 }
 
+#action {
+  min-width: max-content;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  margin: 0.1%;
+  padding: 0.1%;
+  border: solid black;
+}
+
 body {
-  height: fit-content;
-  width: fit-content;
   margin: 0.005%;
   padding: 0.005%;
 }
diff --git a/ui/web/src/components/actions/StartTransaction.vue b/ui/web/src/components/actions/StartTransaction.vue
new file mode 100644 (file)
index 0000000..4d3e818
--- /dev/null
@@ -0,0 +1,46 @@
+<template>
+  <h2>Action Start Transaction</h2>
+  <h3>Connector {{ connectorId }} on {{ chargingStationId }}</h3>
+  <p>Scan RFID tag:</p>
+  <input id="idtag" v-model="state.idTag" type="text" name="idtag" placeholder="RFID tag" />
+  <br />
+  <Button
+    @click="
+      () => {
+        uiClient
+          .startTransaction(props.hashId, parseInt(props.connectorId), state.idTag)
+          .catch((error: Error) => {
+            // TODO: add code for UI notifications or other error handling logic
+            console.error('Error at starting transaction:', error)
+          })
+          .finally(() => {
+            $router.push({ name: 'charging-stations' })
+          })
+      }
+    "
+    >Start Transaction</Button
+  >
+</template>
+
+<script setup lang="ts">
+import { getCurrentInstance, reactive } from 'vue'
+import Button from '@/components/buttons/Button.vue'
+
+const props = defineProps<{
+  hashId: string
+  chargingStationId: string
+  connectorId: string
+}>()
+
+const state = reactive({
+  idTag: ''
+})
+
+const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
+</script>
+
+<style>
+#idtag {
+  text-align: center;
+}
+</style>
index 81d24face817efd795563e1e5127968091f1914f..c9e53696cde361b52feecbb020ec22200c21c6b8 100644 (file)
@@ -9,7 +9,15 @@
       {{ atgStatus?.start === true ? 'Yes' : 'No' }}
     </td>
     <td class="connectors-table__column">
-      <Button @click="startTransaction()">Start Transaction</Button>
+      <Button
+        @click="
+          $router.push({
+            name: 'start-transaction',
+            params: { hashId, chargingStationId, connectorId }
+          })
+        "
+        >Start Transaction</Button
+      >
       <Button @click="stopTransaction()">Stop Transaction</Button>
       <Button @click="startAutomaticTransactionGenerator()">Start ATG</Button>
       <Button @click="stopAutomaticTransactionGenerator()">Stop ATG</Button>
@@ -24,20 +32,16 @@ import type { ConnectorStatus, Status } from '@/types'
 
 const props = defineProps<{
   hashId: string
+  chargingStationId: string
   connectorId: number
   connector: ConnectorStatus
   atgStatus?: Status
-  transactionId?: number
-  idTag?: string
 }>()
 
 const uiClient = getCurrentInstance()?.appContext.config.globalProperties.$uiClient
 
-function startTransaction(): void {
-  uiClient.startTransaction(props.hashId, props.connectorId, props.idTag)
-}
 function stopTransaction(): void {
-  uiClient.stopTransaction(props.hashId, props.transactionId)
+  uiClient.stopTransaction(props.hashId, props.connector.transactionId)
 }
 function startAutomaticTransactionGenerator(): void {
   uiClient.startAutomaticTransactionGenerator(props.hashId, props.connectorId)
index 5eaa17b012bae731566a2355ed03234b8028fb81..107df4947b540c9ec8b2bdc43818f4484fedc118 100644 (file)
@@ -1,7 +1,7 @@
 <template>
   <tr class="cs-table__row">
     <td class="cs-table__column">
-      {{ props.chargingStation.stationInfo.chargingStationId ?? 'Ø' }}
+      {{ props.chargingStation.stationInfo.chargingStationId }}
     </td>
     <td class="cs-table__column">{{ props.chargingStation.started === true ? 'Yes' : 'No' }}</td>
     <td class="cs-table__column">
           <CSConnector
             v-for="(connector, index) in getConnectors()"
             :hash-id="props.chargingStation.stationInfo.hashId"
+            :charging-station-id="props.chargingStation.stationInfo.chargingStationId"
             :connector-id="index + 1"
             :connector="connector"
             :atg-status="getATGStatus(index + 1)"
-            :transaction-id="connector.transactionId"
-            :id-tag="props.idTag"
           />
         </tbody>
       </table>
@@ -62,7 +61,6 @@ import type { ChargingStationData, ConnectorStatus, Status } from '@/types'
 
 const props = defineProps<{
   chargingStation: ChargingStationData
-  idTag: string
 }>()
 
 function getConnectors(): ConnectorStatus[] {
index 05a8255f97757cf0431d8ce3ac495b036e07bbfd..f8e3bf8b9fa2b0d7e77af8ac72fbb7d0966f893d 100644 (file)
@@ -23,7 +23,6 @@
         v-for="chargingStation in props.chargingStations"
         :key="chargingStation.stationInfo?.hashId"
         :charging-station="chargingStation"
-        :id-tag="props.idTag"
       />
     </tbody>
   </table>
@@ -35,7 +34,6 @@ import type { ChargingStationData } from '@/types'
 
 const props = defineProps<{
   chargingStations: ChargingStationData[]
-  idTag: string
 }>()
 </script>
 
index d321d3d50227ce0e13107ec9d69c8f8ad34f3b54..536b6e84ce99d392aae14e3e3b46f7ec8633de2c 100644 (file)
@@ -45,14 +45,25 @@ export class UIClient {
     return this.sendRequest(ProcedureName.STOP_SIMULATOR, {})
   }
 
-  public async deleteChargingStation(hashId: string): Promise<ResponsePayload> {
-    return this.sendRequest(ProcedureName.DELETE_CHARGING_STATIONS, { hashIds: [hashId] })
+  public async listTemplates(): Promise<ResponsePayload> {
+    return this.sendRequest(ProcedureName.LIST_TEMPLATES, {})
   }
 
   public async listChargingStations(): Promise<ResponsePayload> {
     return this.sendRequest(ProcedureName.LIST_CHARGING_STATIONS, {})
   }
 
+  public async addChargingStations(
+    template: string,
+    numberOfStations: number
+  ): Promise<ResponsePayload> {
+    return this.sendRequest(ProcedureName.ADD_CHARGING_STATIONS, { template, numberOfStations })
+  }
+
+  public async deleteChargingStation(hashId: string): Promise<ResponsePayload> {
+    return this.sendRequest(ProcedureName.DELETE_CHARGING_STATIONS, { hashIds: [hashId] })
+  }
+
   public async startChargingStation(hashId: string): Promise<ResponsePayload> {
     return this.sendRequest(ProcedureName.START_CHARGING_STATION, { hashIds: [hashId] })
   }
index bce88243188951771f87f1b3bade16661a3411b6..7b36de586262d0ad129fee1f7e69b2b7d6f60b0a 100644 (file)
@@ -10,7 +10,7 @@ const initializeApp = (config: ConfigurationData) => {
     console.error('Error:', error)
     console.info('Vue instance:', instance)
     console.info('Error info:', info)
-    // TODO: Add code for UI notifications or other error handling logic
+    // TODO: add code for UI notifications or other error handling logic
   }
   app.config.globalProperties.$uiClient = UIClient.getInstance(config)
   app.config.globalProperties.$uiClient.registerWSonOpenListener(() => {
@@ -20,6 +20,7 @@ const initializeApp = (config: ConfigurationData) => {
         app.config.globalProperties.$chargingStations = response.chargingStations
       })
       .catch((error: Error) => {
+        // TODO: add code for UI notifications or other error handling logic
         console.error('Error at fetching charging stations:', error)
         throw error
       })
index 076ca286832d493addb76883abb332c0c6ffc871..0d250c5101a48e6402934fad6a038f7c64dac78e 100644 (file)
@@ -1,5 +1,7 @@
 import { createRouter, createWebHistory } from 'vue-router'
 import ChargingStationsView from '@/views/ChargingStationsView.vue'
+import StartTransaction from '@/components/actions/StartTransaction.vue'
+import AddChargingStations from '@/components/actions/AddChargingStations.vue'
 
 export const router = createRouter({
   history: createWebHistory(),
@@ -10,6 +12,23 @@ export const router = createRouter({
       components: {
         default: ChargingStationsView
       }
+    },
+    {
+      path: '/add-charging-stations',
+      name: 'add-charging-stations',
+      components: {
+        default: ChargingStationsView,
+        action: AddChargingStations
+      }
+    },
+    {
+      path: '/start-transaction/:hashId/:chargingStationId/:connectorId',
+      name: 'start-transaction',
+      components: {
+        default: ChargingStationsView,
+        action: StartTransaction
+      },
+      props: { default: false, action: true }
     }
   ]
 })
index a0c1b4c734ff9f9fc586063b3389fb34fb2fcae3..882c37889a14fdd4d522cb3978062426876a8496 100644 (file)
@@ -68,7 +68,7 @@ export type ChargingStationInfo = {
   hashId: string
   templateIndex: number
   templateName: string
-  chargingStationId?: string
+  chargingStationId: string
   chargeBoxSerialNumber?: string
   chargePointSerialNumber?: string
   meterSerialNumber?: string
index a431540b7d44bdc0cf41b750dd660ed7d5a7e225..9e7a71a8d4cb43ac93c758e863afed30eba3abaf 100644 (file)
@@ -27,8 +27,10 @@ export type ProtocolRequestHandler = (
 export enum ProcedureName {
   START_SIMULATOR = 'startSimulator',
   STOP_SIMULATOR = 'stopSimulator',
-  DELETE_CHARGING_STATIONS = 'deleteChargingStations',
+  LIST_TEMPLATES = 'listTemplates',
   LIST_CHARGING_STATIONS = 'listChargingStations',
+  ADD_CHARGING_STATIONS = 'addChargingStations',
+  DELETE_CHARGING_STATIONS = 'deleteChargingStations',
   START_CHARGING_STATION = 'startChargingStation',
   STOP_CHARGING_STATION = 'stopChargingStation',
   OPEN_CONNECTION = 'openConnection',
index bd9b239c0463b6073521dffae8427ce25f073111..f5757248a07d5afdeb322937b75f909d4db8ab1e 100644 (file)
@@ -5,20 +5,10 @@
       <Button id="simulator-button" @click="stopSimulator()">Stop Simulator</Button>
       <ReloadButton id="reload-button" :loading="state.isLoading" @click="loadChargingStations()" />
     </Container>
-    <Container id="inputs-container">
-      <input
-        id="idtag-field"
-        v-model="state.idTag"
-        type="text"
-        name="idtag-field"
-        placeholder="RFID tag"
-      />
-    </Container>
     <CSTable
       :charging-stations="
         getCurrentInstance()?.appContext.config.globalProperties.$chargingStations ?? []
       "
-      :id-tag="state.idTag"
     />
   </Container>
 </template>
@@ -32,8 +22,7 @@ import ReloadButton from '@/components/buttons/ReloadButton.vue'
 import Button from '@/components/buttons/Button.vue'
 
 const state = reactive({
-  isLoading: false,
-  idTag: ''
+  isLoading: false
 })
 
 const app = getCurrentInstance()
@@ -50,6 +39,7 @@ function loadChargingStations(): void {
         }
       })
       .catch((error: Error) => {
+        // TODO: add code for UI notifications or other error handling logic
         console.error('Error at fetching charging stations:', error)
       })
       .finally(() => {
@@ -79,11 +69,6 @@ function stopSimulator(): void {
   flex-direction: row;
 }
 
-#inputs-container {
-  display: flex;
-  flex-direction: row;
-}
-
 #reload-button {
   flex: auto;
   color: white;
@@ -105,10 +90,4 @@ function stopSimulator(): void {
 #simulator-button {
   flex: auto;
 }
-
-#idtag-field {
-  flex: auto;
-  font-size: 1.5rem;
-  text-align: center;
-}
 </style>