Skip to content

Commit 9ec2376

Browse files
committed
Updates to new AdminUI, in development.
1 parent 03c5826 commit 9ec2376

9 files changed

+785
-157
lines changed

src/AdminUI/package-lock.json

+146-146
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/AdminUI/src/components/AdminPage.vue

+15-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
<v-tab :value="1" @click="fetchUsersAndRoles">{{
66
$t('admin.tabs.userManagement') }}</v-tab>
77
<v-tab :value="2" @click="fetchRoles">{{ $t('admin.tabs.rolesManagement') }}</v-tab>
8-
<v-tab :value="3">{{ $t('admin.tabs.systemSettings') }}</v-tab>
9-
<v-tab :value="4">{{ $t('admin.tabs.logs') }}</v-tab>
8+
<v-tab :value="3" @click="fetchProtocolDriverInstances">{{ $t('admin.tabs.protocolDriverInstances') }}</v-tab>
9+
<v-tab :value="4">{{ $t('admin.tabs.systemSettings') }}</v-tab>
10+
<v-tab :value="5">{{ $t('admin.tabs.logs') }}</v-tab>
1011
</v-tabs>
1112

1213
<v-card-text>
@@ -20,10 +21,14 @@
2021
</v-window-item>
2122

2223
<v-window-item :value="3">
23-
<system-settings-tab ref="systemSettingsTabRef" />
24+
<protocol-driver-instances-tab ref="protocolDriverInstancesTabRef" />
2425
</v-window-item>
2526

2627
<v-window-item :value="4">
28+
<system-settings-tab ref="systemSettingsTabRef" />
29+
</v-window-item>
30+
31+
<v-window-item :value="5">
2732
<logs-tab ref="logsTab" />
2833
</v-window-item>
2934
</v-window>
@@ -36,12 +41,14 @@
3641
<script setup>
3742
import UserManagementTab from './UserManagementTab.vue';
3843
import RolesManagementTab from './RolesManagementTab.vue';
44+
import ProtocolDriverInstancesTab from './ProtocolDriverInstancesTab.vue';
3945
import SystemSettingsTab from './SystemSettingsTab.vue';
4046
import LogsTab from './LogsTab.vue';
4147
import { ref } from 'vue';
4248
4349
const userManagementTabRef = ref(null);
4450
const rolesManagementTabRef = ref(null);
51+
const protocolDriverInstancesTabRef = ref(null);
4552
const systemSettingsTabRef = ref(null);
4653
const logsTab = ref(null);
4754
@@ -57,6 +64,11 @@ const fetchRoles = () => {
5764
rolesManagementTabRef.value.fetchRoles();
5865
}
5966
67+
const fetchProtocolDriverInstances = () => {
68+
if (protocolDriverInstancesTabRef?.value?.fetchProtocolDriverInstances)
69+
protocolDriverInstancesTabRef.value.fetchProtocolDriverInstances();
70+
}
71+
6072
const activeTab = ref(1);
6173
</script>
6274

