mirror of
https://github.com/sergeyyarkov/modbus-logger.git
synced 2026-10-11 02:18:51 +03:00
progres
This commit is contained in:
parent
d934e93d93
commit
8a0cfe5063
@ -587,6 +587,21 @@ video {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.modal {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 5rem;
|
||||
max-width: 28rem;
|
||||
border-radius: 0.125rem;
|
||||
border-width: 1px;
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-top: 0.875rem;
|
||||
padding-bottom: 0.875rem;
|
||||
}
|
||||
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
@ -622,6 +637,15 @@ video {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
@ -682,16 +706,8 @@ video {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mb-2 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mb-10 {
|
||||
margin-bottom: 2.5rem;
|
||||
.mt-auto {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.block {
|
||||
@ -706,6 +722,10 @@ video {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
@ -746,10 +766,31 @@ video {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-16 {
|
||||
width: 4rem;
|
||||
}
|
||||
|
||||
.w-24 {
|
||||
width: 6rem;
|
||||
}
|
||||
|
||||
.w-52 {
|
||||
width: 13rem;
|
||||
}
|
||||
|
||||
.min-w-\[256px\] {
|
||||
min-width: 256px;
|
||||
}
|
||||
|
||||
.min-w-fit {
|
||||
min-width: -moz-fit-content;
|
||||
min-width: fit-content;
|
||||
}
|
||||
|
||||
.min-w-\[100px\] {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.max-w-2xl {
|
||||
max-width: 42rem;
|
||||
}
|
||||
@ -770,6 +811,10 @@ video {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.basis-52 {
|
||||
flex-basis: 13rem;
|
||||
}
|
||||
|
||||
.transform {
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
@ -798,6 +843,42 @@ video {
|
||||
resize: both;
|
||||
}
|
||||
|
||||
.grid-flow-col {
|
||||
grid-auto-flow: column;
|
||||
}
|
||||
|
||||
.grid-cols-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-cols-\[200px_minmax\(900px\2c _1fr\)_100px\] {
|
||||
grid-template-columns: 200px minmax(900px, 1fr) 100px;
|
||||
}
|
||||
|
||||
.grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-rows-3 {
|
||||
grid-template-rows: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-rows-4 {
|
||||
grid-template-rows: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-rows-1 {
|
||||
grid-template-rows: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.grid-rows-2 {
|
||||
grid-template-rows: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
@ -806,6 +887,10 @@ video {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
}
|
||||
@ -814,10 +899,22 @@ video {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.gap-3 {
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.gap-2 {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
|
||||
--tw-space-x-reverse: 0;
|
||||
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
|
||||
@ -842,6 +939,10 @@ video {
|
||||
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
|
||||
}
|
||||
|
||||
.overflow-auto {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.overflow-y-auto {
|
||||
overflow-y: auto;
|
||||
}
|
||||
@ -850,6 +951,10 @@ video {
|
||||
border-radius: 0.125rem;
|
||||
}
|
||||
|
||||
.rounded-md {
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-width: 1px;
|
||||
}
|
||||
@ -858,6 +963,10 @@ video {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.border-4 {
|
||||
border-width: 4px;
|
||||
}
|
||||
|
||||
.border-b {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
@ -962,6 +1071,10 @@ video {
|
||||
padding-top: 1.25rem;
|
||||
}
|
||||
|
||||
.pl-0 {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
@ -1000,6 +1113,11 @@ video {
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.text-5xl {
|
||||
font-size: 3rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
@ -1133,4 +1251,34 @@ button:disabled {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(75 85 99 / var(--tw-text-opacity));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.sm\:grid-cols-1 {
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@media (min-width: 640px) {
|
||||
.md\:sm\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.md\:grid-cols-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
@media (min-width: 640px) {
|
||||
.lg\:sm\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.lg\:grid-cols-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,10 @@
|
||||
.btn {
|
||||
@apply bg-blue-600 text-white py-2 px-3.5 font-semibold text-sm rounded-sm hover:opacity-70;
|
||||
}
|
||||
|
||||
.modal {
|
||||
@apply px-4 py-3.5 ml-auto mr-auto mt-20 bg-white max-w-md border rounded-sm;
|
||||
}
|
||||
}
|
||||
|
||||
[x-cloak] {
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
</div> -->
|
||||
|
||||
<!-- Root element -->
|
||||
<div x-data x-cloak class="mx-auto max-w-5xl flex flex-col h-screen">
|
||||
<div x-data class="mx-auto max-w-5xl flex flex-col h-screen px-2.5">
|
||||
<!-- Monitoring content -->
|
||||
<template x-if="$store.app.currentPage === 'monitoring'">
|
||||
<main x-data="monitoringPage" class="flex-1" x-bind:hidden="$store.app.currentPage !== 'monitoring'">
|
||||
@ -25,7 +25,7 @@
|
||||
<button @click="$store.app.setPage('configuration')" class="btn">Config</button>
|
||||
</div> -->
|
||||
<div class="flex h-full flex-row space-x-2">
|
||||
<div class="border-r min-w-[256px] p-2.5 pt-5">
|
||||
<div class="border-r min-w-[256px] p-2.5 pl-0 pt-5">
|
||||
<div class="flex flex-col space-y-1">
|
||||
<!-- List of devices -->
|
||||
<span class="italic text-sm">Devices:</span>
|
||||
@ -33,7 +33,7 @@
|
||||
No devices added.
|
||||
</span>
|
||||
<div class="flex flex-col max-h-[400px] overflow-y-auto space-y-2">
|
||||
<template x-for="device in devices">
|
||||
<template x-for="device in devices" :key="device.id">
|
||||
<button
|
||||
@click="selectDevice(device)"
|
||||
:class="{'bg-gray-100': selectedDevice?.id === device.id}"
|
||||
@ -43,8 +43,50 @@
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
<!-- Add new device -->
|
||||
<button class="w-full min-h-[50px] text-center border-2 border-dashed rounded-sm hover:bg-gray-50 cursor-pointer">
|
||||
<!-- MODAL: Create new device -->
|
||||
<template x-teleport="body">
|
||||
<div
|
||||
@keyup.escape.window="modalCreateDevice.close()"
|
||||
class="backdrop z-50"
|
||||
x-show="modalCreateDevice.isOpen"
|
||||
>
|
||||
<div class="modal">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="font-bold text-lg">Create new device</h2>
|
||||
<button @click="modalCreateDevice.close()" aria-label="Close">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<form x-on:submit.prevent="createDevice" action="#">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<div>
|
||||
<label for="id">Slave Id</label>
|
||||
<input placeholder="1" id="id" name="id" type="number" min="1" max="255" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="id">Name</label>
|
||||
<input placeholder="Device #1" id="id" name="id" type="text" required />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div class="mt-4 flex justify-end space-x-2">
|
||||
<button class="btn">Create</button>
|
||||
<button @click="modalCreateDevice.close()" class="btn bg-red-500">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Create new device -->
|
||||
<button
|
||||
@click="modalCreateDevice.open()"
|
||||
class="w-full min-h-[50px] text-center border-2 border-dashed rounded-sm hover:bg-gray-50 cursor-pointer"
|
||||
>
|
||||
<span class="text-4xl text-gray-400 select-none">+</span>
|
||||
</button>
|
||||
</div>
|
||||
@ -64,30 +106,73 @@
|
||||
</div>
|
||||
<div class="flex-auto pl-2">
|
||||
<div x-show="selectedDevice != null">
|
||||
<div class="pt-5 border-b flex justify-between items-center pr-6">
|
||||
<h2 x-text="selectedDevice?.name" class="font-bold text-3xl my-2"></h2>
|
||||
<div class="pt-5 border-b flex flex-wrap justify-between items-center pr-6">
|
||||
<h2 x-text="selectedDevice?.name" class="font-bold text-3xl my-2 mr-2"></h2>
|
||||
<!-- Device create, delete -->
|
||||
<div>
|
||||
<a href="#">Edit</a>
|
||||
<a @click="$dispatch('dialog', {
|
||||
<a
|
||||
@click="$dispatch('dialog', {
|
||||
title: 'Remove device',
|
||||
body: `Remove ${selectedDevice.name}? All logs will be cleared.`,
|
||||
isOpen: true,
|
||||
onConfirm: async () => await removeDevice(selectedDevice.id)
|
||||
});"
|
||||
class="text-red-500"
|
||||
href="#">
|
||||
href="#"
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Display Graph -->
|
||||
<div class="mt-4">
|
||||
<div id="div_g" x-ref="graph" style="width: 720px; height: 300px"></div>
|
||||
<div x-show="selectedDevice?.g_display_reg_addr != null" class="mt-4">
|
||||
<div id="div_g" style="width: 720px; height: 300px"></div>
|
||||
<hr class="my-3" />
|
||||
</div>
|
||||
<hr class="mt-10" />
|
||||
<!-- Display values -->
|
||||
<h2 class="text-xl font-bold mb-3">Display values</h2>
|
||||
<h2 class="text-xl font-bold mb-3 mt-5">Display values</h2>
|
||||
<div>
|
||||
<div class="grid grid-cols-1 md:sm:grid-cols-2 lg:sm:grid-cols-3 gap-2">
|
||||
<template x-for="value in [1,2,3,4,5]">
|
||||
<div class="border-2 p-2.5 rounded-sm">
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-xl font-bold">CV1</h3>
|
||||
<div>
|
||||
<button>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="#eab308"
|
||||
d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a.996.996 0 0 0 0-1.41l-2.34-2.34a.996.996 0 0 0-1.41 0l-1.83 1.83l3.75 3.75l1.83-1.83z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="#ef4444"
|
||||
d="m9.4 16.5l2.6-2.6l2.6 2.6l1.4-1.4l-2.6-2.6L16 9.9l-1.4-1.4l-2.6 2.6l-2.6-2.6L8 9.9l2.6 2.6L8 15.1l1.4 1.4ZM5 21V6H4V4h5V3h6v1h5v2h-1v15H5Zm2-2h10V6H7v13ZM7 6v13V6Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="flex flex-col">
|
||||
<span>Value: 7892</span>
|
||||
<span>Reg Addr: 6</span>
|
||||
<span>Format: 32</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="flex justify-center items-center border-4 text-center border-dashed p-2.5 rounded-sm cursor-pointer hover:bg-gray-50"
|
||||
>
|
||||
<span class="text-5xl text-gray-400 font-bold select-none">+</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="select-none" x-show="selectedDevice === null">
|
||||
<span class="block text-center italic text-gray-500 mt-10">No device selected.</span>
|
||||
@ -307,30 +392,34 @@
|
||||
|
||||
<!-- Dialog -->
|
||||
<div x-data="dialog">
|
||||
<template @dialog.window="open($event)" x-if="isOpen">
|
||||
<template @dialog.window="open($event)" @keyup.escape.window="close()" x-if="isOpen">
|
||||
<div class="backdrop z-50">
|
||||
<div
|
||||
role="dialog"
|
||||
class="px-4 py-3.5 ml-auto mr-auto mt-20 bg-white max-w-md border rounded-sm"
|
||||
>
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 x-text="labels.title" class="font-bold text-lg"></h2>
|
||||
<button @click="close()" aria-label="Close">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<p x-text="labels.body"></p>
|
||||
<div class="mt-4 flex justify-end space-x-2">
|
||||
<button x-bind:disabled="isLoading" x-bind:class="isLoading && 'opacity-70'" @click="confirm()" class="btn">Confirm</button>
|
||||
<button @click="close()" class="btn bg-red-500">Cancel</button>
|
||||
</div>
|
||||
<div role="dialog" class="modal">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 x-text="labels.title" class="font-bold text-lg"></h2>
|
||||
<button @click="close()" aria-label="Close">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<p x-text="labels.body"></p>
|
||||
<div class="mt-4 flex justify-end space-x-2">
|
||||
<button
|
||||
x-bind:disabled="isLoading"
|
||||
x-bind:class="isLoading && 'opacity-70'"
|
||||
@click="confirm()"
|
||||
class="btn"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
<button @click="close()" class="btn bg-red-500">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
@ -60,6 +60,15 @@ document.addEventListener("alpine:init", async () => {
|
||||
Alpine.data("monitoringPage", () => ({
|
||||
selectedDevice: null,
|
||||
devices: [],
|
||||
modalCreateDevice: {
|
||||
isOpen: false,
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
},
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
},
|
||||
},
|
||||
async init() {
|
||||
try {
|
||||
this.$store.app.currentPage = "loading";
|
||||
@ -88,7 +97,7 @@ document.addEventListener("alpine:init", async () => {
|
||||
},
|
||||
async removeDevice(id) {
|
||||
try {
|
||||
await api.post('/modbus/remove_device', { id });
|
||||
await api.post("/modbus/remove_device", { id });
|
||||
this.devices = this.devices.filter((d) => d.id != id);
|
||||
this.selectedDevice = null;
|
||||
} catch (error) {
|
||||
@ -135,16 +144,15 @@ document.addEventListener("alpine:init", async () => {
|
||||
title: "Confirm dialog",
|
||||
body: "Are you sure you want to perform this action?",
|
||||
},
|
||||
cb: null,
|
||||
onConfirm: () => undefined,
|
||||
onError: () => undefined,
|
||||
confirm() {
|
||||
if (this.onConfirm.constructor.name === 'AsyncFunction') {
|
||||
if (this.onConfirm.constructor.name === "AsyncFunction") {
|
||||
this.isLoading = true;
|
||||
this.onConfirm()
|
||||
.then(() => this.close())
|
||||
.catch((e) => this.onError(e))
|
||||
.finally(() => this.isLoading = false)
|
||||
.finally(() => (this.isLoading = false));
|
||||
} else {
|
||||
this.onConfirm();
|
||||
this.close();
|
||||
@ -152,13 +160,17 @@ document.addEventListener("alpine:init", async () => {
|
||||
},
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
this.cb = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update dialog state on event
|
||||
* @param {CustomEvent} event
|
||||
*/
|
||||
open(event) {
|
||||
const { detail } = event;
|
||||
this.isOpen = detail.isOpen;
|
||||
this.onConfirm = detail.onConfirm;
|
||||
this.labels = { title: detail.title, body: detail.body };
|
||||
}
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user