Skip to content

Commit 4d95276

Browse files
luizhf42gustavosbarreto
authored andcommitted
refactor(ui): migrate Web Endpoints store to Pinia
1 parent 0176446 commit 4d95276

File tree

16 files changed

+219
-424
lines changed

16 files changed

+219
-424
lines changed

ui/src/components/WebEndpoints/WebEndpointCreate.vue

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
variant="outlined"
5151
return-object
5252
hide-details
53-
@click:control="fetchDevices"
5453
@update:search="fetchDevices"
5554
data-test="web-endpoint-autocomplete"
5655
>
@@ -122,41 +121,32 @@
122121
</template>
123122

124123
<script setup lang="ts">
125-
import { ref, computed } from "vue";
124+
import { ref, computed, onMounted } from "vue";
126125
import { useField } from "vee-validate";
127126
import * as yup from "yup";
128127
import axios, { AxiosError } from "axios";
129128
import DeviceIcon from "@/components/Devices/DeviceIcon.vue";
130-
import { useStore } from "@/store";
131129
import handleError from "@/utils/handleError";
132130
import useSnackbar from "@/helpers/snackbar";
133131
import BaseDialog from "../BaseDialog.vue";
134132
import useDevicesStore from "@/store/modules/devices";
135-
136-
interface DeviceOption {
137-
uid: string;
138-
name: string;
139-
info: {
140-
id: string;
141-
pretty_name: string;
142-
};
143-
[key: string]: unknown;
144-
}
133+
import useWebEndpointsStore from "@/store/modules/web_endpoints";
134+
import { IDevice } from "@/interfaces/IDevice";
145135
146136
const props = defineProps<{
147137
uid?: string;
148138
useDevicesList: boolean;
149139
}>();
150140
151141
const emit = defineEmits(["update"]);
152-
const store = useStore();
153142
const devicesStore = useDevicesStore();
143+
const webEndpointsStore = useWebEndpointsStore();
154144
const snackbar = useSnackbar();
155145
const showDialog = defineModel({ default: false });
156146
const alertText = ref();
157147
158-
const selectedDevice = ref<DeviceOption | null>(null);
159-
const deviceOptions = ref<DeviceOption[]>([]);
148+
const selectedDevice = ref<IDevice | null>(null);
149+
const deviceOptions = ref<IDevice[]>([]);
160150
const loadingDevices = ref(false);
161151
162152
const predefinedTimeouts = ref([
@@ -223,21 +213,19 @@ const close = () => { resetFields(); showDialog.value = false; };
223213
const update = () => { emit("update"); close(); };
224214
225215
const fetchDevices = async (searchQuery?: string) => {
226-
if (!searchQuery && deviceOptions.value.length > 0) return;
227-
228216
loadingDevices.value = true;
229217
230218
const filter = searchQuery
231219
? btoa(JSON.stringify([
232220
{ type: "property", params: { name: "name", operator: "contains", value: searchQuery } },
233221
]))
234-
: "";
235-
222+
: undefined;
236223
try {
237224
await devicesStore.fetchDeviceList({ filter });
238225
deviceOptions.value = devicesStore.devices;
239-
} catch {
226+
} catch (error) {
240227
snackbar.showError("Failed to load devices.");
228+
handleError(error);
241229
}
242230
243231
loadingDevices.value = false;
@@ -251,8 +239,8 @@ const addWebEndpoint = async () => {
251239
: props.uid;
252240
253241
try {
254-
await store.dispatch("webEndpoints/create", {
255-
uid: deviceUid,
242+
await webEndpointsStore.createWebEndpoint({
243+
uid: deviceUid as string,
256244
host: host.value,
257245
port: port.value,
258246
ttl: timeout.value,
@@ -271,4 +259,6 @@ const addWebEndpoint = async () => {
271259
}
272260
}
273261
};
262+
263+
onMounted(async () => { if (props.useDevicesList) await fetchDevices(); });
274264
</script>

ui/src/components/WebEndpoints/WebEndpointDelete.vue

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,29 +36,24 @@
3636
</template>
3737

3838
<script setup lang="ts">
39-
import { useStore } from "@/store";
4039
import hasPermission from "@/utils/permission";
4140
import { actions, authorizer } from "@/authorizer";
4241
import handleError from "@/utils/handleError";
4342
import useSnackbar from "@/helpers/snackbar";
4443
import BaseDialog from "../BaseDialog.vue";
4544
import useAuthStore from "@/store/modules/auth";
45+
import useWebEndpointsStore from "@/store/modules/web_endpoints";
4646
4747
defineOptions({
4848
inheritAttrs: false,
4949
});
5050
51-
const props = defineProps({
52-
address: {
53-
type: String,
54-
required: true,
55-
},
56-
});
51+
const props = defineProps<{ address: string }>();
5752
5853
const emit = defineEmits(["update"]);
5954
const showDialog = defineModel({ default: false });
60-
const store = useStore();
6155
const authStore = useAuthStore();
56+
const webEndpointsStore = useWebEndpointsStore();
6257
const snackbar = useSnackbar();
6358
6459
const update = () => {
@@ -73,9 +68,7 @@ const hasAuthorizationDeleteWebEndpoint = () => {
7368
7469
const remove = async () => {
7570
try {
76-
await store.dispatch("webEndpoints/delete", {
77-
address: props.address,
78-
});
71+
await webEndpointsStore.deleteWebEndpoint(props.address);
7972
update();
8073
snackbar.showSuccess("Web Endpoint deleted successfully.");
8174
} catch (error: unknown) {

ui/src/components/WebEndpoints/WebEndpointList.vue

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
v-model:page="page"
44
v-model:itemsPerPage="itemsPerPage"
55
:headers="headers"
6-
:items="items"
6+
:items="webEndpoints"
77
:totalCount="totalCount"
88
:loading="loading"
99
:itemsPerPageOptions="[10, 20, 50]"
@@ -12,7 +12,7 @@
1212
>
1313
<template #rows>
1414
<tr
15-
v-for="endpoint in items"
15+
v-for="endpoint in webEndpoints"
1616
:key="endpoint.address"
1717
:class="isExpired(endpoint.expires_in) ? 'text-warning' : ''"
1818
>
@@ -33,12 +33,12 @@
3333

3434
<td data-test="web-endpoint-url">
3535
<a
36-
:href="`${urlProtocol}//${endpoint.full_address}`"
36+
:href="`${protocol}//${endpoint.full_address}`"
3737
target="_blank"
3838
rel="noopener noreferrer"
3939
@click="handleClick"
4040
>
41-
{{ `${urlProtocol}//${endpoint.full_address}` }}
41+
{{ `${protocol}//${endpoint.full_address}` }}
4242
</a>
4343
</td>
4444

@@ -61,26 +61,27 @@
6161
import { ref, watch, computed, onMounted } from "vue";
6262
import moment from "moment";
6363
import { useRouter } from "vue-router";
64-
import { useStore } from "@/store";
6564
import DataTable from "@/components/DataTable.vue";
6665
import WebEndpointDelete from "@/components/WebEndpoints/WebEndpointDelete.vue";
6766
import DeviceIcon from "@/components/Devices/DeviceIcon.vue";
68-
import { IWebEndpoints } from "@/interfaces/IWebEndpoints";
67+
import { IWebEndpoint } from "@/interfaces/IWebEndpoints";
68+
import useWebEndpointsStore from "@/store/modules/web_endpoints";
69+
import handleError from "@/utils/handleError";
6970
7071
type SortField = "created_at" | "updated_at" | "address" | "uid";
7172
72-
const store = useStore();
73+
const webEndpointsStore = useWebEndpointsStore();
7374
const router = useRouter();
7475
75-
const items = computed<IWebEndpoints[]>(() => store.getters["webEndpoints/listWebEndpoints"]);
76-
const totalCount = computed(() => store.getters["webEndpoints/getTotalCount"]);
76+
const webEndpoints = computed<IWebEndpoint[]>(() => webEndpointsStore.webEndpoints);
77+
const totalCount = computed(() => webEndpointsStore.webEndpointCount);
7778
78-
const page = ref(store.getters["webEndpoints/getPage"]);
79-
const itemsPerPage = ref(store.getters["webEndpoints/getPerPage"]);
79+
const page = ref(1);
80+
const itemsPerPage = ref(10);
8081
const loading = ref(false);
81-
82-
const sortBy = ref<SortField>(store.getters["webEndpoints/getSortBy"]);
83-
const sortDesc = ref<boolean>(store.getters["webEndpoints/getOrderBy"] === "desc");
82+
const sortField = ref<SortField>();
83+
const sortOrder = ref<"asc" | "desc">();
84+
const { protocol } = window.location;
8485
8586
const headers = [
8687
{ text: "Device", value: "device", sortable: false },
@@ -94,50 +95,35 @@ const headers = [
9495
const fetchWebEndpoints = async () => {
9596
loading.value = true;
9697
try {
97-
await store.dispatch("webEndpoints/get", {
98-
page: page.value,
99-
perPage: itemsPerPage.value,
100-
filter: store.getters["webEndpoints/getFilter"],
101-
sortBy: sortBy.value,
102-
orderBy: sortDesc.value ? "desc" : "asc",
103-
});
104-
105-
store.commit("webEndpoints/setPagePerPage", {
98+
await webEndpointsStore.fetchWebEndpointsList({
10699
page: page.value,
107100
perPage: itemsPerPage.value,
108-
filter: store.getters["webEndpoints/getFilter"],
109-
sortBy: sortBy.value,
110-
orderBy: sortDesc.value ? "desc" : "asc",
101+
sortField: sortField.value,
102+
sortOrder: sortOrder.value,
111103
});
112-
} finally {
113-
loading.value = false;
104+
} catch (error) {
105+
handleError(error);
114106
}
115-
};
116107
117-
const sortByItem = (field: string) => {
118-
const validFields: SortField[] = ["created_at", "updated_at", "address", "uid"];
119-
if (!validFields.includes(field as SortField)) return;
108+
loading.value = false;
109+
};
120110
121-
if (sortBy.value === field) {
122-
sortDesc.value = !sortDesc.value;
123-
} else {
124-
sortBy.value = field as SortField;
125-
sortDesc.value = false;
126-
}
111+
const getSortOrder = () => sortOrder.value === "asc" ? "desc" : "asc";
127112
128-
fetchWebEndpoints();
113+
const sortByItem = async (field: SortField) => {
114+
sortField.value = field;
115+
sortOrder.value = getSortOrder();
116+
await fetchWebEndpoints();
129117
};
130118
131-
watch([page, itemsPerPage], () => {
132-
fetchWebEndpoints();
119+
watch([page, itemsPerPage], async () => {
120+
await fetchWebEndpoints();
133121
});
134122
135-
const refresh = () => {
136-
fetchWebEndpoints();
123+
const refresh = async () => {
124+
await fetchWebEndpoints();
137125
};
138126
139-
const urlProtocol = ref(window.location.protocol);
140-
141127
const isExpired = (date: string) => date !== "0001-01-01T00:00:00Z" && moment().utc().isAfter(moment(date));
142128
143129
const formatDate = (expiresIn: string) => {

ui/src/interfaces/IWebEndpoints.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IDevice } from "./IDevice";
22

3-
export interface IWebEndpoints {
3+
export interface IWebEndpoint {
44
address: string,
55
full_address: string,
66
device: IDevice
@@ -17,6 +17,10 @@ export interface IWebEndpointsCreate {
1717
ttl: number
1818
}
1919

20-
export interface IWebEndpointsDelete {
21-
address: string,
20+
export interface FetchWebEndpointsParams {
21+
perPage?: number;
22+
page?: number;
23+
filter?: string;
24+
sortField?: "created_at" | "updated_at" | "address" | "uid";
25+
sortOrder?: "asc" | "desc";
2226
}

ui/src/router/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { RouteRecordRaw, createRouter, createWebHistory, RouteLocationNormalized, NavigationGuardNext } from "vue-router";
22
import { envVariables } from "../envVariables";
3-
import { store } from "@/store";
43
import { plugin as snackbar } from "@/plugins/snackbar"; // using direct plugin because inject() doesn't work outside components
54
import useAuthStore from "@/store/modules/auth";
65
import useContainersStore from "@/store/modules/containers";
76
import useDevicesStore from "@/store/modules/devices";
87
import useLayoutStore, { Layout } from "@/store/modules/layout";
98
import useNamespacesStore from "@/store/modules/namespaces";
109
import useUsersStore from "@/store/modules/users";
10+
import useWebEndpointsStore from "@/store/modules/web_endpoints";
1111

1212
export const handleAcceptInvite = async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
1313
const namespacesStore = useNamespacesStore();
@@ -293,13 +293,7 @@ export const routes: Array<RouteRecordRaw> = [
293293
name: "WebEndpoints",
294294
component: WebEndpoints,
295295
beforeEnter: async (to, from, next) => {
296-
await store.dispatch("webEndpoints/get", {
297-
page: store.getters["webEndpoints/getPage"],
298-
perPage: store.getters["webEndpoints/getPerPage"],
299-
filter: store.getters["webEndpoints/getFilter"],
300-
sortBy: store.getters["webEndpoints/getSortBy"],
301-
orderBy: store.getters["webEndpoints/getOrderBy"],
302-
});
296+
await useWebEndpointsStore().fetchWebEndpointsList();
303297
next();
304298
},
305299
},

ui/src/store/api/web_endpoints.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import { webEndpointsApi } from "@/api/http";
22

3-
const getWebEndpoints = (
4-
filter: string | undefined,
3+
export const getWebEndpoints = (
54
page: number,
65
perPage: number,
7-
sortBy?: "created_at" | "updated_at" | "address" | "uid",
8-
orderBy?: "asc" | "desc",
6+
filter?: string,
7+
sortField?: "created_at" | "updated_at" | "address" | "uid",
8+
sortOrder?: "asc" | "desc",
99
) => webEndpointsApi.listWebEndpoints(
1010
filter,
1111
page,
1212
perPage,
13-
sortBy,
14-
orderBy,
13+
sortField,
14+
sortOrder,
1515
);
1616

17-
const createWebEndpoints = (uid: string, host: string, port: number, ttl: number) => webEndpointsApi.createWebEndpoint(
17+
export const createWebEndpoint = (uid: string, host: string, port: number, ttl: number) => webEndpointsApi.createWebEndpoint(
1818
{
1919
uid,
2020
host,
@@ -23,6 +23,4 @@ const createWebEndpoints = (uid: string, host: string, port: number, ttl: number
2323
},
2424
);
2525

26-
const deleteWebEndpoints = (address: string) => webEndpointsApi.deleteWebEndpoint(address);
27-
28-
export { getWebEndpoints, createWebEndpoints, deleteWebEndpoints };
26+
export const deleteWebEndpoint = (address: string) => webEndpointsApi.deleteWebEndpoint(address);

ui/src/store/index.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
1-
import { InjectionKey } from "vue";
2-
import { createStore, Store, useStore as vuexUseStore } from "vuex";
1+
import { createStore, useStore as vuexUseStore } from "vuex";
32

4-
import { webEndpoints, WebEndpointsState } from "./modules/web_endpoints";
3+
export const key = Symbol("store");
54

6-
export interface State {
7-
webEndpoints: WebEndpointsState;
8-
}
9-
10-
export const key: InjectionKey<Store<State>> = Symbol("store");
11-
12-
export const store = createStore<State>({
5+
export const store = createStore({
136
modules: {
14-
webEndpoints,
157
},
168
});
179

18-
export function useStore(): Store<State> {
10+
export function useStore() {
1911
return vuexUseStore(key);
2012
}

0 commit comments

Comments
 (0)