src/AdminUI/src/components/DashboardPage.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const shortcuts = ref([
3535
{ titleKey: 'dashboard.logViewer', icon: FileText, color: 'warning', route: '/log-viewer', page: '/log-io', target: "_blank" },
3636
{ titleKey: 'dashboard.grafana', icon: BarChart, color: 'secondary', route: '/grafana', page: '/grafana', target: "_blank" },
3737
{ titleKey: 'dashboard.metabase', icon: Database, color: 'primary', route: '/metabase', page: '/metabase', target: "_blank" },
38-
{ titleKey: 'dashboard.admin', icon: UserCog, color: 'primary', route: '/admin' },
38+
{ titleKey: 'dashboard.admin', icon: UserCog, color: 'primary', route: '/admin', page: '/admin', target: "_blank" },
3939
{ titleKey: 'dashboard.about', icon: HelpCircle, color: 'secondary', route: '/about' },
4040
]);
4141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<template>
2+
<v-container fluid class="pa-0 fill-height">
3+
<v-row no-gutters class="fill-height">
4+
<v-col cols="12" class="fill-height">
5+
<iframe
6+
src="/metabase"
7+
width="100%"
8+
height="100%"
9+
frameborder="0"
10+
style="position: absolute; top: 120; left: 0; width: 100%; height: 100%;"
11+
></iframe>
12+
</v-col>
13+
</v-row>
14+
</v-container>
15+
</template>
16+
17+
<script setup>
18+
// No additional setup needed for this component
19+
</script>
20+
21+
<style scoped>
22+
.fill-height {
23+
height: 100%;
24+
}
25+
</style>
26+
27+
<style>
28+
html {
29+
overflow-y: hidden
30+
}
31+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
<template>
2+
<v-container fluid class="protocol-driver-instances-tab">
3+
4+
<v-btn color="primary" class="mt-4" @click="openAddProtocolDriverInstanceDialog">{{
5+
$t('admin.protocolDriverInstances.addProtocolDriverInstance') }}
6+
<v-icon>mdi-plus</v-icon>
7+
</v-btn>
8+
<v-data-table :headers="headers" :items="driverInstances" :items-per-page="5" class="elevation-1"
9+
:load-children="fetchDriverInstances" :items-per-page-text="$t('common.itemsPerPageText')">
10+
<template #[`item.enabled`]="{ item }">
11+
<v-icon v-if="item.enabled" color="green">mdi-check</v-icon>
12+
<v-icon v-else color="red">mdi-close</v-icon>
13+
</template>
14+
<template #[`item.actions`]="{ item }">
15+
<v-icon size="small" class="me-2" @click="openEditProtocolDriverInstanceDialog(item)">
16+
mdi-pencil
17+
</v-icon>
18+
<v-icon v-if="item.name !== 'admin'" size="small" @click="openDeleteProtocolDriverInstanceDialog(item)">
19+
mdi-delete
20+
</v-icon>
21+
</template>
22+
</v-data-table>
23+
24+
</v-container>
25+
26+
</template>
27+
<script setup>
28+
import { ref, computed, onMounted, onUnmounted } from 'vue';
29+
import { useI18n } from 'vue-i18n';
30+
31+
const { t } = useI18n();
32+
const headers = computed(() => [
33+
{ title: '#', key: 'id' },
34+
{ title: t('admin.protocolDriverInstances.headers.protocolDriver'), align: 'start', key: 'protocolDriver' },
35+
{ title: t('admin.protocolDriverInstances.headers.protocolDriverInstanceNumber'), key: 'protocolDriverInstanceNumber' },
36+
{ title: t('admin.protocolDriverInstances.headers.enabled'), key: 'enabled' },
37+
{ title: t('admin.protocolDriverInstances.headers.logLevel'), key: 'logLevel' },
38+
{ title: t('admin.protocolDriverInstances.headers.allowedNodesList'), key: 'nodesText' },
39+
{ title: t('admin.protocolDriverInstances.headers.actions'), key: 'actions', sortable: false },
40+
]);
41+
42+
const dialogAddNode = ref(false);
43+
const dialogDelInst = ref(false);
44+
const active = ref([]);
45+
const newNode = ref("");
46+
const nodeNames = ref([]);
47+
const driverNameItems = [
48+
"IEC60870-5-104",
49+
"IEC60870-5-104_SERVER",
50+
"IEC60870-5-101",
51+
"IEC60870-5-101_SERVER",
52+
"IEC61850",
53+
"IEC61850_SERVER",
54+
"DNP3",
55+
"DNP3_SERVER",
56+
"MQTT-SPARKPLUG-B",
57+
"OPC-UA",
58+
"OPC-UA_SERVER",
59+
"OPC-DA",
60+
"OPC-DA_SERVER",
61+
"PLCTAG",
62+
"PLC4X",
63+
"TELEGRAF-LISTENER",
64+
"ICCP",
65+
"ICCP_SERVER",
66+
"I104M",
67+
"PI_DATA_ARCHIVE_INJECTOR",
68+
"PI_DATA_ARCHIVE_CLIENT",
69+
];
70+
const driverInstances = ref([]);
71+
72+
const selected = computed(() => {
73+
if (!active.value.length) return undefined;
74+
return driverInstances.value.find((item) => item.id === active.value[0]);
75+
});
76+
77+
onMounted(() => {
78+
fetchDriverInstances();
79+
document.documentElement.style.overflowY = 'scroll';
80+
});
81+
82+
onUnmounted(() => {
83+
document.documentElement.style.overflowY = 'auto';
84+
});
85+
86+
async function fetchNodes() {
87+
try {
88+
const res = await fetch("/Invoke/auth/listNodes");
89+
const json = await res.json();
90+
nodeNames.value = json;
91+
} catch (err) {
92+
console.warn(err);
93+
}
94+
}
95+
96+
async function fetchDriverInstances() {
97+
await fetchNodes();
98+
try {
99+
const res = await fetch("/Invoke/auth/listProtocolDriverInstances");
100+
const json = await res.json();
101+
json.forEach((item, index) => {
102+
item.id = index + 1;
103+
item.nodesText = item.nodeNames.join(', ');
104+
item.driverNameInstance = `${item.protocolDriver} ( ${item.protocolDriverInstanceNumber} )`;
105+
});
106+
driverInstances.value = json;
107+
} catch (err) {
108+
console.warn(err);
109+
}
110+
}
111+
112+
async function deleteDriverInstance() {
113+
try {
114+
const res = await fetch("/Invoke/auth/deleteProtocolDriverInstance", {
115+
method: "post",
116+
headers: {
117+
Accept: "application/json",
118+
"Content-Type": "application/json",
119+
},
120+
body: JSON.stringify({
121+
protocolDriver: selected.value.protocolDriver,
122+
protocolDriverInstanceNumber: selected.value.protocolDriverInstanceNumber,
123+
_id: selected.value._id,
124+
}),
125+
});
126+
const json = await res.json();
127+
if (json.error) console.log(json);
128+
fetchDriverInstances(); // refreshes instances
129+
} catch (err) {
130+
console.warn(err);
131+
}
132+
}
133+
134+
async function createDriverInstance() {
135+
try {
136+
const res = await fetch("/Invoke/auth/createProtocolDriverInstance", {
137+
method: "post",
138+
headers: {
139+
Accept: "application/json",
140+
"Content-Type": "application/json",
141+
},
142+
body: JSON.stringify({
143+
protocolDriver: "IEC60870-5-104", // Default value, can be changed later
144+
protocolDriverInstanceNumber: 1, // Default value, can be changed later
145+
enabled: true,
146+
logLevel: "INFO",
147+
nodeNames: [],
148+
}),
149+
});
150+
const json = await res.json();
151+
if (json.error) console.log(json);
152+
fetchDriverInstances(); // Refresh instances
153+
} catch (err) {
154+
console.warn(err);
155+
}
156+
}
157+
158+
async function updateProtocolDriverInstance() {
159+
if (!selected.value) return;
160+
try {
161+
const res = await fetch("/Invoke/auth/updateProtocolDriverInstance", {
162+
method: "post",
163+
headers: {
164+
Accept: "application/json",
165+
"Content-Type": "application/json",
166+
},
167+
body: JSON.stringify(selected.value),
168+
});
169+
const json = await res.json();
170+
if (json.error) console.log(json);
171+
fetchDriverInstances(); // Refresh instances
172+
} catch (err) {
173+
console.warn(err);
174+
}
175+
}
176+
177+
async function addNewNode() {
178+
if (!selected.value || !newNode.value) return;
179+
try {
180+
const updatedInstance = {
181+
...selected.value,
182+
nodeNames: [...selected.value.nodeNames, newNode.value],
183+
};
184+
const res = await fetch("/Invoke/auth/updateProtocolDriverInstance", {
185+
method: "post",
186+
headers: {
187+
Accept: "application/json",
188+
"Content-Type": "application/json",
189+
},
190+
body: JSON.stringify(updatedInstance),
191+
});
192+
const json = await res.json();
193+
if (json.error) console.log(json);
194+
newNode.value = ""; // Clear the input
195+
fetchDriverInstances(); // Refresh instances
196+
} catch (err) {
197+
console.warn(err);
198+
}
199+
}
200+
201+
const openAddProtocolDriverInstanceDialog = () => {
202+
dialogAddNode.value = true;
203+
};
204+
205+
const openEditProtocolDriverInstanceDialog = (item) => {
206+
selected.value = item;
207+
dialogAddNode.value = true;
208+
};
209+
210+
</script>

0 commit comments

Comments
 (0)