621c7122132ca6fd5564816446ccdaeaeb7bd21e
[e-mobility-charging-stations-simulator.git] / ui / web / src / views / ChargingStationsView.vue
1 <template>
2 <Container id="charging-stations-container">
3 <Container id="buttons-container">
4 <Container
5 v-show="Array.isArray(uiServerConfigurations) && uiServerConfigurations.length > 1"
6 id="ui-server-container"
7 >
8 <select
9 id="ui-server-selector"
10 v-model="state.uiServerIndex"
11 @change="
12 () => {
13 if (
14 getFromLocalStorage<number>('uiServerConfigurationIndex', 0) !== state.uiServerIndex
15 ) {
16 uiClient.setConfiguration(
17 app?.appContext.config.globalProperties.$configuration.uiServer[
18 state.uiServerIndex
19 ]
20 )
21 registerWSEventListeners()
22 uiClient.registerWSEventListener(
23 'open',
24 () => {
25 setToLocalStorage<number>('uiServerConfigurationIndex', state.uiServerIndex)
26 clearToggleButtons()
27 $router.currentRoute.value.name !== 'charging-stations' &&
28 $router.push({ name: 'charging-stations' })
29 },
30 { once: true }
31 )
32 uiClient.registerWSEventListener(
33 'error',
34 () => {
35 state.uiServerIndex = getFromLocalStorage<number>(
36 'uiServerConfigurationIndex',
37 0
38 )
39 uiClient.setConfiguration(
40 app?.appContext.config.globalProperties.$configuration.uiServer[
41 getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
42 ]
43 )
44 registerWSEventListeners()
45 },
46 { once: true }
47 )
48 }
49 }
50 "
51 >
52 <option
53 v-for="uiServerConfiguration in uiServerConfigurations"
54 :value="uiServerConfiguration.index"
55 >
56 {{
57 uiServerConfiguration.configuration.name ?? uiServerConfiguration.configuration.host
58 }}
59 </option>
60 </select>
61 </Container>
62 <ToggleButton
63 :id="'simulator'"
64 :key="state.renderSimulator"
65 :status="state.simulatorState?.started"
66 :on="() => startSimulator()"
67 :off="() => stopSimulator()"
68 :class="simulatorButtonClass"
69 >
70 {{ simulatorButtonMessage }}
71 </ToggleButton>
72 <ToggleButton
73 :id="'add-charging-stations'"
74 :key="state.renderAddChargingStations"
75 :shared="true"
76 :on="
77 () => {
78 $router.push({ name: 'add-charging-stations' })
79 }
80 "
81 :off="
82 () => {
83 $router.push({ name: 'charging-stations' })
84 }
85 "
86 @clicked="
87 () => {
88 state.renderChargingStations = randomUUID()
89 }
90 "
91 >
92 Add Charging Stations
93 </ToggleButton>
94 <ReloadButton
95 id="reload-button"
96 :loading="state.gettingChargingStations"
97 @click="getChargingStations()"
98 />
99 </Container>
100 <CSTable
101 v-show="
102 Array.isArray(app?.appContext.config.globalProperties.$chargingStations) &&
103 app.appContext.config.globalProperties.$chargingStations.length > 0
104 "
105 :key="state.renderChargingStations"
106 :charging-stations="app?.appContext.config.globalProperties.$chargingStations"
107 @need-refresh="
108 () => {
109 state.renderAddChargingStations = randomUUID()
110 state.renderChargingStations = randomUUID()
111 }
112 "
113 />
114 </Container>
115 </template>
116
117 <script setup lang="ts">
118 import { computed, getCurrentInstance, onMounted, onUnmounted, ref } from 'vue'
119 import { useToast } from 'vue-toast-notification'
120 import CSTable from '@/components/charging-stations/CSTable.vue'
121 import type { ResponsePayload, SimulatorState, UIServerConfigurationSection } from '@/types'
122 import Container from '@/components/Container.vue'
123 import ReloadButton from '@/components/buttons/ReloadButton.vue'
124 import {
125 deleteFromLocalStorage,
126 getFromLocalStorage,
127 getLocalStorage,
128 randomUUID,
129 setToLocalStorage
130 } from '@/composables'
131 import ToggleButton from '@/components/buttons/ToggleButton.vue'
132
133 const state = ref<{
134 renderSimulator: `${string}-${string}-${string}-${string}-${string}`
135 renderAddChargingStations: `${string}-${string}-${string}-${string}-${string}`
136 renderChargingStations: `${string}-${string}-${string}-${string}-${string}`
137 gettingSimulatorState: boolean
138 gettingTemplates: boolean
139 gettingChargingStations: boolean
140 simulatorState?: SimulatorState
141 uiServerIndex: number
142 }>({
143 renderSimulator: randomUUID(),
144 renderAddChargingStations: randomUUID(),
145 renderChargingStations: randomUUID(),
146 gettingSimulatorState: false,
147 gettingTemplates: false,
148 gettingChargingStations: false,
149 uiServerIndex: getFromLocalStorage<number>('uiServerConfigurationIndex', 0)
150 })
151
152 const simulatorButtonClass = computed<string>(() =>
153 state.value.simulatorState?.started === true ? 'simulator-stop-button' : 'simulator-start-button'
154 )
155 const simulatorButtonMessage = computed<string>(
156 () =>
157 `${state.value.simulatorState?.started === true ? 'Stop' : 'Start'} Simulator${state.value.simulatorState?.version != null ? ` (${state.value.simulatorState.version})` : ''}`
158 )
159
160 const clearToggleButtons = (): void => {
161 for (const key in getLocalStorage()) {
162 if (key.includes('toggle-button')) {
163 deleteFromLocalStorage(key)
164 }
165 }
166 state.value.renderChargingStations = randomUUID()
167 state.value.renderAddChargingStations = randomUUID()
168 }
169
170 const app = getCurrentInstance()
171
172 const clearChargingStations = (): void => {
173 if (app != null) {
174 app.appContext.config.globalProperties.$chargingStations = []
175 }
176 state.value.renderChargingStations = randomUUID()
177 }
178
179 const uiClient = app?.appContext.config.globalProperties.$uiClient
180
181 const $toast = useToast()
182
183 const getSimulatorState = (): void => {
184 if (state.value.gettingSimulatorState === false) {
185 state.value.gettingSimulatorState = true
186 uiClient
187 .simulatorState()
188 .then((response: ResponsePayload) => {
189 state.value.simulatorState = response.state as SimulatorState
190 })
191 .catch((error: Error) => {
192 $toast.error('Error at fetching simulator state')
193 console.error('Error at fetching simulator state:', error)
194 })
195 .finally(() => {
196 state.value.renderSimulator = randomUUID()
197 state.value.gettingSimulatorState = false
198 })
199 }
200 }
201
202 const getTemplates = (): void => {
203 if (state.value.gettingTemplates === false) {
204 state.value.gettingTemplates = true
205 uiClient
206 .listTemplates()
207 .then((response: ResponsePayload) => {
208 if (app != null) {
209 app.appContext.config.globalProperties.$templates = response.templates
210 }
211 })
212 .catch((error: Error) => {
213 if (app != null) {
214 app.appContext.config.globalProperties.$templates = []
215 }
216 $toast.error('Error at fetching charging station templates')
217 console.error('Error at fetching charging station templates:', error)
218 })
219 .finally(() => {
220 state.value.gettingTemplates = false
221 })
222 }
223 }
224
225 const getChargingStations = (): void => {
226 if (state.value.gettingChargingStations === false) {
227 state.value.gettingChargingStations = true
228 uiClient
229 .listChargingStations()
230 .then((response: ResponsePayload) => {
231 if (app != null) {
232 app.appContext.config.globalProperties.$chargingStations = response.chargingStations
233 }
234 })
235 .catch((error: Error) => {
236 if (app != null) {
237 app.appContext.config.globalProperties.$chargingStations = []
238 }
239 $toast.error('Error at fetching charging stations')
240 console.error('Error at fetching charging stations:', error)
241 })
242 .finally(() => {
243 state.value.renderChargingStations = randomUUID()
244 state.value.gettingChargingStations = false
245 })
246 }
247 }
248
249 const getData = (): void => {
250 getSimulatorState()
251 getTemplates()
252 getChargingStations()
253 }
254
255 const registerWSEventListeners = () => {
256 uiClient.registerWSEventListener('open', getData)
257 uiClient.registerWSEventListener('error', clearChargingStations)
258 uiClient.registerWSEventListener('close', clearChargingStations)
259 }
260
261 const unregisterWSEventListeners = () => {
262 uiClient.unregisterWSEventListener('open', getData)
263 uiClient.unregisterWSEventListener('error', clearChargingStations)
264 uiClient.unregisterWSEventListener('close', clearChargingStations)
265 }
266
267 onMounted(() => {
268 registerWSEventListeners()
269 })
270
271 onUnmounted(() => {
272 unregisterWSEventListeners()
273 })
274
275 const uiServerConfigurations: { index: number; configuration: UIServerConfigurationSection }[] =
276 app?.appContext.config.globalProperties.$configuration.uiServer.map(
277 (configuration: UIServerConfigurationSection, index: number) => ({
278 index,
279 configuration
280 })
281 )
282
283 const startSimulator = (): void => {
284 uiClient
285 .startSimulator()
286 .then(() => {
287 $toast.success('Simulator successfully started')
288 })
289 .catch((error: Error) => {
290 $toast.error('Error at starting simulator')
291 console.error('Error at starting simulator:', error)
292 })
293 .finally(() => {
294 getSimulatorState()
295 })
296 }
297 const stopSimulator = (): void => {
298 uiClient
299 .stopSimulator()
300 .then(() => {
301 if (app != null) {
302 app.appContext.config.globalProperties.$chargingStations = []
303 }
304 $toast.success('Simulator successfully stopped')
305 })
306 .catch((error: Error) => {
307 $toast.error('Error at stopping simulator')
308 console.error('Error at stopping simulator:', error)
309 })
310 .finally(() => {
311 getSimulatorState()
312 })
313 }
314 </script>
315
316 <style>
317 #charging-stations-container {
318 height: fit-content;
319 width: 100%;
320 display: flex;
321 flex-direction: column;
322 }
323
324 #ui-server-container {
325 display: flex;
326 justify-content: center;
327 border-style: outset;
328 }
329
330 #ui-server-selector {
331 width: 100%;
332 background-color: rgb(239, 239, 239);
333 font: small-caption;
334 text-align: center;
335 }
336
337 #ui-server-selector:hover {
338 background-color: rgb(229, 229, 229);
339 }
340
341 #buttons-container {
342 display: flex;
343 flex-direction: row;
344 }
345
346 .simulator-start-button {
347 color: ivory;
348 background-color: green;
349 }
350
351 .simulator-start-button:hover {
352 background-color: rgb(0, 98, 0);
353 }
354
355 .simulator-stop-button {
356 color: ivory;
357 background-color: red;
358 }
359
360 .simulator-stop-button:hover {
361 background-color: rgb(225, 0, 0);
362 }
363
364 #action-button {
365 flex: none;
366 }
367
368 #reload-button {
369 color: ivory;
370 background-color: blue;
371 font-size: 2rem;
372 }
373
374 #reload-button:hover {
375 background-color: rgb(0, 0, 225);
376 }
377
378 #reload-button:active {
379 background-color: darkblue;
380 }
381
382 #action {
383 min-width: max-content;
384 color: ivory;
385 background-color: black;
386 padding: 0.8%;
387 }
388 </style>