export * from './types/JsonType.js'
export * from './types/UIProtocol.js'
export * from './types/UUID.js'
+export * from './utils/converters.js'
export * from './utils/UUID.js'
+export * from './utils/websocket.js'
--- /dev/null
+export const convertToBoolean = (value: unknown): boolean => {
+ let result = false
+ if (value != null) {
+ if (typeof value === 'boolean') {
+ return value
+ } else if (typeof value === 'string') {
+ const normalized = value.trim().toLowerCase()
+ result = normalized === 'true' || normalized === '1'
+ } else if (typeof value === 'number' && value === 1) {
+ result = true
+ }
+ }
+ return result
+}
+
+export const convertToInt = (value: unknown): number => {
+ if (value == null) {
+ return 0
+ }
+ if (Number.isSafeInteger(value)) {
+ return value as number
+ }
+ if (typeof value === 'number') {
+ return Math.trunc(value)
+ }
+ let changedValue: number = value as number
+ if (typeof value === 'string') {
+ changedValue = Number.parseInt(value)
+ }
+ if (Number.isNaN(changedValue)) {
+ // eslint-disable-next-line @typescript-eslint/no-base-to-string
+ throw new Error(`Cannot convert to integer: '${value.toString()}'`)
+ }
+ return changedValue
+}
--- /dev/null
+import { WebSocketReadyState } from '../client/types.js'
+
+export const getWebSocketStateName = (state: number | undefined): string | undefined => {
+ switch (state) {
+ case WebSocketReadyState.CLOSED:
+ return 'Closed'
+ case WebSocketReadyState.CLOSING:
+ return 'Closing'
+ case WebSocketReadyState.CONNECTING:
+ return 'Connecting'
+ case WebSocketReadyState.OPEN:
+ return 'Open'
+ default:
+ return undefined
+ }
+}
</template>
<script setup lang="ts">
-import { randomUUID, type UUIDv4 } from 'ui-common'
+import { convertToBoolean, randomUUID, type UUIDv4 } from 'ui-common'
import { ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import Button from '@/components/buttons/Button.vue'
import {
- convertToBoolean,
resetToggleButtonState,
ROUTE_NAMES,
useExecuteAction,
</template>
<script setup lang="ts">
-import { type OCPPVersion } from 'ui-common'
+import { convertToInt, type OCPPVersion } from 'ui-common'
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useToast } from 'vue-toast-notification'
import Button from '@/components/buttons/Button.vue'
-import { convertToInt, resetToggleButtonState, ROUTE_NAMES, useUIClient } from '@/composables'
+import { resetToggleButtonState, ROUTE_NAMES, useUIClient } from '@/composables'
const props = defineProps<{
chargingStationId: string
{{ getSupervisionUrl() }}
</td>
<td>
- {{ getWebSocketStateName(chargingStation.wsState) }}
+ {{ getWebSocketStateName(chargingStation.wsState) ?? EMPTY_VALUE_PLACEHOLDER }}
</td>
<td>
{{ chargingStation.bootNotificationResponse?.status ?? EMPTY_VALUE_PLACEHOLDER }}
</template>
<script setup lang="ts">
-import type { ChargingStationData, ConnectorEntry, Status } from 'ui-common'
-
+import { type ChargingStationData, type ConnectorEntry, getWebSocketStateName, type Status } from 'ui-common'
import { computed } from 'vue'
import { useToast } from 'vue-toast-notification'
import {
deleteLocalStorageByKeyPattern,
EMPTY_VALUE_PLACEHOLDER,
- getWebSocketStateName,
ROUTE_NAMES,
useExecuteAction,
useUIClient,
-import type { ChargingStationData, ConfigurationData } from 'ui-common'
+import type { ChargingStationData, ConfigurationData, ResponsePayload } from 'ui-common'
import type { InjectionKey, Ref } from 'vue'
-import { inject } from 'vue'
+import { inject, ref as vueRef } from 'vue'
import { useToast } from 'vue-toast-notification'
-import {
- EMPTY_VALUE_PLACEHOLDER,
- SHARED_TOGGLE_BUTTON_KEY_PREFIX,
- TOGGLE_BUTTON_KEY_PREFIX,
-} from './Constants'
+import { SHARED_TOGGLE_BUTTON_KEY_PREFIX, TOGGLE_BUTTON_KEY_PREFIX } from './Constants'
import { UIClient } from './UIClient'
export const configurationKey: InjectionKey<Ref<ConfigurationData>> = Symbol('configuration')
export const templatesKey: InjectionKey<Ref<string[]>> = Symbol('templates')
export const uiClientKey: InjectionKey<UIClient> = Symbol('uiClient')
-export const convertToBoolean = (value: unknown): boolean => {
- let result = false
- if (value != null) {
- // Check the type
- if (typeof value === 'boolean') {
- return value
- } else if (typeof value === 'string') {
- const normalized = value.trim().toLowerCase()
- result = normalized === 'true' || normalized === '1'
- } else if (typeof value === 'number' && value === 1) {
- result = true
- }
- }
- return result
-}
-
-export const convertToInt = (value: unknown): number => {
- if (value == null) {
- return 0
- }
- if (Number.isSafeInteger(value)) {
- return value as number
- }
- if (typeof value === 'number') {
- return Math.trunc(value)
- }
- let changedValue: number = value as number
- if (typeof value === 'string') {
- changedValue = Number.parseInt(value)
- }
- if (Number.isNaN(changedValue)) {
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
- throw new Error(`Cannot convert to integer: '${value.toString()}'`)
- }
- return changedValue
-}
-
export const getFromLocalStorage = <T>(key: string, defaultValue: T): T => {
const item = localStorage.getItem(key)
return item != null ? (JSON.parse(item) as T) : defaultValue
}
}
-/**
- * Returns a human-readable name for a WebSocket ready state.
- * @param state - The WebSocket readyState value
- * @returns The state name or EMPTY_VALUE_PLACEHOLDER for unknown/undefined states
- */
-export const getWebSocketStateName = (state: number | undefined): string => {
- switch (state) {
- case WebSocket.CLOSED:
- return 'Closed'
- case WebSocket.CLOSING:
- return 'Closing'
- case WebSocket.CONNECTING:
- return 'Connecting'
- case WebSocket.OPEN:
- return 'Open'
- default:
- return EMPTY_VALUE_PLACEHOLDER
- }
-}
-
/**
* Resets the state of a toggle button by removing its entry from localStorage.
* @param id - The identifier of the toggle button
})
}
}
+
+export const useFetchData = (
+ clientFn: () => Promise<ResponsePayload>,
+ onSuccess: (response: ResponsePayload) => void,
+ errorMsg: string,
+ onError?: () => void
+): { fetch: () => void; fetching: Ref<boolean> } => {
+ const fetching = vueRef(false)
+ const $toast = useToast()
+ const fetch = (): void => {
+ if (!fetching.value) {
+ fetching.value = true
+ clientFn()
+ .then((response: ResponsePayload) => {
+ onSuccess(response)
+ return undefined
+ })
+ .finally(() => {
+ fetching.value = false
+ })
+ .catch((error: unknown) => {
+ onError?.()
+ $toast.error(errorMsg)
+ console.error(`${errorMsg}:`, error)
+ })
+ }
+ }
+ return { fetch, fetching }
+}
export {
chargingStationsKey,
configurationKey,
- convertToBoolean,
- convertToInt,
deleteFromLocalStorage,
deleteLocalStorageByKeyPattern,
getFromLocalStorage,
getLocalStorage,
- getWebSocketStateName,
resetToggleButtonState,
setToLocalStorage,
templatesKey,
useChargingStations,
useConfiguration,
useExecuteAction,
+ useFetchData,
useTemplates,
useUIClient,
} from './Utils'
import {
type ChargingStationData,
randomUUID,
- type ResponsePayload,
type SimulatorState,
type UIServerConfigurationSection,
type UUIDv4,
UI_SERVER_CONFIGURATION_INDEX_KEY,
useChargingStations,
useConfiguration,
+ useFetchData,
useTemplates,
useUIClient,
} from '@/composables'
}`
const state = ref<{
- gettingChargingStations: boolean
- gettingSimulatorState: boolean
- gettingTemplates: boolean
renderAddChargingStations: UUIDv4
renderChargingStations: UUIDv4
uiServerIndex: number
}>({
- gettingChargingStations: false,
- gettingSimulatorState: false,
- gettingTemplates: false,
renderAddChargingStations: randomUUID(),
renderChargingStations: randomUUID(),
uiServerIndex: getFromLocalStorage<number>(UI_SERVER_CONFIGURATION_INDEX_KEY, 0),
const $toast = useToast()
-const getSimulatorState = (): void => {
- if (state.value.gettingSimulatorState === false) {
- state.value.gettingSimulatorState = true
- $uiClient
- .simulatorState()
- .then((response: ResponsePayload) => {
- simulatorState.value = response.state as unknown as SimulatorState
- return undefined
- })
- .finally(() => {
- state.value.gettingSimulatorState = false
- })
- .catch((error: Error) => {
- $toast.error('Error at fetching simulator state')
- console.error('Error at fetching simulator state:', error)
- })
- }
-}
+const { fetch: getSimulatorState } = useFetchData(
+ () => $uiClient.simulatorState(),
+ (response) => {
+ simulatorState.value = response.state as unknown as SimulatorState
+ },
+ 'Error at fetching simulator state'
+)
-const getTemplates = (): void => {
- if (state.value.gettingTemplates === false) {
- state.value.gettingTemplates = true
- $uiClient
- .listTemplates()
- .then((response: ResponsePayload) => {
- $templates.value = response.templates as string[]
- return undefined
- })
- .finally(() => {
- state.value.gettingTemplates = false
- })
- .catch((error: Error) => {
- clearTemplates()
- $toast.error('Error at fetching charging station templates')
- console.error('Error at fetching charging station templates:', error)
- })
- }
-}
+const { fetch: getTemplates } = useFetchData(
+ () => $uiClient.listTemplates(),
+ (response) => {
+ $templates.value = response.templates as string[]
+ },
+ 'Error at fetching charging station templates',
+ clearTemplates
+)
-const getChargingStations = (): void => {
- if (state.value.gettingChargingStations === false) {
- state.value.gettingChargingStations = true
- $uiClient
- .listChargingStations()
- .then((response: ResponsePayload) => {
- $chargingStations.value = response.chargingStations as ChargingStationData[]
- return undefined
- })
- .finally(() => {
- state.value.gettingChargingStations = false
- })
- .catch((error: Error) => {
- clearChargingStations()
- $toast.error('Error at fetching charging stations')
- console.error('Error at fetching charging stations:', error)
- })
- }
-}
+const { fetch: getChargingStations } = useFetchData(
+ () => $uiClient.listChargingStations(),
+ (response) => {
+ $chargingStations.value = response.chargingStations as ChargingStationData[]
+ },
+ 'Error at fetching charging stations',
+ clearChargingStations
+)
const getData = (): void => {
getSimulatorState()
* @description Unit tests for type conversion, localStorage, UUID, and toggle state utilities.
*/
import { flushPromises } from '@vue/test-utils'
-import { randomUUID, validateUUID } from 'ui-common'
+import { convertToBoolean, convertToInt, randomUUID, validateUUID } from 'ui-common'
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
- convertToBoolean,
- convertToInt,
deleteFromLocalStorage,
getFromLocalStorage,
getLocalStorage,