init report && move live_monitor_vue

This commit is contained in:
vbuglov committed 2024-03-22 13:27:00 +03:00
1 parent b215f88b2c
commit 4e123e6555
255 files changed
+79351

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
<script>
import {mapGetters, mapMutations} from 'vuex'
import MenuContainer from "@frames/Menu/MenuContainer.vue"
export default {
name: 'App',
components: {
MenuContainer
},
data() {
return {}
},
computed: {
...mapGetters('auth', ['inited']),
pageName() {
return this.$route.name
},
outerPages () {
return ['auth', '404']
},
innerPage () {
return !this.outerPages.includes(this.pageName) && this.inited
},
outerPage () {
return this.outerPages.includes(this.pageName) && this.inited
}
},
mounted() {
this.initAuth(localStorage.getItem('token'))
},
methods: {
...mapMutations('auth', ['initAuth'])
}
}
</script>
<template>
<div
v-if="innerPage"
class="flex min-h-[100vh]"
>
<MenuContainer>
<router-view />
</MenuContainer>
</div>
<div
v-if="outerPage"
class="relative"
>
<router-view />
</div>
</template>
Whitespace-only changes.
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

@@ -0,0 +1,53 @@
<!-- eslint-disable -->
<script>
import Modal from '@molecules/Modal/index.vue'
export default {
name: 'Accordion',
components: {
Modal,
},
props: {
isOpen: {
type: Boolean,
default: false
},
id: {
type: String,
default: ""
},
},
setup(props, {slots}) {
const hasHeader = slots.header != undefined
const hasBody = slots.body != undefined
return {
hasHeader,
hasBody
}
},
computed: {
},
methods: {
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="relative w-full">
<input type="checkbox" :id="id" value="" :checked="isOpen" class="hidden peer" required="">
<label :for="id" class="inline-flex items-center justify-between w-full px-5 h-[50px] text-gray-600 bg-white border border-gray-200 rounded-lg cursor-pointer dark:hover:text-gray-300 dark:border-gray-700 peer-checked:border-slate-300 peer-checked:rounded-b-none peer-checked:border-slate-200 hover:text-gray-600 dark:peer-checked:text-gray-300 peer-checked:text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:bg-gray-800 transition-all">
<div v-if="hasHeader"><slot name="header" /></div>
</label>
<i class="ri-arrow-down-s-line absolute right-[10px] top-[15px] peer-checked:rotate-180 transition-all pointer-events-none"></i>
<div v-if="hasBody" class="hidden peer-checked:block transition-all w-full px-5 py-4 text-gray-600 bg-white border border-gray-200 rounded-b-lg cursor-default">
<slot name="body" />
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,53 @@
<!-- eslint-disable -->
<template>
<button @click="setSelected()" :type="type" :class="classBtn" class="w-full relative col-span-4 md:col-span-2 items-center justify-center text-center border-solid border-blue-1 z-10 cursor-pointer">
<i v-if="classIcon" :class="classIcon" class="text-blue-1"></i>
{{ title }}
</button>
</template>
<script>
export default {
components: {
},
props: {
onClick: {
type: Function,
default: () => {
},
},
selected: {
type: String,
default: "",
},
type: {
type: String,
default: "",
},
minWidth: {
type: String,
default: "80px",
},
title: {
type: String,
default: "",
},
classBtn: {
type: Array,
default: () => [],
},
classIcon: {
type: String,
default: "",
}
},
methods: {
setSelected: function() {
this.onClick(this.selected)
}
}
}
</script>
@@ -0,0 +1,59 @@
<template>
<fwb-button
color="dark"
:onclick="onClick"
outline
square
>
<svg
class="feather feather-x-square"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<rect
height="18"
rx="2"
ry="2"
width="18"
x="3"
y="3"
/>
<line
x1="9"
x2="15"
y1="9"
y2="15"
/>
<line
x1="15"
x2="9"
y1="9"
y2="15"
/>
</svg>
</fwb-button>
</template>
<script>
import {FwbButton} from "flowbite-vue";
export default {
components: {
FwbButton
},
props: {
onClick: {
type: Function,
default: () => {
},
}
}
}
</script>
@@ -0,0 +1,41 @@
<template>
<fwb-button
color="dark"
:onclick="onClick"
outline
square
>
<svg
class="feather feather-edit"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
</svg>
</fwb-button>
</template>
<script>
import {FwbButton} from "flowbite-vue";
export default {
components: {
FwbButton
},
props: {
onClick: {
type: Function,
default: () => {
},
}
}
}
</script>
@@ -0,0 +1,42 @@
<template>
<fwb-button
color="dark"
:onclick="onClick"
outline
square
>
<svg
class="feather feather-save"
fill="none"
height="24"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
<polyline points="17 21 17 13 7 13 7 21" />
<polyline points="7 3 7 8 15 8" />
</svg>
</fwb-button>
</template>
<script>
import {FwbButton} from "flowbite-vue";
export default {
components: {
FwbButton
},
props: {
onClick: {
type: Function,
default: () => {
},
}
}
}
</script>
@@ -0,0 +1,87 @@
<!-- eslint-disable -->
<script>
// import Button from "@/components/1_atoms/Button.vue";
export default {
name: 'DoubleSwitch',
components: {
// Button,
},
props: {
machine: {
type: Object,
default: () => ({})
},
firstTitle: {
type: String,
default: ''
},
secondTitle: {
type: String,
default: ''
},
firstColor: {
type: String,
default: ''
},
secondColor: {
type: String,
default: ''
},
},
data() {
return {
// isChecked: false
}
},
computed: {
},
mounted () {
},
inject: ['isChecked'],
// watch: {
// isChecked: {
// handler(newValue, oldValue) {
// this.$emit('switched', newValue)
// },
// deep: true
// },
// },
emits: ['switched'],
methods: {
buttonClass: function(value) {
return `border border-slate-400 flex items-center justify-center cursor-pointer rounded transition-all text-xs text-center py-[5px] px-2 ${value}`
},
setChecked(e) {
// this.isChecked = e.target.checked
this.$emit('switched', e.target.checked)
}
}
}
</script>
<!-- eslint-disable -->
<template>
<label class="relative inline-flex items-center cursor-pointer">
<input
v-bind="$attrs"
class="input sr-only peer/checkbox"
type="checkbox"
:checked="isChecked"
@change="setChecked"
/>
<div :class="`w-11 h-6 rounded-full peer peer-focus:ring-4 peer-focus:ring-${secondColor}-300 dark:peer-focus:ring-${secondColor}-800 dark:bg-${firstColor}-700 peer-checked/checkbox:after:translate-x-full peer-checked/checkbox:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:border-${firstColor}-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-${firstColor}-600`" :style="{background: !isChecked ? firstColor : secondColor}"></div>
<span class="block peer-checked/checkbox:hidden ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"> {{ firstTitle }}</span>
<span class="hidden peer-checked/checkbox:block ml-2 text-sm font-medium text-gray-900 dark:text-gray-300"> {{ secondTitle }} </span>
</label>
</template>
<style scoped>
</style>
@@ -0,0 +1,109 @@
<template>
<div class="flex border-8 w-1/2 flex-col py-1 gap-y-5">
<div class="flex flex-row gap-y-5 w-full justify-between">
<fwb-p
v-if="!isEdit"
class="self-center px-5"
>
{{ label }}
</fwb-p>
<div
class="flex flex-row gap-x-5 justify-between"
:class="{'w-full': isEdit}"
>
<fwb-input
v-if="isEdit"
class="w-full"
:model-value="realLink"
@update:model-value="args => {tempLink = args}"
/>
<ButtonEdit
v-if="!isEdit"
:on-click="() => {isEdit = true}"
/>
<ButtonSave
v-if="isEdit"
:on-click="() => {clickSave()}"
/>
<ButtonDiscard
v-if="isEdit"
:on-click="() => {clickDiscard()}"
/>
</div>
</div>
<YouTube
v-if="isLinkNormal && isReady"
ref="youtube"
:src="realLink"
width="100%"
/>
</div>
</template>
<script>
import ButtonSave from "@atoms/ButtonSave.vue";
import YouTube from "vue3-youtube";
import ButtonEdit from "@atoms/ButtonEdit.vue";
import ButtonDiscard from "@atoms/ButtonDiscard.vue";
import {FwbInput, FwbP} from "flowbite-vue";
export default {
components: {
FwbInput,
FwbP,
YouTube, ButtonDiscard, ButtonSave, ButtonEdit
},
props: {
videoLink: {
type: String,
default: '',
},
label: {
type: String,
default: ''
},
timeout: {
type: Number,
default: 0
},
},
data() {
return {
isEdit: false,
isReady: false,
tempLink: this.$props.videoLink,
realLink: this.$props.videoLink,
}
},
computed: {
isLinkNormal() {
return this.$data.realLink.toString().includes("https://www.youtube.com/watch?")
}
},
mounted() {
if (this.timeout) {
setTimeout(() => {
this.isReady = true
}, this.timeout);
} else {
this.isReady = true
}
},
methods: {
updateLink(newLink) {
console.log(newLink)
},
clickSave() {
this.$data.isEdit = false
let l = this.$data.tempLink
if (l.toString().includes("https://www.youtube.com/watch?")) {
this.$data.realLink = l
}
},
clickDiscard() {
this.$data.isEdit = false
console.log(`discard ${this.$data.tempLink}`)
}
}
}
</script>
@@ -0,0 +1,75 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import Modal from '../Modal/index.vue'
export default {
name: 'ButtonModal',
components: {Modal},
props: {
text: {
default: "",
type: String
},
"btnClass": {
default: "",
type: String,
},
containerClass: {
default: "",
type: String,
},
forcedBtnClass: {
default: "",
type: String
},
onToggle: {
default: () => null,
type: Function
}
},
data() {
return {
open: false,
}
},
computed: {
__btnClass__() {
if (this.forcedBtnClass) return this.forcedBtnClass
return `${this.btnClass} cursor-pointer w-full bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up`
}
},
methods: {
toggle () {
this.open = !this.open
this.onToggle({isOpen: this.open})
}
}
}
</script>
<template>
<div
v-if="text"
:class="__btnClass__"
@click="toggle"
>
{{ text }}
</div>
<div
v-if="!text"
:class="__btnClass__"
@click="toggle"
>
<slot name="button" />
</div>
<Modal
v-if="open"
:close="toggle"
:isOpen="true"
:containerClass="containerClass"
>
<slot />
</Modal>
</template>
@@ -0,0 +1,112 @@
<!-- eslint-disable -->
<script>
import { toRefs } from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex";
import Button from "@/components/1_atoms/Button.vue";
import ECharts from '@store/hooks/Echarts';
export default {
name: 'Chart',
components: {
Button,
},
props: {
data: {
type: Object,
default: () => {}
},
sizes: {
type: Object,
default: () => {}
},
id: {
type: String,
required: true,
default: ''
},
type: {
type: String,
required: true,
default: ''
},
height: {
type: String,
default: ''
},
maxWidth: {
type: String,
default: ''
},
maxHeight: {
type: String,
default: ''
},
},
data() {
return {
chartStyles: {
maxWidth: `${this.maxWidth}`,
maxHeight: `${this.maxHeight}`,
top: '15px;',
padding: '10px',
margin: '10px',
boxShadow: 'rgb(0 0 0 / 12%) 2px 3px 7px',
border: '1px solid rgba(0, 0, 0, 0.15)',
}
}
},
setup(props) {
},
watch: {
},
computed: {
// ...mapGetters('main', ['activeChartData', 'prevGroupByData', 'activeMenuChartData']),
},
mounted () {
this.$nextTick(function () {
const chart = new ECharts(this.id, this.sizes)
this.setTypeFunc(this.type, chart, this.data)
chart.onClickedBarCharts(this.updateCharts)
})
},
updated () {
this.$nextTick(function () {
const chart = new ECharts(this.id, this.sizes)
this.setTypeFunc(this.type, chart, this.data)
chart.onClickedBarCharts(this.updateCharts)
})
},
methods: {
...mapActions('machines', ['updateCharts']),
buttonClass: function(value) {
return `border border-slate-400 flex items-center justify-center cursor-pointer rounded transition-all text-xs text-center py-[5px] px-2 ${value}`
},
setTypeFunc: function(type, chart, data) {
switch(type) {
case 'pie': return chart.makePieCharts({...data, type: type})
case 'mount': return chart.makeBarMountCharts({...data, type: type, height: this.height})
case 'mainMl': return chart.makeBarCharts({...data, type: type})
case 'default': return chart.makeBarTypesCharts({...data, type: type})
case 'types': return chart.makeBarTypesCharts({...data, type: type})
case 'without_first_layout': return chart.makeBarTypesCharts({...data, type: type})
case 'ml': return chart.makeBarTypesCharts({...data, type: type})
case 'packLines': return chart.makeLinesPackCharts({...data, type: type})
}
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div :id="id" class="flex grow justify-center relative overflow-hidden bg-white" :style="chartStyles">
</div>
<div id="button_close" class="flex absolute h-max top-[50px] right-[45px]">
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,72 @@
<template>
<input
ref="datepicker"
type="text"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 flatpickr-input"
>
</template>
<script>
import flatpickr from "flatpickr";
import { Russian } from "flatpickr/dist/l10n/ru";
export default {
name: 'Datepicker',
components: {},
inject: ['defaultDate'],
props: {
enableTime: {
type: Boolean,
default: false
},
dateFormat: {
type: String,
default: null
},
onchange: {
type: Function,
default: () => null
},
},
data() {
return {
datepicker: null,
value: this.defaultDate
}
},
computed: {
_dateFormat () {
if (this.dateFormat) return this.dateFormat
if (this.enableTime) return "d.m.Y H:i"
return "d.m.Y"
}
},
mounted () {
this.datepicker = flatpickr(this.$refs.datepicker, {
allowInput: true,
enableTime: this.enableTime,
dateFormat: this._dateFormat,
locale: Russian,
onChange: (a,_b,_c) => {
this.onchange(a[0])
}
});
this.datepicker.setDate(this.defaultDate)
},
updated() {
this.$nextTick(function () {
this.datepicker = flatpickr(this.$refs.datepicker, {
allowInput: true,
enableTime: this.enableTime,
dateFormat: this._dateFormat,
locale: Russian,
});
this.datepicker.setDate(this.value)
})
},
methods: {
}
}
</script>
@@ -0,0 +1,300 @@
import Map from 'ol/Map.js';
import OSM from 'ol/source/OSM.js';
import TileLayer from 'ol/layer/Tile.js';
import View from 'ol/View.js';
import { transform, fromLonLat, toLonLat } from "ol/proj";
import Point from "ol/geom/Point";
import {Fill, Stroke, Style, Text, Icon} from 'ol/style.js';
import { Vector as VectorLayer } from "ol/layer";
import { Vector as VectorSource } from "ol/source";
import Feature from "ol/Feature";
import Overlay from "ol/Overlay";
import {toStringHDMS} from 'ol/coordinate.js';
const svg = (color = "black", course) => {
const screen_width = document.documentElement.clientWidth;
let svg_width = 25;
let svg_height = 45;
let text_size = "30px";
if (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
)
) {
svg_width = screen_width < 700 ? 30 : 50;
}
return `
<svg xmlns="http://www.w3.org/2000/svg" fill="rgb(59 130 246)" width="${svg_width}" height="${svg_height}" viewBox="0 0 24 24" ><path fill="none" d="M0 0h24v24H0z"/><path d="M18.364 17.364L12 23.728l-6.364-6.364a9 9 0 1 1 12.728 0zM12 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0-2a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"/></svg>
`
};
const createMap = (elId, zoom, coordinates = [37.619829, 55.752603]) => {
return new Map(
{
target: elId,
layers: [
new TileLayer({
source: new OSM(),
}),
],
view: new View({
projection: "EPSG:3857",
center: fromLonLat(coordinates),
zoom: zoom,
}),
}
)
}
const mapFeature = (coord) => {
return new Feature({
geometry: new Point(coord).transform("EPSG:4326", "EPSG:900913"),
projection: "EPSG:4326",
});
}
const iconFeature = (point) => {
return new Feature({
geometry: new Point(point).transform("EPSG:4326", "EPSG:900913"),
projection: "EPSG:4326",
});
};
const iconStyle = (color, text, course = null) =>
new Style({
image: new Icon({
scale: 1.4,
anchor: [0.45, 32],
anchorXUnits: "fraction",
anchorYUnits: "pixels",
src: "data:image/svg+xml;utf8," + svg(color, course),
}),
text: new Text({
text: text,
offsetY: -14,
offsetX: -18,
textBaseline: 'middle',
backgroundFill: new Fill({color: [90, 90, 60, 0.8]}),
backgroundStroke: new Stroke({color: [25, 230, 115, 0]}),
padding: [3, 3, 3, 3],
textAlign: 'right',
justify: 'left',
font: '12px bold Calibri,sans-serif',
fill: new Fill({
color: 'white',
}),
}),
});
const labelStyle = (text) => new Style({
text: new Text({
text: text,
font: '25px Calibri,sans-serif',
fill: new Fill({
color: '#fff',
}),
}),
});
const addLayerIcon = (map, coordinates, text) => {
return map.addLayer(
new VectorLayer({
type: "vector_layer",
source: new VectorSource({
features: [iconFeature(coordinates)],
}),
style: [
iconStyle("green", text),
],
})
)
}
const addLayer = (map, coordinates, text) => {
return map.addLayer(
new VectorLayer({
type: "vector_layer",
source: new VectorSource({
features: [mapFeature(coordinates), iconFeature(coordinates)],
}),
style: [
iconStyle("green", text),
],
})
)
}
const setView = (map, zoom, coordinates) => {
return map.setView(new View({
projection: "EPSG:3857",
center: fromLonLat(coordinates),
zoom: zoom,
}))
}
const addLayerWithEmptyCoords = (map, labelText) => {
return map.addLayer(
new VectorLayer({
type: "vector_layer",
background: 'rgba(0,0,0,0.7)',
source: new VectorSource({
features: [mapFeature([37.619829, 55.752603])]
}),
style: [labelStyle(labelText)]
})
)
}
const removeLayers = (map) => {
return map.getLayers().forEach(layer => {
if(layer?.values_?.type == "vector_layer") {
map.removeLayer(layer)
}
})
}
const setTooltip = (map, coordinates, machine, tooltip_id, content_id, closer_id) => {
const tooltipSelector = document.getElementById(tooltip_id)
const contentSelector = document.getElementById(content_id)
const onClose = document.getElementById(closer_id)
const tooltip = new Overlay({
element: tooltipSelector,
})
map.addOverlay(tooltip)
const tooltipContent = new Overlay({
position: coordinates,
positioning: 'center-center',
element: contentSelector,
stopEvent: false,
});
map.addOverlay(tooltipContent)
const onClosed = () => {
if(onClose) {
tooltip.setPosition(undefined)
onClose.blur()
const tooltipText = tooltipSelector.querySelector(`.${content_id}`)
if(tooltipText) {
tooltipText.remove()
}
}
}
map.on('click', function (e) {
const feature = map.forEachFeatureAtPixel(e.pixel, function (feature) {
return feature;
})
onClosed()
const tooltipText = document.createElement('div')
tooltipText.classList.add(content_id)
if (feature) {
const container = tooltip.getElement();
const props = feature.getProperties()
const coordsFormated = feature.getGeometry()
const coordsFormat = feature.getGeometry().getFlatCoordinates()
// const hdms = toLonLat(coordinates)
// setTimeout(() => {
container.style.opacity = "1"
container.style.display = "flex"
container.style.minWidth = "max-content"
tooltipText.innerHTML = `
<div class="flex flex-col justify-items-center">
<div class="tooltip-title flex text-lg text-blue-500"> ${machine.machine_type} </div>
<code> ${coordinates} </code>
<hr class="mt-1 mb-2">
<div class="tooltip-title flex text-sm"><div class="w-20">Imei:</div> <div>${machine.imei}</div> </div>
<div class="tooltip-title flex text-sm"><div class="w-20">Дорога:</div> <div>${machine.railway_name}</div> </div>
<div class="tooltip-title flex text-sm"><div class="w-20">Компания:</div> <div>${machine.org_name}</div> </div>
<div class="tooltip-title flex text-sm"><div class="w-20">8зн номер:</div> <div>${machine.nomer_zn8}</div> </div>
<div class="tooltip-title flex text-sm"><div class="w-20">Устройство:</div> <div>${machine.device_number}</div> </div>
<a class="flex justify-center items-center bg-blue-300 hover:bg-blue-400 text-gray-800 p-1 mt-2 rounded" target="_blank" href="/html/askr_devices/analyze_comm/${machine.imei}"}">Детализация</a>
</div>`
container.prepend(tooltipText)
// }, 200)
tooltip.setPosition(e.coordinate)
}
});
map.on('pointermove', function (e) {
const pixel = map.getEventPixel(e.originalEvent);
const hit = map.hasFeatureAtPixel(pixel);
// console.log('hit', hit)
// console.log('pixel', pixel)
// console.log('map', map)
// map.getTarget().style.cursor = hit ? 'pointer' : '';
})
onClose.addEventListener('click', onClosed)
}
export class CoordsMap {
constructor(id) {
this.id = id
}
loadMap(coordinates, machine) {
if(!coordinates) {
if(!this.map) {
console.log('coordinates', coordinates)
console.log('machine', machine)
this.map = createMap(this.id, 5)
addLayerWithEmptyCoords(this.map, 'Данных о местоположении машины нет')
} else {
removeLayers(this.map)
addLayerWithEmptyCoords(this.map, 'Данных о местоположении машины нет')
setView(this.map, 5, [37.619829, 55.752603])
}
}
if(coordinates) {
console.log('coordinates', coordinates)
console.log('machine', machine)
if(!this.map) {
this.map = createMap(this.id, 8, coordinates)
addLayerIcon(this.map, coordinates, `${coordinates[1] ? +coordinates[1].toFixed(5) : ''} ${coordinates[0] ? +coordinates[0].toFixed(5) : ''}`)
} else {
removeLayers(this.map)
addLayerIcon(this.map, coordinates, `${coordinates[1] ? +coordinates[1].toFixed(5) : ''} ${coordinates[0] ? +coordinates[0].toFixed(5) : ''}`)
setView(this.map, 8, coordinates)
}
}
if(coordinates && machine) {
if(!this.map) {
this.map = createMap(this.id, 8, coordinates)
addLayerIcon(this.map, coordinates, machine.machine_type)
setTooltip(this.map, coordinates, machine, 'popup', 'popup-content', 'popup-closer')
// this.map.addOverlay(popup)
// this.map.addOverlay(marker(coordinates))
// setOverlay(this.map)
} else if (machine) {
removeLayers(this.map)
addLayer(this.map, coordinates, machine.machine_type)
setView(this.map, 8, coordinates)
setTooltip(this.map, coordinates, machine, 'popup', 'popup-content', 'popup-closer')
// this.map.addOverlay(popup)
// this.map.addOverlay(marker(coordinates))
// setOverlay(this.map)
}
}
}
}
@@ -0,0 +1,76 @@
<!-- eslint-disable -->
<script>
import Modal from '@molecules/Modal/index.vue'
import {CoordsMap} from './helpers'
export default {
name: 'MapModal',
components: {
Modal,
},
props: {
open: {
type: Boolean,
default: false
},
close: {
type: Function,
default: () => {}
},
id: {
type: String,
default: ""
},
data: {
type: Object,
default: () => ({})
},
text: {
type: String,
default: ""
},
headerClass: {
type: String,
default: ""
},
containerClass: {
type: String,
default: ""
}
},
updated () {
this.$nextTick(function () {
if (this.data && this.id && this.open) {
const newMap = new CoordsMap(this.id)
newMap.loadMap(this.data.coords, this.data.machine)
}
})
},
computed: {
},
methods: {
}
}
</script>
<!-- eslint-disable -->
<template>
<Modal
v-if="open"
:close="close"
:isOpen="true"
:containerClass="containerClass"
>
<div :class="headerClass">
<slot name="header" />
</div>
<slot />
</Modal>
</template>
<style scoped>
</style>
@@ -0,0 +1,87 @@
<template>
<div
v-if="isOpen"
class="absolute"
>
<div class="absolute">
<div
id="select_machines_modal"
class="fixed z-[10000] inset-0"
>
<!-- Modal container -->
<div class="h-screen flex items-center justify-center p-4">
<!-- Overlay -->
<div
class="absolute z-0 inset-0 bg-gray-500 opacity-75"
aria-hidden="true"
/>
<!-- Modal box -->
<div
class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl "
role="dialog"
aria-modal="true"
tabindex="0"
autofocus=""
>
<button
type="button"
class="absolute top-2 right-2 text-gray-400 flex space-x-1 items-center"
aria_label="close modal"
@click="close"
>
<span class="text-sm">(esc)</span>
<i class="ri ri-close-line text-2xl" />
</button>
<div
class="min-w-[350px] min-h-[400px] py"
:class="containerClass"
>
<slot />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Modal',
components: {},
props: {
isOpen: {
default: false,
type: Boolean
},
close: {
default: () => null,
type: Function
},
containerClass: {
default: "",
type: String,
}
},
data() {
},
computed: {
},
mounted () {
document.addEventListener("keydown", this.onEsc);
},
unmounted() {
document.removeEventListener("keydown", this.onEsc);
},
methods: {
onEsc (e) {
if (e.key === "Escape") {
this.close()
}
}
}
}
</script>
@@ -0,0 +1,41 @@
<template>
<svg
aria-hidden="true"
:width="size"
:height="size"
class="text-gray-200 animate-spin dark:text-gray-600 fill-blue-600"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</template>
<script>
export default {
name: 'RawSpinner',
components: {},
props: {
size: {
type: Number,
default: 25
},
},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,37 @@
<template>
<div class="w-fit flex items-center">
<RawSpinner
class="mr-2"
/>
<span
v-if="!disableText"
class="text-lg text-slate-700"
>{{ text }}</span>
</div>
</template>
<script>
import RawSpinner from './Spinner.vue'
export default {
name: 'Spinner',
components: {RawSpinner},
props: {
text: {
default: "Загрузка",
type: String
},
disableText: {
default: false,
type: Boolean
}
},
data() {
},
computed: {
},
methods: {
}
}
</script>
Whitespace-only changes.
@@ -0,0 +1,184 @@
<!-- eslint-disable -->
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import { validation } from './helper'
import { Alert } from "@store/hooks/Alert"
export default {
name: 'Pagination',
components: {
},
props: {
params: {
type: Object,
default: () => ({})
},
},
data() {
return {
inputValue: null,
initLocalStorage: null,
}
},
computed: {
},
watch: {
params: {
handler(value, oldValue) {
if(value) {
const loader = document.querySelector(`#table_loder_${this.params.id}`)
loader.style.display = 'none'
}
},
},
},
mounted () {
},
methods: {
selectOptions: (selectOption, paginationOptions) => {
const options = selectOption ? selectOption : optionsDefault
const currentOptions = options.filter((el) => {
if(el <= Math.ceil(paginationOptions.total / 10) * 10) {
return el
}
})
if(paginationOptions.show_all) {
currentOptions.push('Все')
}
return currentOptions
},
currentEntries: (pagination) => {
if (pagination) {
const endPageDataIdx = pagination.page_size * pagination.page_number
const isEndPageDataIdxMore = endPageDataIdx > pagination.total
if(isEndPageDataIdxMore || pagination.page_size > pagination.total) {
return (`${pagination.page_size * (pagination.page_number - 1) + 1} ... ${pagination.total}`)
} else {
return (`${pagination.page_size * (pagination.page_number - 1) + 1} ... ${pagination.page_size * pagination.page_number}`)
}
}
},
setPagination: function ({pageNumber, pageSize}) {
const loader = document.querySelector(`#table_loder_${this.params.id}`)
loader.style.display = 'block'
this.params.updatePagination({pageNumber: pageNumber, pageSize: pageSize})
},
updateSelectedValue: function (e) {
const loader = document.querySelector(`#table_loder_${this.params.id}`)
loader.style.display = 'block'
this.setPagination({
pageNumber: 1, // this.params.paginationOptions.page_number,
pageSize: e.target.value
})
},
updateInputValue: function (e) {
const pageNumber = e.target.value.trim()
const buttonPageInput = document.querySelector(`.page-button-${this.params.id}`)
if(pageNumber) {
this.inputValue = pageNumber
buttonPageInput.removeAttribute('disabled')
buttonPageInput.classList.remove('opacity-50')
} else {
buttonPageInput.setAttribute('disabled', 'disabled')
buttonPageInput.classList.add('opacity-50')
}
},
setPaginationButton: function (e) {
if(this.inputValue) {
const error = validation(this.inputValue, this.params.paginationOptions)
if(Number(this.inputValue) === this.params.paginationOptions.page_number) {
return false
}
if(!error) {
e.target.removeAttribute('disabled')
e.target.classList.remove('opacity-50')
const loader = document.querySelector(`#table_loder_${this.params.id}`)
loader.style.display = 'block'
e.target.focus()
const params = {pageNumber: this.inputValue, pageSize: this.params.paginationOptions.page_size}
return this.params.updatePagination(params)
} else {
e.target.setAttribute('disabled', 'disabled')
e.target.classList.add('opacity-50')
const errorObj = {
id: this.params.id,
color: "red",
icon: "<i class='ri-alert-line'></i>",
message: error,
delay: 5000,
}
return new Alert(errorObj)
}
}
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="pagination w-full flex flex-row items-center justify-between gap-3 mt-2 mb-8 xl:mb-0 pb-8 xl:p-2.5 bg-slate-100 border-2 border-solid border-slate-300 rounded-lg flex-col 2xl:flex-row">
<div class="pagination-left flex flex-row gap-1">
<div class="current-entries flex text-sm font-bold">Показаны {{currentEntries(params?.paginationOptions)}}</div>
<div class="count-entries flex text-sm font-bold">(Всего записей - {{params?.paginationOptions?.total}})</div>
</div>
<div class="pagination-right flex flex-col xl:flex-row items-center gap-3">
<div :id="`table_loder_${params.id}`" class="hidden w-full h-auto bg-light flex items-center justify-center px-2"><div type-node="loader" class="flex my_margin-3 items-center justify-center">
<div role="status">
<svg aria-hidden="true" class="mr-2 w-6 h-6 text-gray-400 animate-spin dark:text-gray-400 fill-white" viewBox="0 0 100 101" fill="none" >
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
</div>
</div></div>
<div class="buttons flex h-fit gap-1 sm:gap-3 text-sm">
<div class="flex gap-1 sm:gap-3" v-if="params.paginationOptions.page_number > 1">
<div v-if="params.paginationOptions.page_number - 1 !== 1">
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: 1, pageSize: params.paginationOptions.page_size})">
<span>1</span>
</button>
</div>
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number - 1, pageSize: params.paginationOptions.page_size})">
<span class="hidden sm:flex">Назад</span><i class="ri-arrow-left-line sm:hidden"></i>
</button>
<button :table_btn="`${params.id}`" class="previous flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number - 1, pageSize: params.paginationOptions.page_size})">
<span>{{params.paginationOptions.page_number - 1}}</span>
</button>
</div>
<button :table_btn="`${params.id}`" class="active flex items-center text-blue-700 font-bold bg-white py-1 px-3 gap-2 rounded cursor-default">
<span>{{params.paginationOptions.page_number}}</span>
</button>
<div class="flex gap-1 sm:gap-3" v-if="params.paginationOptions.page_number < params.paginationOptions.total_pages">
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number + 1, pageSize: params.paginationOptions.page_size})">
<span>{{params.paginationOptions.page_number + 1}}</span>
</button>
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.page_number + 1, pageSize: params.paginationOptions.page_size})">
<span class="hidden sm:flex">Вперёд</span><i class="ri-arrow-right-line sm:hidden"></i>
</button>
<div v-if="params.paginationOptions.page_number + 1 !== params.paginationOptions.total_pages">
<button :table_btn="`${params.id}`" class="next flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-2 rounded" @click="setPagination({pageNumber: params.paginationOptions.total_pages, pageSize: params.paginationOptions.page_size})">
<span>{{params.paginationOptions.total_pages}}</span>
</button>
</div>
</div>
</div>
<div class="flex flex-row items-center justify-center gap-2"><input v-on:input="updateInputValue" :class="`page-input-${params.id}`" class="flex max-w-[80px] items-center text-slate-700 text-sm font-bold focus-visible:outline-blue-300 bg-white py-1 px-2 gap-2 rounded" value="" placeholder="Страница"/><button @click="setPaginationButton" disabled :class="`page-button-${params.id}`" class="opacity-50 flex items-center text-white font-bold bg-slate-600 hover:bg-slate-700 py-1 px-1 rounded">Перейти</button></div>
<div id="select-on-page" class="select-on-page flex items-center justify-center gap-3 p-1 text-sm text-slate-700 font-bold">
Показать
<select name="on_page" v-bind:value="params.paginationOptions.page_size" v-on:change="updateSelectedValue" :table_option="`${params.id}`" class="flex text-gray-700 pl-3 pr-9 py-0.5 rounded cursor-pointer border-none" style="--tw-ring-color:focus: none" >
<option :key={idx} v-for="(el, idx) in selectOptions(params.selectOption, params.paginationOptions)" :value="`${el === 'Все' ? params.paginationOptions.total : el}`">
{{ el }}
</option>`
</select>
</div>
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,356 @@
import { always, cond, equals, is, T, mergeLeft } from "ramda";
import Handlebars from "handlebars/dist/cjs/handlebars";
import moment from "moment";
import presetConfig from './presetConfig'
import {parseFunc, parseFunc3} from './stringFunction'
const tablefy = (list = []) => {
const defaultData = (title) => ({
headerFilter: "input",
minWidth: 80,
headerFilterPlaceholder: title,
download: false,
print: false,
vertAlign: "middle",
sorter: "string",
responsive: 0
})
if (!is(Array, list)) return []
return list.map((el) => mergeLeft(el, defaultData(el?.title || "")))
}
const math = (value, operator, operand, options) => {
var left = parseFloat(value);
var right = parseFloat(operand);
switch(operator) {
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
case "%":
return left % right;
default:
return "";
}
}
const link = (col) => ({
...col,
formatter: (cell, { prefix = "", text, key = "id" }, onRendered) => {
onRendered(function () {
let row = cell.getRow().getData();
if (!text && !cell.getValue()) {
return null;
}
if (!row[key]) return null;
cell.getElement().innerHTML = `<a target="_blank" href="${prefix}/${
row[key]
}">${text || cell.getValue()}</a>`;
});
},
});
[
['renderFunc', (strFunc, record, strDataIndex) => {
const pFunc = strFunc.replace(/##/gm, "'")
return parseFunc(pFunc)(record[strDataIndex], record)
}],
['math', math],
['presetConfig', (record, pack) => presetConfig(pack, record)],
[
"dt_shift",
(date, shift, param) => {
return date ? moment(date).add(shift, param) : "нд";
},
],
[
"dt_format",
(date) => {
return date ? moment(date).format("DD.MM.YYYY HH:mm") : "нд";
},
],
[
"dt_db_format",
(date) => {
return date ? moment(date).format("YYYY-MM-DDTHH:mm:ss") : "нд";
},
],
[
"from_now",
(date) => {
return moment(date).fromNow()
},
],
[
"json_parse",
(item) => {
return `
<div>
${item}
</div>
`
},
],
[
"time_from_sec",
(ms) => {
if (!ms) return "н/д"
return `${moment(moment().subtract(Math.round(ms), 'milliseconds')).fromNow()}`.replace("назад", '')
},
],
[
"lenght",
(list) => {
return is(Array, list) ? list.length : 0;
},
],
[
"more",
(one, two) => {
return one > two;
},
],
[
"less",
(one, two) => {
return one < two;
},
],
[
"or",
(one, two) => {
return one || two;
},
],
[
"eq",
(one, two) => {
return one == two;
},
],
[
"from_list",
(list, param_string) => {
}
],
[
"endOf",
(date, param) => moment(date).endOf(param)
],
[
"startOf",
(date, param) => moment(date).startOf(param)
],
[
"render",
(list, key) => {
return list.map(el => el[key]).join(", ")
}
],
[
"get",
(map, key) => {
return map[key] ? `${map[key]}` : ""
}
],
[
"get_from",
(map, key) => {
return map[key] ? `${map[key]}` : ""
}
],
[
"uint8parse",
(list) => pipe(
(x) => Uint8Array.from(x, e => e.charCodeAt(0)),
Array.from
)(list)
],
[
"filter_aoo",
(list = [], key, _availible_list) => {
const availible_list = pipe(
(x) => Uint8Array.from(x, e => e.charCodeAt(0)),
Array.from
)(_availible_list)
return list.filter((el) =>
availible_list.includes(el[key]))
}
]
].forEach(([name, func]) => {
Handlebars.registerHelper(name, func);
});
const render = (col) => ({
...col,
formatter: (cell, _, onRendered) => {
onRendered(function () {
const record = cell.getRow().getData()
// console.log('col.onCell', parseFunc(col.onCell))
if(col.onCell && record.status) {
// cell.getElement().style.backgroundColor = setColorCellFromStatus(record.status)
cell.getElement().style.backgroundColor = parseFunc3(col.onCell)(record)
}
if(col.onCell && !record.status) {
console.log('onCell', col.onCell)
cell.getElement().style.backgroundColor = parseFunc(col.onCell)(record)?.style?.backgroundColor
}
if (`${col.render}`.includes("{{") && `${col.render}`.includes("}}")) {
let record = cell.getRow().getData();
cell.getElement().innerHTML = Handlebars.compile(col.render)(
{...record, __record__: record}
);
} else {
cell.getElement().innerHTML = parseFunc(col.render)(record[col["dataIndex"]] || record[col["field"]] || record[col["key"]], record)
}
});
},
});
let sorter = (a, b) => {
let [a_field, b_field] = [a["field"], b["field"]];
if (a_field.includes("id") && b_field.includes("id"))
return a_field.localeCompare(b_field);
if (a_field.includes("id")) return -1;
if (b_field.includes("id")) return 1;
if (a_field.includes("dt") && b_field.includes("dt"))
return b_field.localeCompare(a_field);
if (a_field.includes("dt")) return -1;
if (b_field.includes("dt")) return 1;
if (a_field.includes("imei") && b_field.includes("imei"))
return b_field.localeCompare(a_field);
if (a_field.includes("imei")) return -1;
if (b_field.includes("imei")) return 1;
return a_field.localeCompare(b_field);
};
const prepCol = (col) =>
cond([
[() => is(String, col.render), () => render(col)],
[equals("link"), () => link(col)],
[T, always(col)],
])(col.mode);
const prepColumns = (cols) =>
is(Array, cols) ? cols.map((col) => prepCol(col)) : [];
const renderGroupHeader = ({ groupHeader, value, count, group, extra }) => {
const group_data = group._group;
if (group_data.level == 0) {
const {company_id, param, train_info: train, date: _date} = group_data.groupList[0].rows[0].data
const date = `${_date}`.replace(/\s(.*)/gm, "")
const icident_group = extra.find((el) =>
el.date === date &&
el.company_id == company_id &&
el.train == train &&
el.param == param
) || {}
return Handlebars.compile(groupHeader)({
_value: value,
_count: count,
extra: extra,
icident_group: icident_group
});
}
return `${value} (${count} записей)`;
};
const groupByFunction = (groupBy) => (data) =>
Handlebars.compile(groupBy)(data);
const makedFiltering = (id, filterParams, selectedFilter, clearFilters) => {
const table = document.querySelector(`#${id}`)
const isFiltering = document.querySelector(`${id}-filters`)
if(id && filterParams) {
filterParams.unshift([])
const filtersContainer = document.createElement('div')
filtersContainer.setAttribute("id", `${id}-filters`)
filtersContainer.classList.add("col-span-12")
filtersContainer.innerHTML = `
<div id="${id}-filtersContainer" class="filters flex flex-row items-center justify-start gap-3 mb-0 pt-2.5 bg-slate-100 rounded-t-md flex-row 2xl:flex-row">
<div id="${id}-selectFilters" class="flex items-center justify-start gap-3 p-1 ml-2 text-base text-slate-700 font-bold">
Фильтр по статусу
<select name="selectFilter" id="${id}-selectFilter" class="flex text-gray-700 pl-3 pr-9 py-0.5 rounded cursor-pointer border-none" style="--tw-ring-color:focus: none" >
${filterParams.map((el) => {
return (`<option value='${el}'>
${el}
</option>`)
})}
</select>
</div>
<button id="${id}-filtersClear"class="w-fit h-fit flex items-center text-white text-sm font-bold bg-slate-600 hover:bg-slate-700 py-1 px-3 gap-1 rounded">Очистить фильтр</button>
</div>`
if(!isFiltering) {
table.before(filtersContainer)
}
else {
isFiltering.remove()
table.before(filtersContainer)
}
const selectFilters = document.querySelectorAll(`#${id}-selectFilter`)
const selectOptions = document.querySelectorAll(`#${id}-selectFilter option`)
const buttonClearFilters = document.querySelector(`#${id}-filtersClear`)
selectFilters.forEach((el) => {
el.onchange = (e) => {
if(e.target.value) {
selectOptions[0].removeAttribute('selected')
console.log('e', e)
console.log('selectFilters[0]', selectFilters[0])
console.log('selectOptions[0][0]', selectOptions[0])
selectedFilter(e.target.value)
} else {
clearFilters()
e.target.setAttribute('selected', 'selected')
}
}
})
buttonClearFilters.onclick = () => {
clearFilters()
selectOptions[0].setAttribute('selected', 'selected')
console.log('selectFilters[0]', selectFilters[0])
}
}
}
const validation = (value, pagination) => {
const pipe = (...fns) => (...x) => fns.reduce((error, validator) => error || validator(...x), undefined)
const required = value => {
return value ? undefined : "*"
}
const minPage = length => value => {
value = value.replace(/\s+/g, '')
return value >= length ? undefined : `Номер страницы должен быть больше или равен ${length}`
}
const maxPage = length => value => {
value = value.replace(/\s+/g, '')
return length >= value ? undefined : `Номер страницы должен быть меньше или равен ${length}`
}
const onlyDigits = value => {
const pattern = /^([\d])*$/g
return pattern.test(value) ? undefined : `Номер страницы должен содержать только цифры`
}
const composedValidators = pipe(onlyDigits, minPage(1), maxPage(pagination.total_pages))
return composedValidators(value)
}
export {prepColumns, sorter, renderGroupHeader, groupByFunction, makedFiltering, tablefy, validation};
@@ -0,0 +1,194 @@
<script>
import {TabulatorFull as Tabulator} from 'tabulator-tables';
import {
prepColumns,
sorter,
renderGroupHeader,
groupByFunction,
makedFiltering,
tablefy
} from "./helper";
import localization from "./localization";
import { cond, equals, is, mergeAll, sort, T, mergeDeepRight } from "ramda";
import { pipe } from "@helpers/functions";
import Pagination from "./Pagination.vue";
export default {
name: 'App',
components: {
Pagination
},
props: {
dataSource: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
initialSort: {
type: String,
default: null
},
groupBy: {
type: String,
default: null
},
groupByTemplate: {
type: String,
default: null
},
groupValues: {
type: String,
default: null
},
height: {
type: String,
default: "550px"
},
groupExtraValues: {
type: String,
default: null
},
groupHeader: {
type: String,
default: null
},
pagination: {
type: Object,
default: null
},
selectOption: {
type: String,
default: null
},
filterData: {
type: String,
default: null
},
withTablefy: {
type: Boolean,
default: true
}
},
data() {
return {
refId: Math.random().toString(36).slice(4),
}
},
computed: {
data () {
if (!is(Array, this.dataSource)) return []
if (this.withTablefy) return tablefy(this.dataSource)
return this.dataSource
}
},
mounted() {
this.makeTabele()
},
updated() {
this.makeTabele()
},
methods: {
makeTabele () {
this.table = new Tabulator(this.$refs[this.refId], this.getConfig())
},
getConfig () {
let configurator = pipe(this.__defaultCfg());
return configurator(
(x) => mergeDeepRight(x, this.__getColsCfg()),
(x) => mergeDeepRight(x, this.__getSortCfg()),
(x) => mergeDeepRight(x, this.__getGroupCfg()),
(x) => mergeDeepRight(x, this.__getGroupHeaderCfg()),
// (x) => mergeAll(x, this.__getPaginationCgs())
);
},
__defaultCfg () {
return {
locale: "ru",
langs: localization(),
height: this.height || "250px",
responsiveLayout: "collapse",
placeholder: "Нет записей",
movableColumns: true,
autoResize: false,
data: this.data,
groupStartOpen: false,
groupHeader: function (value, count, data, group) {
return value + "<span>(" + count + " записей)</span>";
},
};
},
__getColsCfg() {
let layout, columns = []
if (this.autoColumns && is(Object, this.data[0])) {
let first = this.data[0];
let keys = Object.keys(first).map((key) => ({
headerFilter: "input",
headerFilterPlaceholder: key,
title: key,
field: key,
download: false,
print: false,
vertAlign: "middle",
sorter: "string",
responsive: 0,
minWidth: 80,
maxWidth: 300,
}));
layout = "fitDataFill"
columns = sort(sorter, keys)
} else {
layout = "fitDataStretch"
columns = prepColumns(this.columns)
}
if (this.withTablefy) {
columns = tablefy(columns)
}
return {columns, layout}
},
__getSortCfg() {
if (this.initialSort) return { initialSort: this.initialSort };
return {};
},
__getGroupCfg() {
if (this.groupByTemplate)
return { groupBy: groupByFunction(this.groupByTemplate) };
if (this.groupBy) return { groupBy: this.groupBy };
return {};
},
__getGroupHeaderCfg() {
if (this.groupHeader)
return {
groupHeader: (value, count, data, group) =>
renderGroupHeader({
extra: this.groupExtraValues,
groupHeader: this.groupHeader,
value,
count,
data,
group,
}),
};
return {};
},
}
}
</script>
<template>
<div class="mb-4">
<div class="overflow-x-auto w-full">
<div
:ref="refId"
class="w-full"
/>
</div>
</div>
<div v-if="pagination">
<Pagination :params="pagination"/>
</div>
</template>
@@ -0,0 +1,29 @@
const localization = () => ({
ru: {
groups: {
item: "запись",
items: "записей",
},
"pagination":{
"page_size":"Размер таблицы", //label for the page size select element
"page_title":"Показать страницу",//tooltip text for the numeric page button, appears in front of the page number (eg. "Show Page" will result in a tool tip of "Show Page 1" on the page 1 button)
"first":"К первой", //text for the first page button
"first_title":"Показать страницу", //tooltip text for the first page button
"last":"К последней",
"last_title":"Последняя страница",
"prev":"<<",
"prev_title":"<<",
"next":">>",
"next_title":">>",
"all":"Все",
"counter":{
"showing": "Показать",
"of": "из",
"rows": "строки",
"pages": "страницы",
}
},
},
});
export default localization;
@@ -0,0 +1,244 @@
import { cond, T } from "ramda";
const text = `
0 Конфигурация не установлена (по умолчанию)
1 Стандартная конфигурация МРТ (МПТ/АВФ)
2 Стандартная конфигурация ЖДСМ
3 Стандартная конфигурация УК/ВПО
4 ЩОМ/РМ (Акселерометр)
5 СМ (Сибирский ПГУПС)
6 СМ (зарезервировано)
7 Эмуляция платы БКП
8 Конфигурация Блок-М
40 Эмуляторы
41 Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus
50 Стандартная конфигурация ModBus Master (опрос внешних устройств)
51 Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)
52 Конфигурация трекера (электрички и т.п.)
53 Конфигурация трекер + счетчик импульсов + акселерометр
54 РадарП
55 СДРГО
100 ПМА-1М РПМ
101 ЩОМ-1400 РПМ
102 АМ-140 СКРТ РПМ
103 АС-01 РПМ
104 ТЭС ПСС-1К РПМ
105 ПСС-2П РПМ
200 РПБ-01 ВНИКТИ
201 МПК Спецлоко
202 УК 25/25 ВНИКТИ
203 СЗ-800 ВНИКТИ
300 ЩОМ-1200С ТЖДМ
301 ЩОМ-2000 ТЖДМc
302 ССГС-1 ТЖДМ
303 ВПО-С ТЖДМ
304 МПВ ТЖДМ
305 УПК ТЖДМ
306 ПРЛ-М ТЖДМ
307 РУ-700 ТЖДМ
308 МПВ Секция 2 ТЖДМ
1911 Конфигурация для отладки
4096 Настраиваемая конфигурация
`
//TODO SERGEY
const parse = () => {
text
return {}
}
const get_f1_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
};
return r[el] || el;
};
const sadko_2_preset = (el) => {
const r = {
0: "Конфигурация, не установлена (по умолчанию)",
1: "Стандартная, конфигурация МРТ (МПТ/АВФ)",
2: "Стандартная конфигурация ЖДСМ",
3: "Стандартная конфигурация УК/ВПО",
4: "ЩОМ/РМ (Акселерометр)",
5: "СМ (Сибирский ПГУПС)",
6: "СМ (зарезервировано)",
7: "Эмуляция платы БКП",
8: "Конфигурация Блок-М",
40: "Эмуляторы",
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus",
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
52: "Конфигурация трекера (электрички и т.п.)",
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
54: "РадарП",
55: "СДРГО",
100 : "ПМА-1М РПМ",
101 : "ЩОМ-1400 РПМ",
102 : "АМ-140 СКРТ РПМ",
103 : "АС-01 РПМ",
104 : "ТЭС ПСС-1К РПМ",
105 : "ПСС-2П РПМ",
200 : "РПБ-01 ВНИКТИ",
201 : "МПК Спецлоко",
202 : "УК 25/25 ВНИКТИ",
203 : "СЗ-800 ВНИКТИ",
300 : "ЩОМ-1200С ТЖДМ",
301 : "ЩОМ-2000 ТЖДМ",
302 : "ССГС-1 ТЖДМ",
303 : "ВПО-С ТЖДМ",
304 : "МПВ ТЖДМ",
305 : "УПК ТЖДМ",
306 : "ПРЛ-М ТЖДМ",
307 : "РУ-700 ТЖДМ",
308 : "МПВ Секция 2 ТЖДМ",
1911: "Конфигурация для отладки",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_4_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Стандартная конфигурация МРТ (МПТ/АВФ)",
2: "Стандартная конфигурация ЖДСМ",
3: "Стандартная конфигурация УК/ВПО",
4: "ЩОМ/РМ (Акселерометр)",
5: "СМ (Сибирский ПГУПС)",
6: "СМ (зарезервировано)",
7: "Эмуляция платы БКП",
8: "Конфигурация Блок-М",
40: "Эмуляторы (CAN1 + ОНК-160 + ModBus)",
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus)",
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
52: "Конфигурация трекера (электрички и т.п.)",
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
54: "РадарП",
55: "СДРГО",
100: "ПМА-1М РПМ",
101: "ЩОМ-1400 РПМ",
102: "АМ-140 СКРТ РПМ",
103: "АС-01 РПМ",
104: "ТЭС ПСС-1К РПМ",
105: "ПСС-2П РПМ",
200: "РПБ-01 ВНИКТИ",
201: "МПК Спецлоко",
202: "УК 25/25 ВНИКТИ",
203: "СЗ-800 ВНИКТИ",
300: "ЩОМ-1200С ТЖДМ",
301: "ЩОМ-2000 ТЖДМ",
302: "ССГС-1 ТЖДМ",
303: "ВПО-С ТЖДМ",
304: "МПВ ТЖДМ",
305: "УПК ТЖДМ",
306: "ПРЛ-М ТЖДМ",
307: "РУ-700 ТЖДМ",
308: "МПВ Секция 2 ТЖДМ",
1911: "Конфигурация для отладки",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_skrt_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Обработка импульсной СКРТ",
2: "Конфигурация ModBus Slave Base",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const uk_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "УК AC с подключением по SPI",
2: "УК AC с подключением по LORA",
3: "УК DC с подключением по SPI",
4: "УК DC с подключением по LORA",
5: "ВПО AC/DC с подключением по SPI",
6: "ВПО AC/DC с подключением по LORA",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_64_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_dig_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Конфигурация с эмуляторами",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_69_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_80_preset = (el) => {
const r = {
0: "Плата с экраном для СМ",
1: "Плата с экраном для ПСС",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_81_preset = (el) => {
const r = {
0: "Плата с ультразвуковыми датчиками (СМ и т.п.)",
4096: "Плата с ультразвуковыми датчиками (СМ и т.п.) настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_82_preset = (el) => {
const r = {
0: "Плата с дискретными входами + LORA (СМ Tail)",
4096: "Плата с дискретными входами + LORA (СМ Tail) настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_83_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Конфигурация для МПТ",
2: "Конфигурация для ТЭС ПСС-1К",
3: "Конфигурация для СМ (СМ-2Б)",
4: "Конфигурация для Динамик",
5: "Конфигурация для УТМ-5",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const presetConfig = (el, record) => {
const { board_id: board_id } = record;
board_id == 64 && console.log(record)
return cond([
[(board) => board == 1, () => get_f1_preset(el)],
[(board) => board == 2, () => sadko_2_preset(el)],
[(board) => board == 4, () => sadko_4_preset(el)],
[(board) => board == 16, () => sadko_skrt_preset(el)],
[(board) => board == 19, () => uk_preset(el)],
[(board) => board == 64, () => sadko_64_preset(el)],
[(board) => board == 68, () => sadko_dig_preset(el)],
[(board) => board == 69, () => sadko_69_preset(el)],
[(board) => board == 80, () => sadko_80_preset(el)],
[(board) => board == 81, () => sadko_81_preset(el)],
[(board) => board == 82, () => sadko_82_preset(el)],
[(board) => board == 83, () => sadko_83_preset(el)],
[T, () => el],
])(board_id);
};
export default presetConfig
@@ -0,0 +1,406 @@
// import "antd/dist/antd.css";
import axios from "axios";
import { parseSepareteDate } from "./time_funcs";
import {
range,
groupBy,
find,
propEq,
findIndex,
update,
is,
cond,
T,
isNil
} from "ramda";
let getPackSettings = (userId, page, packStructId, func) => {
if (userId && page && packStructId) {
axios({
method: "GET",
url: `/api/settings/${userId}/${page}/${packStructId}`,
headers: {
Authorization: `vniizht ${btoa(JSON.stringify({ date: new Date() }))}`,
},
})
.then(({ data }) => {
func(data)
})
.catch((e) => console.warn(e));
}
};
const prepDiscretes = (from, to, arr) =>
range(from, to + 1).map((el) => ({
title: el,
dataIndex: `disc${el}`,
key: `disc${el}`,
width: 60,
render: (v) =>
v ? (
"вкл"
) : (
"выкл"
),
}));
const is_number = (val) => {
try {
return +val;
} catch (_e) {
return false;
}
};
const is_dt = (val) => {
let is_invalid = new Date(val) !== "Invalid Date";
return val && val != "" && !is_invalid;
};
const compare = (val, from, untill) => {
if (is_number(val)) {
return val >= +from && val <= +untill;
}
if (is_dt(val)) {
let val = new Date(val).getTime;
let from = new Date(from).getTime;
let untill = new Date(untill).getTime;
return val >= from && val <= untill;
}
return (
`${val}`.toLowerCase() == `${from}`.toLowerCase() ||
`${val}`.toLowerCase() == `${untill}`.toLowerCase()
);
};
const withCustomSettings = (_columns, requestSettings) => {
let columns = _columns
if (requestSettings?.colors?.isOn) {
}
if (requestSettings?.colors?.isOn) {
let colors = requestSettings.colors.list.filter((el) => el.isOn);
colors = groupBy((c) => c.param)(colors);
coloredParams = Object.keys(colors);
columns = columns.map((el) => {
if (coloredParams.includes(el.dataIndex)) {
return {
...el,
onCell: (data, index) => {
let colrSettings = colors[el.dataIndex];
let value = data[el.dataIndex];
let color = find(({ from, untill }) =>
compare(value, from, untill)
)(colrSettings);
return {
style: { background: color?.color?.hex || "white" },
};
},
};
}
return el;
});
}
let sort_order =
requestSettings?.settings?.sort_order == "desc" ? "descend" : "ascend";
let prim_param = requestSettings?.settings?.date_param;
let prim_base_date =
find(propEq("dataIndex", prim_param), columns) ||
find(propEq("dataIndex", "pack_dt_new"), columns);
let prim_index = findIndex(propEq("dataIndex", prim_param), columns);
// сделано, потому что в пакете может быть подменена дата
if (prim_index < 0) {
prim_base_date = find(propEq("dataIndex", "pack_dt_new"), columns);
prim_index = findIndex(propEq("dataIndex", "pack_dt_new"), columns);
}
if (prim_index >= 0) {
let sec_param = prim_param == "pack_dt" ? "dt" : "pack_dt";
let sec_base_date = find(propEq("dataIndex", sec_param), columns);
let sec_index = findIndex(propEq("dataIndex", sec_param), columns);
if (sec_index < 0) {
sec_base_date = find(propEq("dataIndex", "pack_dt_new"), columns);
sec_index = findIndex(propEq("dataIndex", "pack_dt_new"), columns);
}
if (sec_index > 0) {
columns = update(
prim_index,
{
...prim_base_date,
defaultSortOrder: sort_order,
sorter: (a, b) => new Date(a[prim_param]) - new Date(b[prim_param]),
},
columns
);
let sec_object = {
...sec_base_date,
sorter: (a, b) => new Date(a[prim_param]) - new Date(b[prim_param]),
};
delete sec_object.defaultSortOrder;
columns = update(sec_index, sec_object, columns);
}
}
return columns;
};
const fetch_sorter_field = (sorter, req_params) => {
const date_param = req_params?.settings?.date_param
return cond([
[() => sorter?.field, () => sorter.field],
[is(Array), field_from_arr(sorter, date_param)],
[isNil, () => date_param],
])(sorter)
}
const field_from_arr = (sorter, date_param) => () => {
const field = sorter.find((el) => `${el?.field}`.includes("dt"))?.field
return field || date_param
}
const get_f1_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
};
return r[el] || el;
};
const sadko_2_preset = (el) => {
const r = {
0: "Конфигурация, не установлена (по умолчанию)",
1: "Стандартная, конфигурация МРТ (МПТ/АВФ)",
2: "Стандартная конфигурация ЖДСМ",
3: "Стандартная конфигурация УК/ВПО",
4: "ЩОМ/РМ (Акселерометр)",
5: "СМ (Сибирский ПГУПС)",
6: "СМ (зарезервировано)",
7: "Эмуляция платы БКП",
8: "Конфигурация Блок-М",
40: "Эмуляторы",
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus",
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
52: "Конфигурация трекера (электрички и т.п.)",
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
54: "РадарП",
55: "СДРГО",
100 : "ПМА-1М РПМ",
101 : "ЩОМ-1400 РПМ",
102 : "АМ-140 СКРТ РПМ",
103 : "АС-01 РПМ",
104 : "ТЭС ПСС-1К РПМ",
105 : "ПСС-2П РПМ",
200 : "РПБ-01 ВНИКТИ",
201 : "МПК Спецлоко",
202 : "УК 25/25 ВНИКТИ",
203 : "СЗ-800 ВНИКТИ",
300 : "ЩОМ-1200С ТЖДМ",
301 : "ЩОМ-2000 ТЖДМ",
302 : "ССГС-1 ТЖДМ",
303 : "ВПО-С ТЖДМ",
304 : "МПВ ТЖДМ",
305 : "УПК ТЖДМ",
306 : "ПРЛ-М ТЖДМ",
307 : "РУ-700 ТЖДМ",
308 : "МПВ Секция 2 ТЖДМ",
1911: "Конфигурация для отладки",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_4_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Стандартная конфигурация МРТ (МПТ/АВФ)",
2: "Стандартная конфигурация ЖДСМ",
3: "Стандартная конфигурация УК/ВПО",
4: "ЩОМ/РМ (Акселерометр)",
5: "СМ (Сибирский ПГУПС)",
6: "СМ (зарезервировано)",
7: "Эмуляция платы БКП",
8: "Конфигурация Блок-М",
40: "Эмуляторы (CAN1 + ОНК-160 + ModBus)",
41: "Эмуляторы (CAN1 + CAN2 (Блок-М) + ModBus)",
50: "Стандартная конфигурация ModBus Master (опрос внешних устройств)",
51: "Стандартная конфигурация ModBus Master (опрос плат АС КРСПС)",
52: "Конфигурация трекера (электрички и т.п.)",
53: "Конфигурация трекер + счетчик импульсов + акселерометр",
54: "РадарП",
55: "СДРГО",
100: "ПМА-1М РПМ",
101: "ЩОМ-1400 РПМ",
102: "АМ-140 СКРТ РПМ",
103: "АС-01 РПМ",
104: "ТЭС ПСС-1К РПМ",
105: "ПСС-2П РПМ",
200: "РПБ-01 ВНИКТИ",
201: "МПК Спецлоко",
202: "УК 25/25 ВНИКТИ",
203: "СЗ-800 ВНИКТИ",
300: "ЩОМ-1200С ТЖДМ",
301: "ЩОМ-2000 ТЖДМ",
302: "ССГС-1 ТЖДМ",
303: "ВПО-С ТЖДМ",
304: "МПВ ТЖДМ",
305: "УПК ТЖДМ",
306: "ПРЛ-М ТЖДМ",
307: "РУ-700 ТЖДМ",
308: "МПВ Секция 2 ТЖДМ",
1911: "Конфигурация для отладки",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_skrt_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Обработка импульсной СКРТ",
2: "Конфигурация ModBus Slave Base",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const uk_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "УК AC с подключением по SPI",
2: "УК AC с подключением по LORA",
3: "УК DC с подключением по SPI",
4: "УК DC с подключением по LORA",
5: "ВПО AC/DC с подключением по SPI",
6: "ВПО AC/DC с подключением по LORA",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_64_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_dig_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Конфигурация с эмуляторами",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_69_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_80_preset = (el) => {
const r = {
0: "Плата с экраном для СМ",
1: "Плата с экраном для ПСС",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_81_preset = (el) => {
const r = {
0: "Плата с ультразвуковыми датчиками (СМ и т.п.)",
4096: "Плата с ультразвуковыми датчиками (СМ и т.п.) настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_82_preset = (el) => {
const r = {
0: "Плата с дискретными входами + LORA (СМ Tail)",
4096: "Плата с дискретными входами + LORA (СМ Tail) настраиваемая конфигурация",
};
return r[el] || el;
};
const sadko_83_preset = (el) => {
const r = {
0: "Конфигурация не установлена (по умолчанию)",
1: "Конфигурация для МПТ",
2: "Конфигурация для ТЭС ПСС-1К",
3: "Конфигурация для СМ (СМ-2Б)",
4: "Конфигурация для Динамик",
5: "Конфигурация для УТМ-5",
4096: "Настраиваемая конфигурация",
};
return r[el] || el;
};
const presetConfig = (el, record) => {
const { board_id: board_id } = record;
board_id == 64 && console.log(record)
return cond([
[(board) => board == 1, () => get_f1_preset(el)],
[(board) => board == 2, () => sadko_2_preset(el)],
[(board) => board == 4, () => sadko_4_preset(el)],
[(board) => board == 16, () => sadko_skrt_preset(el)],
[(board) => board == 19, () => uk_preset(el)],
[(board) => board == 64, () => sadko_64_preset(el)],
[(board) => board == 68, () => sadko_dig_preset(el)],
[(board) => board == 69, () => sadko_69_preset(el)],
[(board) => board == 80, () => sadko_80_preset(el)],
[(board) => board == 81, () => sadko_81_preset(el)],
[(board) => board == 82, () => sadko_82_preset(el)],
[(board) => board == 83, () => sadko_83_preset(el)],
[T, () => el],
])(board_id);
};
const fVarsNames = [
"getPackSettings",
"prepDiscretes",
"is_number",
"is_dt",
"compare",
"withCustomSettings",
"parseSepareteDate",
"presetConfig",
];
const fVarsFuncs = [
getPackSettings,
prepDiscretes,
is_number,
is_dt,
compare,
withCustomSettings,
parseSepareteDate,
presetConfig,
];
let parseFunc = (string) => {
return (item, el) => {
const f = new Function(
"item",
"el",
...fVarsNames,
`return (${string})(item, el)`
);
return f(item, el, ...fVarsFuncs);
};
};
let parseFunc2 = (string) => {
const f = new Function(...fVarsNames, `return (() => ${string})()`);
return f(...fVarsFuncs);
};
const parseFunc3 = (string) => {
const f = new Function(`return (${string})`)
return f()
}
export {parseFunc, parseFunc2, parseFunc3}
@@ -0,0 +1,168 @@
import { inc, type, cond, equals, T, pipe } from "ramda";
export const isDate = (date) =>
type(date) === "Date" || new Date(date).getTime() > 0;
export const formateDateToString = (date) =>
isDate(date) && date.toLocaleString();
const monthNames = [
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь",
];
export const reformateDateString = (date, format = "full") =>
!!date ? reformate(date, format) : null;
const reformate = (date, format) =>
cond([
[
equals("time-only"),
() => {
const { hr, min, sec } = getTime(date);
return `${hr}:${min}:${sec}`;
},
],
[
equals("date-only"),
() => {
const { day, mth, year } = getDate(date);
return `${day}/${mth}/${year}`;
},
],
[
equals("date-report"),
() => {
const { day, mth, year } = getDate(date);
return `${year}-${mth}-${day}`;
},
],
[
equals("mth-date-report"),
() => {
date = new Date(date);
return `${getMthName(date)} ${date.getDate()}`;
},
],
[
T,
() => {
const { hr, min, sec } = getTime(date);
const { day, mth, year } = getDate(date);
return `${day}/${mth}/${year} ${hr}:${min}:${sec}`;
},
],
])(format);
export const addHours = (oldDate, h) => {
const date = new Date(oldDate);
date.setHours(date.getHours() + h);
return date;
};
export const lastDay = addHours(new Date(), -24);
const getTime = (date) => {
date = new Date(date);
return {
hr: formatTime(date.getHours()),
minutes: formatTime(date.getMinutes()),
sec: formatTime(date.getSeconds()),
};
};
const getDate = (date) => {
date = new Date(date);
return {
mth: formatDate(date.getMonth()),
day: date.getDate(),
year: date.getFullYear(),
};
};
export const getMthName = (date) => monthNames[date.getMonth()];
export const formatDate = (t) => (inc(t) >= 10 ? inc(t) : `0${inc(t)}`);
export const formatTime = (t) => `0${t}`.slice(-2);
export const getDateByType = (date) => {
if (type(date) === "String") return new Date(date.replace(/\s/, "T"));
return new Date(date);
};
export const parseSepareteDate = (
date = new Date(),
symbol = ".",
mode = "DateDMY"
) => {
const selfDate = getDateByType(date);
const dateBits = {
year: selfDate.getFullYear(),
month: selfDate.getMonth() + 1,
day: selfDate.getDate(),
hours: selfDate.getHours(),
minutes: selfDate.getMinutes(),
seconds: selfDate.getSeconds(),
};
Object.keys(dateBits).forEach((key) => {
if (dateBits[key] < 10) dateBits[key] = `0${dateBits[key]}`;
});
switch (mode) {
case "DateDMY":
return dateBits.day + symbol + dateBits.month + symbol + dateBits.year;
case "DateDM":
return dateBits.day + symbol + dateBits.month;
case "DateYMD":
return dateBits.year + symbol + dateBits.month + symbol + dateBits.day;
case "DateDMYHM":
return `${
dateBits.day + symbol + dateBits.month + symbol + dateBits.year
} ${dateBits.hours}:${dateBits.minutes}`;
case "DateDMYHMS":
return `${
dateBits.day + symbol + dateBits.month + symbol + dateBits.year
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
case "DateYMDHM":
return `${
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
} ${dateBits.hours}:${dateBits.minutes}`;
case "DateYMDHMS":
return `${
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
case "DateHM":
return `${dateBits.hours}:${dateBits.minutes}`;
case "DateHMDM":
return `${dateBits.hours}:${dateBits.minutes} ${dateBits.day}${symbol}${dateBits.month}`;
case "DateDataBase":
return `${
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
}T${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
case "DateDataBaseSpace":
return `${
dateBits.year + symbol + dateBits.month + symbol + dateBits.day
} ${dateBits.hours}:${dateBits.minutes}:${dateBits.seconds}`;
default:
return dateBits.day + symbol + dateBits.month + symbol + dateBits.year;
}
};
export const toStartDay = (date = new Date()) => {
const selfDate = getDateByType(date);
return selfDate.setHours(0, 0, 0);
};
export const withTime = (date, time) =>
pipe(
(x) => parseSepareteDate(x, "-", "DateYMD"),
(x) => `${x}T${time}`
)(date);
@@ -0,0 +1,50 @@
<template>
<ButtonModal
text="Выбрать машину"
btnClass="col-span-4"
containerClass="pt-10 px-4 min-w-[80vw]"
>
<Tabulator v-bind="tabulatorOtps" />
</ButtonModal>
</template>
<script>
import ButtonModal from '@molecules/ButtonModal/index.vue'
import Tabulator from "@molecules/Tabulator/index.vue"
export default {
name: 'Name',
components: {ButtonModal, Tabulator},
data() {
},
computed: {
tabulatorOtps() {
return {
dataSource: this.packsData,
columns: [
{
title: "Нормер пакета",
field: "pack_number",
width: 150
},
{
title: "Количество записей",
field: "count",
width: 150
},
{
title: "Последняя дата пакета",
field: "pack_dt",
width: 150,
render: "{{ dt_format (dt_shift pack_dt 3 'hour') }}"
}
],
height: "550px"
}
}
},
methods: {
}
}
</script>
@@ -0,0 +1,223 @@
import { parse } from "handlebars/dist/cjs/handlebars/compiler/base";
import { cond, T, isEmpty } from "ramda";
const setPackColumns = (name) => {
switch(name) {
case 'КолонкиПакетов':
return [
{
title: "Пакет на проде",
field: "switch ",
width: 150,
render: `
<form x-data='{ isProd: {{ isProd }} }' phx-change='update_pack_struct_online:{{ id }}'>
<div class='flex justify-center'>
<div class='form-check form-switch'>
<label for='toogle_group_{{ id }}' class='flex items-center cursor-pointer'>
<input value='true' x-model='isProd' id='toogle_group_{{ id }}' name='toogle_pack_struct_{{ id }}' class='form-check-input appearance-none w-9 rounded-full float-left h-3 align-top bg-white bg-no-repeat bg-contain bg-gray-300 checked:bg-blue-500 focus:outline-none cursor-pointer shadow-sm' type='checkbox' role='switch' id='flexSwitchCheckDefault'>
</label>
</div>
</div>
</form>
`
},
{
title: "Название",
field: "name",
width: 180
},
{
title: "Действия",
field: "actions",
width: 100,
render: `
<div class='flex gap-2 py-1'>
<button style='width: 25px; height: 25px;' class='italic border border-solid transition-all duration-300 rounded-full cursor-pointer hover:text-gray-500 linear-loader card-btn-action pt-[2px] text-primary' phx-click='select_struct:{{ id }}' >
<i class='ri-eye-fill'></i>
</button>
<button style='width: 25px; height: 25px;' class='italic border border-solid transition-all duration-300 rounded-full cursor-pointer hover:text-gray-500 linear-loader card-btn-action'>
<i class='ri-pencil-fill'></i>
</button>
<button style='width: 25px; height: 25px;' class='relative flex items-center justify-center italic border border-solid transition-all duration-300 rounded-full cursor-pointer hover:text-gray-500 linear-loader card-btn-action-red' x-data='{ open: false }' @click='open = !open'>
<div x-show='open' class='popup-text not-italic'>
<span>Вы точно хотите удалить структуру?</span>
<span class='flex gap-2'>
<span class='justify-center flex flex-grow bg-blue-500 hover:bg-blue-700 text-white rounded'>Да</span>
<span class='justify-center flex flex-grow bg-gray-500 hover:bg-gray-700 text-white rounded'>Отмена</span>
</span>
</div>
<i class='ri-delete-bin-fill'></i>
</button>
</div>
`
}
]
case 'Сырые':
return [
{
title: "Время пакета",
field: "pack_dt",
width: 180
},
{
title: "Архивная дата",
field: "arhiv_date",
width: 90
},
{
title: "Номер пакета",
field: "pack_number",
width: 70
},
{
title: "Длинна пакета",
field: "pack_len",
width: 70
},
{
title: "Репл",
field: "id_repl",
width: 70
},
{
title: "IMEI",
field: "imei",
width: 120
},
{
title: "ДАННЫЕ",
field: "data",
width: 600
},
{
title: "Дата начала передачи",
field: "connect_dt",
width: 180
},
{
title: "Дата завершения передачи",
field: "disconnect_dt",
width: 180
}
]
case 'Нераспознанные':
return [
{
title: "Время пакета",
field: "pack_dt",
width: 180
},
{
title: "Архивная дата",
field: "arhiv_date",
width: 90
},
{
title: "Номер пакета",
field: "pack_number",
width: 70
},
{
title: "Длинна пакета",
field: "pack_len",
width: 70
},
{
title: "Репл",
field: "id_repl",
width: 70
},
{
title: "IMEI",
field: "imei",
width: 120
},
{
title: "ДАННЫЕ",
field: "data",
width: 600
},
{
title: "Дата начала передачи",
field: "connect_dt",
width: 180
},
{
title: "Дата завершения передачи",
field: "disconnect_dt",
width: 180
}
]
default: return []
}
}
const prepareSetPackColumns = (name, data) => {
console.log('prepareSetPackColumns data', data);
console.log('prepareSetPackColumns name', name);
switch(name) {
case 'askr_logs':
if (data.length > 0) {
return data[0].map((el) => ({title: Object.keys(el)[0], field: Object.keys(el)[0], width: 150}))
}
if (data.length === 0) {
return []
}
case 'raw':
return setPackColumns('Сырые')
case 'unrecognized':
return setPackColumns('Нераспознанные')
default: return []
}
}
const normalizeCols = (cols) => {
console.log('normalizeCols - cols', cols)
const withoutCoordsPackDtCols = cols.filter((el) => el.field !== 'pack_dt' && el.field !== 'lat_lon')
const coordsField = {
title: "Координаты",
field: "lat_lon",
key: "coords",
render: `(_, rec) => {
if (!rec?.geo) return ""
const lat = rec?.geo?.coordinates[1]
const lon = rec?.geo?.coordinates[0]
return \`<div class="underline underline-offset-2 decoration-dashed cursor-pointer" phx-click="load_map" phx-value-lon="\${lat}" phx-value-lat="\${lon}">
\${lat ? +lat.toFixed(5) : lat} \${lon ? +lon.toFixed(5) : lon}
</div>\`
}`
}
const packDtField = {
title: "Дата формирования пакета",
field: "pack_dt",
key: "pack_dt",
render: `{{ dt_format (dt_shift pack_dt 3 'hour') }}`,
width: 130
}
return [packDtField, coordsField, ...withoutCoordsPackDtCols]
}
const parseCols = (packStructure, report) => {
if (!isEmpty(packStructure)) return normalizeCols(packStructure)
if (!packStructure && report && !isEmpty(report)) return prepareSetPackColumns('askr_logs', report.askr_logs)
if (!packStructure && !report) return []
}
const fetchColumn = (packStructure, report, id = null) => {
console.log('packStructure', packStructure)
console.log('id', id)
const getCurrentPackColls = (packStructure, id) => {
const currentPackStructure = packStructure.find((el) => el.id === id)
console.log('currentPackStructure', currentPackStructure)
const currColumns = currentPackStructure.columns ? currentPackStructure.columns : []
return parseCols(currColumns, null)
}
return cond([
[(packStructure) => !packStructure === true, () => parseCols(null, report)],
[(packStructure) => (packStructure?.length >= 1) && (id && typeof id === 'number'), () => getCurrentPackColls(packStructure, id)],
[(packStructure) => packStructure?.length >= 1, () => parseCols(packStructure[0].columns, null)],
[T, () => parseCols(null, report)],
])(packStructure)
}
export {prepareSetPackColumns, setPackColumns, normalizeCols, parseCols, fetchColumn}
@@ -0,0 +1,58 @@
<!-- eslint-disable -->
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
export default {
name: 'PackViewButton',
components: {
},
props: {
active: {
type: Boolean,
default: false
},
mode: {
type: String,
default: ""
},
name: {
type: String,
default: ""
},
inner: {
type: String,
default: ""
},
labelClass: {
type: String,
default: ""
},
idx: {
type: Number,
default: ""
}
},
computed: {
...mapGetters('machines', ['selectedPackMode']),
},
methods: {
...mapActions('machines', ['updateModePack']),
}
}
</script>
<!-- eslint-disable -->
<template>
{{ console.log('selectedPackMode', selectedPackMode)
}}
<button @click="updateModePack(mode)" v-bind:key="idx" type="button" class="w-full">
<input name="switchTableMode" type="radio" :id="name" :value="name" :checked="active" class="hidden peer" required="">
<label :for="name" :class="labelClass" class="transition-all w-full border-slate-600 w-full block cursor-pointer py-1 text-ssm sm:text-sm hover:bg-slate-700 hover:text-white text-slate-600 border-slate-600
peer-checked:bg-slate-600 peer-checked:text-white">
{{ inner }}
</label>
</button>
</template>
@@ -0,0 +1,123 @@
<!-- eslint-disable -->
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import PackViewButton from './Button.vue'
import Spinner from "@molecules/Spinner/index.vue"
import Tabulator from "@molecules/Tabulator/index.vue"
import { isEmpty } from "ramda";
export default {
name: 'PackView',
components: {
PackViewButton,
Spinner,
Tabulator
},
props: {
isLoader: {
type: Boolean,
default: false
},
id: {
type: String,
default: ""
},
},
data () {
return {
hasData: false
}
},
setup (props, {slots}) {
const hasDescription = slots.description != undefined
const hasError = slots.error != undefined
console.log('slots', slots)
return {
hasDescription,
hasError
}
},
updated () {
this.$nextTick(function () {
if (!isEmpty(this.packData)) {
this.hasData = true
}
})
},
computed: {
...mapGetters('machines', ['packData', 'selectPackStructs', 'selectedPackStruct']),
buttonsData: () =>
([
{mode: 'askr_logs', isActive: true, name: 'switch_askr_logs', inner: 'обработанные', labelClass: 'rounded-l-lg border'},
{mode: 'raw', isActive: false, name: 'switch_raw', inner: 'сырые', labelClass: 'border-y'},
{mode: 'unrecognized', isActive: false, name: 'switch_unrecognized', inner: 'нераспознанные', labelClass: 'rounded-r-lg border'},
])
},
methods: {
...mapActions('machines', ['updateSelectStruct']),
updateSelectedValue: function (e) {
console.log('e', e.target.value)
this.updateSelectStruct(Number(e.target.value))
},
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="w-full relative">
{{
console.log('packData', packData)
}}
<div v-if="!hasData" class="w-full h-[50px] rounded-xl bg-slate-200 hidden" :id="`${id}_loader`">
<Spinner/>
</div>
<div v-if="hasData" :id="id" class="w-full rounded-md bg-light border-rounded p-4">
<div class="grid grid-cols-12 mb-6">
<div class="flex flex-col gap-1 col-span-12 sm:col-span-4 md:col-span-5 lg:col-span-5 2xl:col-span-7 sm:mb-0">
<h2 :id="`${id}_pack_name`" class=" text-lg">Пакет: <span v-if="selectPackStructs" >{{selectPackStructs[0]?.packNumber}}</span></h2>
<div v-if="hasDescription" class="text-xs text-slate-500">
<slot name="description" />
</div>
</div>
<!-- <form phx-change="c_packs_mode:on_click" :id="`${id}_table_active`" class="col-span-12 sm:col-span-8 md:col-span-7 lg:col-span-7 2xl:col-span-5 flex justify-center hidden"> -->
<div class="col-span-12 sm:col-span-8 md:col-span-7 lg:col-span-7 2xl:col-span-5 flex justify-center">
<PackViewButton v-for="(button, idx) in buttonsData"
:idx="idx"
:mode="button.mode"
:name="button.name"
:active="button.isActive"
:inner="button.inner"
:labelClass="button.labelClass"
/>
</div>
<!-- </form> -->
</div>
<div class="relative" :id="id">
<div id="selectStructContainer" class="flex gap-2 items-center mb-2">
<span>Выберите структуру:</span>
<select v-if="selectPackStructs" id="selectStruct" name="struct" v-bind:value="selectedPackStruct.id" v-on:change="updateSelectedValue" class=" w-fit bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" style="--tw-ring-color:focus: none" >
<option :key={idx} v-for="(el, idx) in selectPackStructs" :value="el.id">
{{ el.name }}
</option>
</select>
</div>
<div :id="`${id}_report`" class="tab-pane active bg-light relative shadow text-center min-h-[300px] relative rounded-md w-full relative">
<Tabulator v-bind="packData" />
<div v-if="isLoader" class="w-full h-[50px] rounded-xl bg-slate-200 hidden" :id="`${id}_loader`">
<Spinner/>
</div>
</div>
</div>
</div>
<div v-if="hasError" :id="`${id}_error`" class="w-full rounded-md bg-light border-rounded p-4 flex flex-col gap-5 items-center justify-center hidden">
<slot name="error" />
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,10 @@
<script setup>
</script>
<template>
<div class="w-full pl-2 flex items-center h-[40px] border-b border-slate-200">
<div>
Hi!
</div>
</div>
</template>
@@ -0,0 +1,47 @@
<script>
export default {
name: 'BreadCrumbs',
components: {},
props: {
breadcrumbs: {
type: Array,
default: null
},
},
data() {
},
computed: {
},
methods: {
rkey() {
return Math.random().toString(36).slice(4)
}
}
}
</script>
<template>
<!-- BEGIN: Breadcrumb -->
<nav
v-if="breadcrumbs"
aria-label="breadcrumb"
class="-intro-x h-[45px] mr-auto"
>
<ol class="breadcrumb breadcrumb-light">
<li
v-for="{name, link, active} in breadcrumbs"
:key="rkey(name)"
:class="{'breadcrumb-item active': active, 'breadcrumb-item': !active}"
aria-current="page"
>
<a
:href="link"
>{{ name }}</a>
</li>
</ol>
</nav>
<!-- END: Breadcrumb -->
<div class="flex grow w-full border-hide-600" />
</template>
@@ -0,0 +1,43 @@
<script>
export default {
name: 'LeftPanel',
components: {},
data() {
return {
mobile_menu_opened: false
}
},
computed: {
},
methods: {
}
}
</script>
<template>
<div class="flex items-center border-white/[0.2] border-r">
<div
class="flex p-4 cursor-pointer flex md:hidden"
@click="mobile_menu_opened = !mobile_menu_opened"
>
<i
icon="bar-chart-2"
class="w-6 h-6 text-white transform rotate-90"
/>
</div>
<router-link
class="items-center relative -intro-x flex cursor-pointer p-4 hidden md:flex"
to="/?page=machines&mode=cards"
>
<div
class="text-white mx-4"
style="font-weight: 600;"
>
LiveMonitor
</div>
</router-link>
<div class="w-[15px]" />
</div>
</template>
@@ -0,0 +1,75 @@
<script>
import MenuList from './MenuList.vue'
import MobileMenu from './MobileMenu.vue'
import LeftPanel from './LeftPanel.vue'
import Breadcrumbs from './Breadcrumbs.vue'
import RightPanel from './RightPanel.vue'
import {mapGetters} from 'vuex'
export default {
name: 'MenuContainer',
components: {
MobileMenu,
MenuList,
LeftPanel,
Breadcrumbs,
RightPanel
},
data() {
},
computed: {
...mapGetters('layout', ["show_menu", 'is_enabled_menu']),
...mapGetters('auth', ['menuList']),
},
methods: {
}
}
</script>
<template>
<div class="main w-full">
<!-- BEGIN: TOP BAR -->
<div
id="menu"
class="top-bar-boxed w-full h-[59px] mt-[4px] z-[51] relative justify-center border-b border-white/[0.2] -mx-3 px-3 sm:pl-8 md:pt-0 mb-[4px]"
>
<MobileMenu
v-if="false"
current_menu_item=""
current_user=""
:menu_list="menuList"
/>
<div class="w-full">
<div class="flex justify-between items-center">
<LeftPanel />
<Breadcrumbs
v-if="false"
:breadcrumbs="[]"
/>
<RightPanel
:current_user="''"
:page_title="''"
:notifications="[{from: 'Бот из LM', datetime: '10:00', message: 'Проведен редизайн приложения', readed: false}]"
/>
</div>
<div class="sm:hidden flex items-center justify-between" />
</div>
</div>
<!-- END: TOP BAR -->
<div class="wrapper">
<div class="wrapper-box">
<div v-if="show_menu && is_enabled_menu">
<MenuList
:menu_list="menuList"
/>
</div>
<div class="content">
<div class="grid grid-cols-12 gap-6 h-full">
<slot />
</div>
</div>
</div>
</div>
</div>
</template>
@@ -0,0 +1,119 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import { cond, equals, T, always } from 'ramda'
import { createIcons, icons } from "lucide";
import MenuItemGroup from './MenuItemGroup.vue'
export default {
name: 'MenuItem',
components: {MenuItemGroup},
props: {
type: {
type: String,
default: null
},
menu_item: {
type: Object,
default: () => ({
link: "/"
})
},
base_class: {
type: String,
default: ""
},
routeName: {
type: String,
default: ""
}
},
data() {
},
computed: {
_base_class() {
return `${this.base_class}`
},
activeClass () {
return 'side-menu side-menu--active'
},
menuRouterName() {
return this.menu_item.router || ""
},
currentRouterName() {
return this.$router.currentRoute._value.name || ""
},
linkClass() {
const menuRouterName = this.menu_item.router || ""
if (menuRouterName === this.routeName)
return `${this._base_class} ${this._base_class}--active`
return this._base_class
},
_type() {
return cond([
[equals("simple_link"), (x) => x],
[equals("line"), (x) => x],
[equals("group"), (x) => x],
[T, () => "other"],
])(this.type)
}
},
mounted() {
this.icons()
},
updated() {
console.log(123)
this.icons()
},
methods: {
icons() {
createIcons({ icons, "stroke-width": 1.5, nameAttr: "icon" })
}
}
}
</script>
<template>
<li v-if="_type == 'simple_link'">
<router-link
phx-hook="MenuLink"
:class="linkClass"
:to="menu_item.link"
>
<div :class="`${_base_class}__icon`">
<i :icon="menu_item?.icon" />
</div>
<div :class="`${_base_class}__title`">
{{ menu_item.show }}
</div>
</router-link>
</li>
<li v-if="_type == 'line'">
<hr class="m-4">
</li>
<MenuItemGroup
v-if="_type == 'group'"
:type="_type"
:base_class="base_class"
:menu_item="menu_item"
/>
<div v-if="_type == 'other'">
""
</div>
</template>
<!-- <router-link
v-if="type === 'link'"
class="flex p-2 rounded-md hover:bg-gray-100 flex items-center"
:class="{'bg-gray-200': pageName === name}"
:to="link"
>
<i :class="`ri-${icon} text-slate-600 mr-2 `" />
<span
v-if="isOpenMenu"
class="text-slate-700"
>{{ title }}</span>
</router-link> -->
@@ -0,0 +1,93 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import { cond, equals, T } from 'ramda'
import { createIcons, icons } from "lucide";
export default {
name: 'MenuItemCopy',
components: {},
props: {
type: {
type: String,
default: null
},
menu_item: {
type: Object,
default: () => ({
link: "/"
})
},
base_class: {
type: String,
default: ""
}
},
data() {
},
computed: {
_base_class() {
return `${this.base_class}`
},
activeClass () {
return 'side-menu side-menu--active'
},
menuRouterName() {
return this.menu_item.router || ""
},
currentRouterName() {
return this.$router.currentRoute._value.name || ""
},
linkClass() {
const menuRouterName = this.menu_item.router || ""
const currentRouterName = this.$router.currentRoute._value.name || Math.random()
if (menuRouterName === currentRouterName)
return `${this._base_class} ${this._base_class}--active`
return this._base_class
},
_type() {
return cond([
[equals("simple_link"), (x) => x],
[equals("line"), (x) => x],
[equals("group"), (x) => x],
[T, () => "other"],
])(this.type)
}
},
mounted() {
this.icons()
},
updated() {
this.icons()
},
methods: {
icons() {
createIcons({ icons, "stroke-width": 1.5, nameAttr: "icon" })
}
}
}
</script>
<template>
<li v-if="_type == 'simple_link'">
<router-link
phx-hook="MenuLink"
:class="linkClass"
:to="menu_item.link"
>
<div :class="`${_base_class}__icon`">
<i :icon="menu_item?.icon" />
</div>
<div :class="`${_base_class}__title`">
{{ menu_item.show }}
</div>
</router-link>
</li>
<li v-if="_type == 'line'">
<hr class="m-4">
</li>
<div v-if="_type == 'other'">
""
</div>
</template>
@@ -0,0 +1,80 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import MenuItem from './MenuItemCopy.vue'
export default {
name: 'MenuItemGroup',
components: {
MenuItem
},
props: {
type: {
type: String,
default: null
},
base_class: {
type: String,
default: null
},
menu_item: {
type: Object,
default: () => ({})
},
},
data() {
return {
open: false
}
},
computed: {
containerClass() {
const menuRouterName = this.menu_item.router || ""
const currentRouterName = this.$router.currentRoute._value.name || Math.random()
if (menuRouterName === currentRouterName) return `${this.base_class} ${this.base_class}--active`
return `${this.base_class}`
},
itemClass() {
if (this.open) return `${this.base_class}__sub-icon transform rotate-180`
return `${this.base_class}__sub-icon`
},
listClass() {
if (this.open) return `${this.base_class}__sub-open`
return ""
},
menu_list_with_index() {
return this.menu_item.children.map((el, index) => ({...el, index}))
}
},
methods: {
toggle() {
this.open = !this.open
}
}
}
</script>
<template>
<li>
<span
:class="containerClass"
@click="toggle"
>
<div :class="`${base_class}__icon`"> <i :icon="menu_item.icon" /> </div>
<div :class="`${base_class}__title`">
{{ menu_item.show }}
<div :class="itemClass"> <i icon="chevron-down" /> </div>
</div>
</span>
<ul :class="listClass">
<MenuItem
v-for="item in menu_item.children"
:key="JSON.stringify(item)"
:base_class="base_class"
:type="item.type"
:menu_item="item"
/>
</ul>
</li>
</template>
@@ -0,0 +1,63 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import {mapGetters} from 'vuex'
import MenuItem from './MenuItem.vue'
export default {
name: 'MenuList',
components: {MenuItem},
props: {
current_menu_item: {
type: String,
default: ""
},
menu_list: {
type: Array,
default: () => []
}
},
data() {
return {
routeName: ""
}
},
computed: {
...mapGetters('', ['mobile_menu_opened', 'show_menu']),
menu_list_with_index() {
return this.menu_list.map((el, index) => ({...el, index}))
}
},
watch:{
$route (to){
this.routeName = to.name;
}
},
methods: {
rkey() {
return Math.random().toString(36).slice(4)
}
}
}
</script>
<template>
<nav
x-show="show_menu"
class="side-nav"
>
<ul>
<MenuItem
v-for="{index, ...menu_item} in menu_list_with_index"
:id="`main_menu_item_component_${index}`"
:key="rkey(index)"
base_class="side-menu"
:type="menu_item.type"
:menu_item="menu_item"
:current_menu_item="current_menu_item"
:routeName="routeName"
/>
</ul>
</nav>
</template>
@@ -0,0 +1,63 @@
<script>
import MenuItem from './MenuItem.vue'
export default {
name: 'MobileMenu',
components: {
MenuItem
},
props: {
// eslint-disable-next-line vue/prop-name-casing
current_menu_item: {
type: String,
default: ""
},
// eslint-disable-next-line vue/prop-name-casing
menu_list: {
type: Array,
default: () => []
}
},
data() {
},
computed: {
menu_list_with_index() {
return this.menu_list.map((el, index) => ({...el, index}))
}
},
methods: {
rkey() {
return Math.random().toString(36).slice(4)
}
}
}
</script>
<template>
<div x-bind:class="mobile_menu_opened ? 'mobile-menu--active mobile-menu md:hidden' : 'mobile-menu md:hidden'">
<div class="scrollable">
<span
class="mobile-menu-toggler"
@click="mobile_menu_opened = !mobile_menu_opened"
>
<i
icon="x-circle"
class="w-8 h-8 text-white transform -rotate-90"
/>
</span>
<ul class="scrollable__content py-2">
<MenuItem
v-for="{index, ...menu_item} in menu_list_with_index"
:id="`main_menu_item_component_${index}`"
:key="rkey(index)"
base_class="menu"
:type="menu_item.type"
:menu_item="menu_item"
:current_menu_item="current_menu_item"
/>
</ul>
</div>
</div>
</template>
@@ -0,0 +1,174 @@
<!-- eslint-disable vue/prop-name-casing -->
<script>
import {mapGetters, mapMutations} from 'vuex'
import { createIcons, icons } from "lucide";
export default {
name: 'RightPanel',
components: {},
props: {
// eslint-disable-next-line vue/prop-name-casing
page_title: {
type: String,
default: ""
}
},
data() {
return {
}
},
computed: {
...mapGetters('auth', ['current_user'])
},
mounted() {
this.icons()
},
updated() {
this.icons()
},
methods: {
...mapMutations('layout', ["toggle_menu"]),
icons() {
createIcons({ icons, "stroke-width": 1.5, nameAttr: "icon" })
},
rkey() {
return Math.random().toString(36).slice(4)
},
closeDropDown() {
setTimeout(() => {
document.querySelector("body").click()
}, 1);
}
},
}
</script>
<template>
<div class="flex items-center justify-end grow gapy-2 px-4 border-hide-600">
<div class="">
{{ page_title }}
</div>
<div class="flex">
<div class="relative">
<div class="intro-x dropdown">
<div
class="dropdown-toggle notification notification--bullet cursor-pointer"
role="button"
aria-expanded="false"
data-tw-toggle="dropdown"
>
<i
icon="bell"
class="cursor-pointer border-white/[0.2] border-r border-l p-4 text-white"
style="width: 55px; height: 55px;"
/>
<div
style="font-size: 7px; font-weight: 900; top: 10px; left: 30px"
class="h-[13px] w-[13px] bg-white rounded-full mr-8 transition absolute flex itens-center justify-center"
>
<span
style="line-height: 13px; font-weight: 900; font-size: 7px"
class="text-black"
>0</span>
</div>
</div>
<div class="notification-content pt-2 dropdown-menu">
<div class="notification-content__box dropdown-content">
<div class="notification-content__title">
Оповещения
</div>
<div
v-for="{from, datetime, message, readed} in notifications"
:key="rkey(message)"
class="cursor-pointer relative flex items-center "
>
<div :class="{'bg-gray-400 w-3 h-3 absolute right-0 bottom-0 rounded-full border-2 border-white dark:border-darkmode-600|, else: ~s|bg-success w-3 h-3 absolute right-0 bottom-0 rounded-full border-2 border-white dark:border-darkmode-600': readed}" />
<div class="ml-2 overflow-hidden">
<div class="flex items-center">
<a
href="javascript:;"
class="font-medium truncate mr-5"
>{{ from }}</a>
<div class="text-xs text-slate-400 ml-auto whitespace-nowrap">
{{ datetime }}
</div>
</div>
<div class="w-full truncate text-slate-500 mt-0.5">
{{ message }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="cursor-pointer border-white/[0.2] border-r p-4">
<i
id="menu_book_icon"
icon="book"
class="text-white w-[24px] h-[24px]"
/>
</div>
<div class="cursor-pointer border-white/[0.2] border-r p-4">
<i
id="menu_question_icon"
icon="help-circle"
class="text-white w-[24px] h-[24px]"
/>
</div>
<div
class="cursor-pointer hidden md:flex border-white/[0.2] border-r p-4"
@click="toggle_menu"
>
<i
id="menu_library"
icon="library"
class="text-white w-[24px] h-[24px]"
/>
</div>
</div>
<!-- BEGIN: Account Menu -->
<div class="intro-x dropdown w-[50px] h-[50px] px-3 flex justify-center items-center">
<div
class="dropdown-toggle rounded-full overflow-hidden shadow-lg image-fit zoom-in scale-110"
role="button"
aria-expanded="false"
data-tw-toggle="dropdown"
>
<img
alt="image"
src="/images/profile-4.jpg"
>
</div>
<div class="dropdown-menu w-56">
<ul class="dropdown-content before:block before:absolute before:bg-black before:inset-0 before:rounded-md before:z-[-1]">
<li class="p-2">
<div class="font-bold">
{{ current_user?.first_name || "" }} {{ current_user?.last_name || "" }}
</div>
<div
class="text-xs text-black/60 dark:text-slate-500"
style="margin-top: -0.25rem;"
>
{{ current_user?.position || "позиция не указана" }}
</div>
</li>
<li>
<hr class="dropdown-divider border-white/[0.08]">
</li>
<li @click="closeDropDown">
<router-link
to="/users/log_out"
class="dropdown-item hover:bg-white/5 hover:text-black"
>
Выйти
</router-link>
</li>
</ul>
</div>
</div>
<!-- END: Account Menu -->
</div>
</template>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,139 @@
<template>
<div
class="h-[100vh] z-40 bg-white flex flex-col px-4 pt-8 border-r border-slate-200 transition-all"
:class="{'w-[300px]': isOpenMenu, 'w-[70px]': !isOpenMenu}"
>
<div class="pl-1 flex items-center mb-8 justify-between">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--logos"
width="31.88"
height="32"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 256 257"
>
<defs><linearGradient
id="IconifyId1813088fe1fbc01fb466"
x1="-.828%"
x2="57.636%"
y1="7.652%"
y2="78.411%"
><stop
offset="0%"
stop-color="#41D1FF"
/><stop
offset="100%"
stop-color="#BD34FE"
/></linearGradient><linearGradient
id="IconifyId1813088fe1fbc01fb467"
x1="43.376%"
x2="50.316%"
y1="2.242%"
y2="89.03%"
><stop
offset="0%"
stop-color="#FFEA83"
/><stop
offset="8.333%"
stop-color="#FFDD35"
/><stop
offset="100%"
stop-color="#FFA800"
/></linearGradient></defs><path
fill="url(#IconifyId1813088fe1fbc01fb466)"
d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"
/><path
fill="url(#IconifyId1813088fe1fbc01fb467)"
d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"
/>
</svg>
<div
class="p-1 px-2 rounded-md transition-all cursor-pointer hover:bg-gray-200"
:class="{'rotate-90': !isOpenMenu, '-rotate-90': isOpenMenu}"
@click="toggleMenu"
>
<i class="ri-upload-line" />
</div>
</div>
<div class="flex flex-col">
<div
v-for="{title, icon, link, type, name} in menu"
:key="`${title}${name}`"
>
<router-link
v-if="type === 'link'"
class="flex p-2 rounded-md hover:bg-gray-100 flex items-center"
:class="{'bg-gray-200': pageName === name}"
:to="link"
>
<i :class="`ri-${icon} text-slate-600 mr-2 `" />
<span
v-if="isOpenMenu"
class="text-slate-700"
>{{ title }}</span>
</router-link>
<div
v-if="type === 'line'"
class="border-b my-4 border-slate-200"
/>
</div>
</div>
</div>
</template>
<script>
import {mapGetters, mapMutations} from "vuex";
export default {
name: 'App',
components: {},
data() {
const menu = [
{
title: "Главная",
icon: "home-4-line",
link: "/",
type: "link",
name: "main"
},
{
title: "Главная1",
icon: "home-4-line",
link: "/page1",
type: "link",
name: "page1"
},
{
title: "Главная2",
icon: "home-4-line",
link: "/page2",
type: "link",
name: "page2"
},
{
title: "Выход",
icon: "logout-circle-line",
link: "/auth",
type: "link",
name: ""
},
]
return {
menu
}
},
computed: {
...mapGetters('layout', ['isOpenMenu']),
pageName() {
return this.$route.name
}
},
methods: {
...mapMutations('layout', ['toggleMenu']),
}
}
</script>
@@ -0,0 +1,982 @@
<template>
<svg
width="1008"
height="362"
viewBox="0 0 1008 362"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g
clip-path="url(#clip0_114_2)"
filter="url(#filter0_d_114_2)"
>
<path
d="M4 0H1004V203.1H4V0Z"
fill="url(#paint0_linear_114_2)"
/>
<path
d="M4 200.7H1004V354H4V200.7Z"
fill="url(#paint1_linear_114_2)"
/>
<path
d="M4 273.4H1004V354H4V273.4Z"
fill="url(#paint2_linear_114_2)"
/>
<path
d="M4 275.2C4 275.2 87.8 247.2 184 247.2C280.2 247.2 381 275.2 381 275.2H4Z"
fill="#C0CBDD"
/>
<path
d="M381 275.2C381 275.2 435.7 247.2 498.5 247.2C561.3 247.2 627.1 275.2 627.1 275.2H381Z"
fill="#C0CBDD"
/>
<path
d="M627.2 275.2C627.2 275.2 710.9 247.2 807.1 247.2C903.3 247.2 1004 275.2 1004 275.2H627.2Z"
fill="#C0CBDD"
/>
<path
d="M-370.8 275.2C-370.8 275.2 -287.1 247.2 -190.9 247.2C-94.7 247.2 6 275.2 6 275.2H-370.8Z"
fill="#C0CBDD"
/>
<path
d="M13.8 282.4H10.8L4 307.6H7L13.8 282.4Z"
fill="#5C687A"
/>
<path
d="M23.8 282.4H20.8L14 307.6H17L23.8 282.4Z"
fill="#5C687A"
/>
<path
d="M33.8 282.4H30.8L24 307.6H27L33.8 282.4Z"
fill="#5C687A"
/>
<path
d="M43.8 282.4H40.8L34 307.6H37L43.8 282.4Z"
fill="#5C687A"
/>
<path
d="M53.8 282.4H50.8L44 307.6H47L53.8 282.4Z"
fill="#5C687A"
/>
<path
d="M63.8 282.4H60.8L54 307.6H57L63.8 282.4Z"
fill="#5C687A"
/>
<path
d="M73.8 282.4H70.8L64 307.6H67L73.8 282.4Z"
fill="#5C687A"
/>
<path
d="M83.8 282.4H80.8L74 307.6H77L83.8 282.4Z"
fill="#5C687A"
/>
<path
d="M93.8 282.4H90.8L84 307.6H87L93.8 282.4Z"
fill="#5C687A"
/>
<path
d="M103.8 282.4H100.8L94 307.6H97L103.8 282.4Z"
fill="#5C687A"
/>
<path
d="M113.8 282.4H110.8L104 307.6H107L113.8 282.4Z"
fill="#5C687A"
/>
<path
d="M123.8 282.4H120.8L114 307.6H117L123.8 282.4Z"
fill="#5C687A"
/>
<path
d="M133.8 282.4H130.8L124 307.6H127L133.8 282.4Z"
fill="#5C687A"
/>
<path
d="M143.8 282.4H140.8L134 307.6H137L143.8 282.4Z"
fill="#5C687A"
/>
<path
d="M153.8 282.4H150.8L144 307.6H147L153.8 282.4Z"
fill="#5C687A"
/>
<path
d="M163.8 282.4H160.8L154 307.6H157L163.8 282.4Z"
fill="#5C687A"
/>
<path
d="M173.8 282.4H170.8L164 307.6H167L173.8 282.4Z"
fill="#5C687A"
/>
<path
d="M183.8 282.4H180.8L174 307.6H177L183.8 282.4Z"
fill="#5C687A"
/>
<path
d="M193.8 282.4H190.8L184 307.6H187L193.8 282.4Z"
fill="#5C687A"
/>
<path
d="M203.8 282.4H200.8L194 307.6H197L203.8 282.4Z"
fill="#5C687A"
/>
<path
d="M213.8 282.4H210.8L204 307.6H207L213.8 282.4Z"
fill="#5C687A"
/>
<path
d="M223.8 282.4H220.8L214 307.6H217L223.8 282.4Z"
fill="#5C687A"
/>
<path
d="M233.8 282.4H230.8L224 307.6H227L233.8 282.4Z"
fill="#5C687A"
/>
<path
d="M243.8 282.4H240.8L234 307.6H237L243.8 282.4Z"
fill="#5C687A"
/>
<path
d="M253.8 282.4H250.8L244 307.6H247L253.8 282.4Z"
fill="#5C687A"
/>
<path
d="M263.8 282.4H260.8L254 307.6H257L263.8 282.4Z"
fill="#5C687A"
/>
<path
d="M273.8 282.4H270.8L264 307.6H267L273.8 282.4Z"
fill="#5C687A"
/>
<path
d="M283.8 282.4H280.8L274 307.6H277L283.8 282.4Z"
fill="#5C687A"
/>
<path
d="M293.8 282.4H290.8L284 307.6H287L293.8 282.4Z"
fill="#5C687A"
/>
<path
d="M303.8 282.4H300.8L294 307.6H297L303.8 282.4Z"
fill="#5C687A"
/>
<path
d="M313.8 282.4H310.8L304 307.6H307L313.8 282.4Z"
fill="#5C687A"
/>
<path
d="M323.8 282.4H320.8L314 307.6H317L323.8 282.4Z"
fill="#5C687A"
/>
<path
d="M333.8 282.4H330.8L324 307.6H327L333.8 282.4Z"
fill="#5C687A"
/>
<path
d="M343.8 282.4H340.8L334 307.6H337L343.8 282.4Z"
fill="#5C687A"
/>
<path
d="M353.8 282.4H350.8L344 307.6H347L353.8 282.4Z"
fill="#5C687A"
/>
<path
d="M363.8 282.4H360.8L354 307.6H357L363.8 282.4Z"
fill="#5C687A"
/>
<path
d="M373.8 282.4H370.8L364 307.6H367L373.8 282.4Z"
fill="#5C687A"
/>
<path
d="M383.8 282.4H380.8L374 307.6H377L383.8 282.4Z"
fill="#5C687A"
/>
<path
d="M393.8 282.4H390.8L384 307.6H387L393.8 282.4Z"
fill="#5C687A"
/>
<path
d="M403.8 282.4H400.8L394 307.6H397L403.8 282.4Z"
fill="#5C687A"
/>
<path
d="M413.8 282.4H410.8L404 307.6H407L413.8 282.4Z"
fill="#5C687A"
/>
<path
d="M423.8 282.4H420.8L414 307.6H417L423.8 282.4Z"
fill="#5C687A"
/>
<path
d="M433.8 282.4H430.8L424 307.6H427L433.8 282.4Z"
fill="#5C687A"
/>
<path
d="M443.8 282.4H440.8L434 307.6H437L443.8 282.4Z"
fill="#5C687A"
/>
<path
d="M453.8 282.4H450.8L444 307.6H447L453.8 282.4Z"
fill="#5C687A"
/>
<path
d="M463.8 282.4H460.8L454 307.6H457L463.8 282.4Z"
fill="#5C687A"
/>
<path
d="M473.8 282.4H470.8L464 307.6H467L473.8 282.4Z"
fill="#5C687A"
/>
<path
d="M483.8 282.4H480.8L474 307.6H477L483.8 282.4Z"
fill="#5C687A"
/>
<path
d="M493.8 282.4H490.8L484 307.6H487L493.8 282.4Z"
fill="#5C687A"
/>
<path
d="M503.8 282.4H500.8L494 307.6H497L503.8 282.4Z"
fill="#5C687A"
/>
<path
d="M1004 282.4H1001L994.2 307.6H997.2L1004 282.4Z"
fill="#5C687A"
/>
<path
d="M994 282.4H991L984.2 307.6H987.2L994 282.4Z"
fill="#5C687A"
/>
<path
d="M984 282.4H981L974.2 307.6H977.2L984 282.4Z"
fill="#5C687A"
/>
<path
d="M974 282.4H971L964.2 307.6H967.2L974 282.4Z"
fill="#5C687A"
/>
<path
d="M964 282.4H961L954.2 307.6H957.2L964 282.4Z"
fill="#5C687A"
/>
<path
d="M954 282.4H951L944.2 307.6H947.2L954 282.4Z"
fill="#5C687A"
/>
<path
d="M944 282.4H941L934.2 307.6H937.2L944 282.4Z"
fill="#5C687A"
/>
<path
d="M934 282.4H931L924.2 307.6H927.2L934 282.4Z"
fill="#5C687A"
/>
<path
d="M924 282.4H921L914.2 307.6H917.2L924 282.4Z"
fill="#5C687A"
/>
<path
d="M914 282.4H911L904.2 307.6H907.2L914 282.4Z"
fill="#5C687A"
/>
<path
d="M904 282.4H901L894.2 307.6H897.2L904 282.4Z"
fill="#5C687A"
/>
<path
d="M894 282.4H891L884.2 307.6H887.2L894 282.4Z"
fill="#5C687A"
/>
<path
d="M884 282.4H881L874.2 307.6H877.2L884 282.4Z"
fill="#5C687A"
/>
<path
d="M874 282.4H871L864.2 307.6H867.2L874 282.4Z"
fill="#5C687A"
/>
<path
d="M864 282.4H861L854.2 307.6H857.2L864 282.4Z"
fill="#5C687A"
/>
<path
d="M854 282.4H851L844.2 307.6H847.2L854 282.4Z"
fill="#5C687A"
/>
<path
d="M844 282.4H841L834.2 307.6H837.2L844 282.4Z"
fill="#5C687A"
/>
<path
d="M834 282.4H831L824.2 307.6H827.2L834 282.4Z"
fill="#5C687A"
/>
<path
d="M824 282.4H821L814.2 307.6H817.2L824 282.4Z"
fill="#5C687A"
/>
<path
d="M814 282.4H811L804.2 307.6H807.2L814 282.4Z"
fill="#5C687A"
/>
<path
d="M804 282.4H801L794.2 307.6H797.2L804 282.4Z"
fill="#5C687A"
/>
<path
d="M794 282.4H791L784.2 307.6H787.2L794 282.4Z"
fill="#5C687A"
/>
<path
d="M784 282.4H781L774.2 307.6H777.2L784 282.4Z"
fill="#5C687A"
/>
<path
d="M774 282.4H771L764.2 307.6H767.2L774 282.4Z"
fill="#5C687A"
/>
<path
d="M764 282.4H761L754.2 307.6H757.2L764 282.4Z"
fill="#5C687A"
/>
<path
d="M754 282.4H751L744.2 307.6H747.2L754 282.4Z"
fill="#5C687A"
/>
<path
d="M744 282.4H741L734.2 307.6H737.2L744 282.4Z"
fill="#5C687A"
/>
<path
d="M734 282.4H731L724.2 307.6H727.2L734 282.4Z"
fill="#5C687A"
/>
<path
d="M724 282.4H721L714.2 307.6H717.2L724 282.4Z"
fill="#5C687A"
/>
<path
d="M714 282.4H711L704.2 307.6H707.2L714 282.4Z"
fill="#5C687A"
/>
<path
d="M704 282.4H701L694.2 307.6H697.2L704 282.4Z"
fill="#5C687A"
/>
<path
d="M694 282.4H691L684.2 307.6H687.2L694 282.4Z"
fill="#5C687A"
/>
<path
d="M684 282.4H681L674.2 307.6H677.2L684 282.4Z"
fill="#5C687A"
/>
<path
d="M674 282.4H671L664.2 307.6H667.2L674 282.4Z"
fill="#5C687A"
/>
<path
d="M664 282.4H661L654.2 307.6H657.2L664 282.4Z"
fill="#5C687A"
/>
<path
d="M654 282.4H651L644.2 307.6H647.2L654 282.4Z"
fill="#5C687A"
/>
<path
d="M644 282.4H641L634.2 307.6H637.2L644 282.4Z"
fill="#5C687A"
/>
<path
d="M634 282.4H631L624.2 307.6H627.2L634 282.4Z"
fill="#5C687A"
/>
<path
d="M624 282.4H621L614.2 307.6H617.2L624 282.4Z"
fill="#5C687A"
/>
<path
d="M614 282.4H611L604.2 307.6H607.2L614 282.4Z"
fill="#5C687A"
/>
<path
d="M604 282.4H601L594.2 307.6H597.2L604 282.4Z"
fill="#5C687A"
/>
<path
d="M594 282.4H591L584.2 307.6H587.2L594 282.4Z"
fill="#5C687A"
/>
<path
d="M584 282.4H581L574.2 307.6H577.2L584 282.4Z"
fill="#5C687A"
/>
<path
d="M574 282.4H571L564.2 307.6H567.2L574 282.4Z"
fill="#5C687A"
/>
<path
d="M564 282.4H561L554.2 307.6H557.2L564 282.4Z"
fill="#5C687A"
/>
<path
d="M504 282.4H501L494.2 307.6H497.2L504 282.4Z"
fill="#5C687A"
/>
<path
d="M494 282.4H491L484.2 307.6H487.2L494 282.4Z"
fill="#5C687A"
/>
<path
d="M484 282.4H481L474.2 307.6H477.2L484 282.4Z"
fill="#5C687A"
/>
<path
d="M474 282.4H471L464.2 307.6H467.2L474 282.4Z"
fill="#5C687A"
/>
<path
d="M464 282.4H461L454.2 307.6H457.2L464 282.4Z"
fill="#5C687A"
/>
<path
d="M454 282.4H451L444.2 307.6H447.2L454 282.4Z"
fill="#5C687A"
/>
<path
d="M444 282.4H441L434.2 307.6H437.2L444 282.4Z"
fill="#5C687A"
/>
<path
d="M434 282.4H431L424.2 307.6H427.2L434 282.4Z"
fill="#5C687A"
/>
<path
d="M424 282.4H421L414.2 307.6H417.2L424 282.4Z"
fill="#5C687A"
/>
<path
d="M414 282.4H411L404.2 307.6H407.2L414 282.4Z"
fill="#5C687A"
/>
<path
d="M404 282.4H401L394.2 307.6H397.2L404 282.4Z"
fill="#5C687A"
/>
<path
d="M394 282.4H391L384.2 307.6H387.2L394 282.4Z"
fill="#5C687A"
/>
<path
d="M384 282.4H381L374.2 307.6H377.2L384 282.4Z"
fill="#5C687A"
/>
<path
d="M374 282.4H371L364.2 307.6H367.2L374 282.4Z"
fill="#5C687A"
/>
<path
d="M364 282.4H361L354.2 307.6H357.2L364 282.4Z"
fill="#5C687A"
/>
<path
d="M354 282.4H351L344.2 307.6H347.2L354 282.4Z"
fill="#5C687A"
/>
<path
d="M344 282.4H341L334.2 307.6H337.2L344 282.4Z"
fill="#5C687A"
/>
<path
d="M334 282.4H331L324.2 307.6H327.2L334 282.4Z"
fill="#5C687A"
/>
<path
d="M324 282.4H321L314.2 307.6H317.2L324 282.4Z"
fill="#5C687A"
/>
<path
d="M314 282.4H311L304.2 307.6H307.2L314 282.4Z"
fill="#5C687A"
/>
<path
d="M304 282.4H301L294.2 307.6H297.2L304 282.4Z"
fill="#5C687A"
/>
<path
d="M294 282.4H291L284.2 307.6H287.2L294 282.4Z"
fill="#5C687A"
/>
<path
d="M284 282.4H281L274.2 307.6H277.2L284 282.4Z"
fill="#5C687A"
/>
<path
d="M274 282.4H271L264.2 307.6H267.2L274 282.4Z"
fill="#5C687A"
/>
<path
d="M264 282.4H261L254.2 307.6H257.2L264 282.4Z"
fill="#5C687A"
/>
<path
d="M254 282.4H251L244.2 307.6H247.2L254 282.4Z"
fill="#5C687A"
/>
<path
d="M244 282.4H241L234.2 307.6H237.2L244 282.4Z"
fill="#5C687A"
/>
<path
d="M234 282.4H231L224.2 307.6H227.2L234 282.4Z"
fill="#5C687A"
/>
<path
d="M224 282.4H221L214.2 307.6H217.2L224 282.4Z"
fill="#5C687A"
/>
<path
d="M214 282.4H211L204.2 307.6H207.2L214 282.4Z"
fill="#5C687A"
/>
<path
d="M204 282.4H201L194.2 307.6H197.2L204 282.4Z"
fill="#5C687A"
/>
<path
d="M194 282.4H191L184.2 307.6H187.2L194 282.4Z"
fill="#5C687A"
/>
<path
d="M184 282.4H181L174.2 307.6H177.2L184 282.4Z"
fill="#5C687A"
/>
<path
d="M174 282.4H171L164.2 307.6H167.2L174 282.4Z"
fill="#5C687A"
/>
<path
d="M164 282.4H161L154.2 307.6H157.2L164 282.4Z"
fill="#5C687A"
/>
<path
d="M154 282.4H151L144.2 307.6H147.2L154 282.4Z"
fill="#5C687A"
/>
<path
d="M144 282.4H141L134.2 307.6H137.2L144 282.4Z"
fill="#5C687A"
/>
<path
d="M134 282.4H131L124.2 307.6H127.2L134 282.4Z"
fill="#5C687A"
/>
<path
d="M124 282.4H121L114.2 307.6H117.2L124 282.4Z"
fill="#5C687A"
/>
<path
d="M114 282.4H111L104.2 307.6H107.2L114 282.4Z"
fill="#5C687A"
/>
<path
d="M104 282.4H101L94.2 307.6H97.2L104 282.4Z"
fill="#5C687A"
/>
<path
d="M94 282.4H91L84.2 307.6H87.2L94 282.4Z"
fill="#5C687A"
/>
<path
d="M84 282.4H81L74.2 307.6H77.2L84 282.4Z"
fill="#5C687A"
/>
<path
d="M74 282.4H71L64.2 307.6H67.2L74 282.4Z"
fill="#5C687A"
/>
<path
d="M64 282.4H61L54.2 307.6H57.2L64 282.4Z"
fill="#5C687A"
/>
<path
d="M554 282.4H551L544.2 307.6H547.2L554 282.4Z"
fill="#5C687A"
/>
<path
d="M544 282.4H541L534.2 307.6H537.2L544 282.4Z"
fill="#5C687A"
/>
<path
d="M534 282.4H531L524.2 307.6H527.2L534 282.4Z"
fill="#5C687A"
/>
<path
d="M524 282.4H521L514.2 307.6H517.2L524 282.4Z"
fill="#5C687A"
/>
<path
d="M514 282.4H511L504.2 307.6H507.2L514 282.4Z"
fill="#5C687A"
/>
<path
d="M554 282.4H551L544.2 307.6H547.2L554 282.4Z"
fill="#5C687A"
/>
<path
d="M544 282.4H541L534.2 307.6H537.2L544 282.4Z"
fill="#5C687A"
/>
<path
d="M534 282.4H531L524.2 307.6H527.2L534 282.4Z"
fill="#5C687A"
/>
<path
d="M524 282.4H521L514.2 307.6H517.2L524 282.4Z"
fill="#5C687A"
/>
<path
d="M514 282.4H511L504.2 307.6H507.2L514 282.4Z"
fill="#5C687A"
/>
<path
d="M-495.5 300.2H1004V305.3H-495.5V300.2Z"
fill="#5C687A"
/>
<path
d="M-495.5 283.8H1004V286.6H-495.5V283.8Z"
fill="#5C687A"
/>
<path
d="M23.9995 69.7224L24.0234 69.1877L23.4883 69.2002L23.4879 69.2002L23.4856 69.2003L23.4752 69.2005L23.4319 69.2012C23.3931 69.2017 23.3348 69.2023 23.2581 69.2026C23.1047 69.2031 22.8778 69.2023 22.587 69.1968C22.0052 69.1859 21.1679 69.1561 20.1508 69.0811C18.115 68.931 15.3673 68.5999 12.51 67.8778C6.76331 66.4256 0.764888 63.4446 -1.01361 57.3819L-1.02018 57.3473C-1.02723 57.3095 -1.0376 57.2523 -1.0504 57.177C-1.07602 57.0262 -1.1114 56.8031 -1.14952 56.5177C-1.22579 55.9467 -1.31292 55.1278 -1.35515 54.1427C-1.43976 52.169 -1.34326 49.5462 -0.629963 46.9186C0.0827254 44.2932 1.40501 41.6838 3.75825 39.6995C6.10731 37.7188 9.52886 36.3227 14.5123 36.1999L15.0909 36.1857L14.9929 35.6158L14.9928 35.6157L14.9928 35.6153L14.9928 35.6153L14.9921 35.611L14.9887 35.5895C14.9856 35.5696 14.9811 35.5385 14.9756 35.4969C14.9646 35.4138 14.9499 35.2888 14.9358 35.1272C14.9077 34.8039 14.8824 34.3352 14.8952 33.7628C14.9209 32.6163 15.0993 31.0651 15.706 29.4372C16.9069 26.2151 19.8135 22.6092 26.7928 21.2914L26.7928 21.2915L26.8041 21.2891L26.8043 21.2891L26.8057 21.2888L26.8124 21.2874L26.8418 21.2814C26.8684 21.276 26.9089 21.2681 26.9625 21.258C27.0697 21.2378 27.2295 21.2091 27.436 21.176C27.8492 21.1097 28.4489 21.0256 29.1892 20.9557C30.6712 20.8156 32.7091 20.7329 34.9368 20.96C39.4042 21.4154 44.5437 23.1037 47.5758 27.9647L48.2684 29.0751L48.4926 27.7858L48.4926 27.7858L48.4927 27.7854L48.493 27.7832L48.4948 27.7733L48.5024 27.7315C48.5094 27.694 48.52 27.6376 48.5345 27.5635C48.5636 27.4153 48.6084 27.1961 48.6706 26.9157C48.7949 26.3549 48.9886 25.5497 49.2653 24.5776C49.819 22.6317 50.7027 20.025 52.0226 17.3729C54.6742 12.045 59.0148 6.66778 65.856 5.89697L65.862 5.89622L65.8625 5.89617L65.8651 5.89584L65.8768 5.89443L65.9256 5.88875C65.9692 5.88378 66.0348 5.87654 66.121 5.86773C66.2935 5.85009 66.5483 5.82611 66.8745 5.80116C67.5269 5.75126 68.4639 5.69754 69.5971 5.68285C71.8661 5.65344 74.91 5.78071 78.0271 6.40291C84.274 7.64982 90.6366 10.8354 91.9002 18.542L91.9004 18.6079C91.9004 18.6767 91.9001 18.7788 91.8987 18.9106C91.8958 19.1743 91.8885 19.5565 91.8707 20.0281C91.8351 20.9719 91.7577 22.2707 91.5909 23.6918C91.4239 25.1142 91.1683 26.6501 90.7787 28.071C90.4284 29.3487 89.9766 30.5074 89.4016 31.4037C89.3621 31.4594 89.3278 31.5087 89.2987 31.5511C89.2853 31.5705 89.2731 31.5884 89.262 31.6049C89.2407 31.6362 89.2235 31.6622 89.2102 31.6823L89.1869 31.718L89.1805 31.728L89.1786 31.7309L89.178 31.7319C89.1778 31.7323 89.1776 31.7326 89.6 32.0001L89.1776 31.7326L90.0048 32.2936C90.0819 32.1872 90.1569 32.0774 90.2298 31.9646C90.2881 31.8826 90.3586 31.7857 90.4413 31.6757C90.7253 31.2973 91.1518 30.7648 91.7166 30.1557C92.8476 28.9361 94.5252 27.4181 96.716 26.2132C101.065 23.821 107.497 22.6253 115.833 27.6233L115.855 27.6378C115.876 27.6517 115.907 27.6732 115.949 27.7021C116.032 27.7599 116.155 27.8475 116.31 27.9639C116.622 28.1969 117.064 28.5449 117.58 29.0012C118.613 29.9148 119.935 31.256 121.099 32.9687C123.421 36.3852 125.106 41.2571 122.638 47.2086L122.426 47.7199L122.956 47.8789L122.956 47.879L122.956 47.879L122.958 47.8794L122.965 47.8815L122.995 47.8911C123.023 47.8999 123.065 47.9134 123.121 47.9318C123.232 47.9686 123.395 48.0248 123.603 48.1014C124.018 48.2546 124.607 48.4888 125.296 48.8109C126.678 49.4565 128.449 50.4486 130.033 51.8384C131.616 53.228 132.995 54.998 133.631 57.1998C134.257 59.3674 134.179 62.0042 132.791 65.187C132.786 65.1911 132.781 65.1953 132.775 65.1998C132.703 65.2564 132.595 65.3396 132.453 65.4429C132.17 65.6495 131.757 65.9359 131.239 66.251C130.2 66.8825 128.753 67.6212 127.107 68.0675C125.461 68.5136 123.636 68.6624 121.821 68.1414C120.014 67.6226 118.173 66.4273 116.506 64.1083L115.942 63.3229L115.627 64.2369L115.627 64.237L115.627 64.2375L115.627 64.2377L115.625 64.2435L115.615 64.2721C115.61 64.2854 115.603 64.3025 115.595 64.3232C115.587 64.3434 115.578 64.3671 115.567 64.3941C115.524 64.5033 115.455 64.6668 115.357 64.877C115.162 65.2975 114.852 65.9041 114.395 66.6351C113.481 68.0969 111.983 70.0555 109.653 72.0176C105.001 75.9349 96.9932 79.9001 83.6 79.9001H83.5726L83.5455 79.9031L83.5454 79.9031L83.5435 79.9033L83.5348 79.9042L83.497 79.9079C83.463 79.9112 83.4112 79.916 83.3427 79.9216C83.2058 79.9328 83.0019 79.9475 82.7385 79.9609C82.2116 79.9877 81.447 80.0093 80.5036 79.9877C78.6157 79.9446 76.0185 79.7287 73.1805 79.0392C67.5044 77.6602 60.9065 74.3997 57.0449 66.8719L56.1026 67.1243C56.1006 67.1243 56.1006 67.1243 56.1006 67.1242L56.1006 67.1248L56.1007 67.1267L56.1012 67.1428C56.1016 67.1581 56.1021 67.1826 56.1022 67.2156C56.1025 67.2816 56.1013 67.3817 56.0951 67.5111C56.0827 67.7701 56.0503 68.1452 55.9701 68.599C55.8095 69.5077 55.4587 70.7223 54.6997 71.9496C53.2004 74.3743 50.0414 76.9555 43.2819 77.2004L43.2819 77.2005C43.2747 77.2008 43.2679 77.2011 43.2644 77.2013L43.2499 77.2025L43.235 77.2042L43.2147 77.2067L43.1642 77.2137L43.1526 77.2153C43.1122 77.2209 43.0575 77.2285 42.9888 77.2378C42.8378 77.2582 42.Line truncated
stroke="white"
/>
<path
d="M840.5 131.985L840.483 131.442L839.943 131.503L839.943 131.504L839.94 131.504L839.931 131.505L839.894 131.509C839.86 131.513 839.809 131.518 839.743 131.525C839.61 131.538 839.414 131.557 839.163 131.579C838.66 131.622 837.936 131.676 837.055 131.72C835.291 131.806 832.902 131.85 830.397 131.676C827.887 131.503 825.283 131.114 823.078 130.347C820.877 129.581 819.145 128.465 818.252 126.884C818.245 126.865 818.233 126.836 818.219 126.796C818.19 126.717 818.149 126.597 818.101 126.443C818.007 126.134 817.889 125.687 817.8 125.144C817.62 124.054 817.557 122.598 818.004 121.093C818.448 119.597 819.401 118.031 821.292 116.71C823.192 115.384 826.061 114.291 830.357 113.797L830.989 113.724L830.77 113.13L830.77 113.129L830.77 113.129L830.77 113.128L830.767 113.121C830.764 113.113 830.759 113.098 830.753 113.078C830.741 113.037 830.723 112.973 830.703 112.889C830.664 112.721 830.619 112.471 830.599 112.162C830.559 111.543 830.621 110.689 831.033 109.751C831.846 107.898 834.101 105.567 840.112 104.188L840.112 104.188L840.118 104.186L840.119 104.186L840.119 104.186L840.12 104.186L840.126 104.184L840.151 104.178C840.174 104.173 840.209 104.165 840.255 104.154C840.346 104.133 840.483 104.103 840.659 104.067C841.012 103.993 841.524 103.894 842.159 103.791C843.428 103.584 845.181 103.362 847.116 103.3C851.021 103.176 855.525 103.71 858.373 106.178L859.049 106.764L859.193 105.881L859.193 105.881L859.193 105.881L859.193 105.881L859.194 105.876L859.198 105.853C859.202 105.833 859.208 105.801 859.216 105.759C859.233 105.674 859.259 105.548 859.296 105.385C859.372 105.059 859.493 104.587 859.676 104.013C860.043 102.864 860.653 101.311 861.628 99.6961C863.575 96.4714 866.965 93.0114 872.786 91.9927L872.786 91.9928L872.797 91.9906L872.798 91.9905L872.8 91.9902L872.808 91.9885L872.842 91.9819C872.873 91.976 872.919 91.9673 872.979 91.9561C873.1 91.9337 873.28 91.9013 873.511 91.8619C873.974 91.783 874.644 91.6761 875.472 91.5645C877.127 91.3411 879.406 91.0993 881.902 91.025C884.401 90.9506 887.105 91.0443 889.615 91.4864C892.127 91.9288 894.405 92.7135 896.09 93.9926L896.095 93.9966L896.131 94.0266C896.163 94.0536 896.21 94.0944 896.271 94.1477C896.391 94.2544 896.563 94.4106 896.762 94.6064C897.161 94.9999 897.663 95.5444 898.089 96.1598C898.519 96.7809 898.847 97.4393 898.943 98.0637C899.024 98.5882 898.943 99.0914 898.599 99.5597C898.599 99.5602 898.598 99.5606 898.598 99.5611C898.53 99.6339 898.479 99.695 898.443 99.7409C898.426 99.7638 898.412 99.7829 898.401 99.7977L898.388 99.8167L898.383 99.8237L898.382 99.8265L898.381 99.8278C898.38 99.8284 898.38 99.8289 898.8 100.1L898.38 99.8289L899.167 100.439C899.238 100.363 899.303 100.286 899.363 100.208C899.458 100.11 899.605 99.9761 899.815 99.8248C900.29 99.4835 901.095 99.0469 902.38 98.7362C904.953 98.1138 909.457 97.9949 917.053 100.178L917.073 100.185C917.091 100.191 917.12 100.201 917.157 100.214C917.232 100.24 917.343 100.279 917.484 100.333C917.765 100.44 918.166 100.601 918.636 100.818C919.578 101.254 920.786 101.906 921.875 102.778C922.967 103.652 923.907 104.723 924.366 105.988C924.818 107.233 924.821 108.717 923.952 110.479L923.549 111.296L924.454 111.197L924.454 111.197L924.454 111.197L924.455 111.197L924.461 111.197L924.487 111.194C924.511 111.192 924.548 111.188 924.595 111.184C924.691 111.176 924.832 111.166 925.011 111.157C925.37 111.138 925.88 111.122 926.479 111.138C927.685 111.168 929.225 111.321 930.626 111.799C932.03 112.276 933.233 113.057 933.869 114.302C934.5 115.54 934.627 117.352 933.632 120.023C933.632 120.024 933.632 120.025 933.631 120.026L933.631 120.026L933.624 120.045C933.617 120.063 933.606 120.09 933.591 120.125C933.56 120.197 933.514 120.304 933.45 120.438C933.322 120.708 933.125 121.091 932.855 121.533C932.312 122.419 931.479 123.531 930.314 124.459C929.152 125.384 927.662 126.124 925.789 126.283C923.915 126.442 921.616 126.023 918.833 124.558L918.4 124.33L918.166 124.752L918.166 124.752L918.165 124.754C918.163 124.756 918.16 124.761 918.156 124.767C918.154 124.771 918.15 124.776 918.147 124.782C918.127 124.81 918.091 124.86 918.036 124.927C917.925 125.061 917.733 125.269 917.426 125.531C916.812 126.057 915.738 126.805 913.929 127.633C910.309 129.29 903.768 131.256 892.151 132.403L892.133 132.404L892.115 132.407L892.115 132.407L892.115 132.407L892.113 132.408L892.106 132.409L892.073 132.414C892.043 132.419 891.999 132.426 891.94 132.435C891.821 132.453 891.646 132.479 891.419 132.509C890.966 132.569 890.307 132.646 889.492 132.713C887.86 132.847 885.607 132.943 883.118 132.789C878.115 132.478 872.278 131.164 868.562 127.256L867.421 126.055L867.707 127.683L867.707 127.683L867.707 127.684L867.707 127.685L867.707 127.687C867.708 127.692 867.71 127.702 867.711 127.717C867.714 127.747 867.718 127.796 867.72 127.862C867.724 127.993 867.718 128.19 867.678 128.434C867.599 128.921 867.384 129.601 866.827 130.335C865.714 131.799 863.166 133.563 857.337 134.304L857.167 134.326L857.139 134.354C857.121 134.358 857.102 134.363 857.08 134.368C8Line truncated
stroke="white"
/>
<path
d="M23.8212 158.98L23.2811 159.001L23.3 159.5L23.2811 159.001L23.2808 159.001L23.279 159.001L23.2711 159.001L23.2384 159.002C23.2268 159.002 23.2128 159.003 23.1966 159.003C23.1722 159.004 23.1425 159.005 23.1079 159.006C22.9929 159.009 22.8229 159.012 22.6051 159.015C22.1695 159.02 21.5427 159.021 20.781 159.005C19.2564 158.972 17.1973 158.87 15.0518 158.592C12.9023 158.313 10.6882 157.86 8.84366 157.136C7.00096 156.413 5.61073 155.451 4.96175 154.205C4.95719 154.191 4.95113 154.172 4.94385 154.148C4.92516 154.086 4.89855 153.993 4.86923 153.873C4.8105 153.631 4.74137 153.282 4.70258 152.861C4.62474 152.015 4.67079 150.897 5.14792 149.77C6.07915 147.571 8.76952 145.096 16.2199 144.8L16.8698 144.774L16.6786 144.156L16.6785 144.155L16.6785 144.155L16.6774 144.151C16.6758 144.146 16.6731 144.135 16.6695 144.12C16.6622 144.091 16.6517 144.043 16.641 143.979C16.6196 143.851 16.5981 143.661 16.6015 143.426C16.6083 142.958 16.714 142.316 17.1184 141.629C17.9198 140.268 19.989 138.586 25.2649 137.896L25.2649 137.896L25.2749 137.895L25.275 137.895L25.2751 137.895L25.2762 137.894L25.2814 137.894L25.3037 137.89C25.3238 137.887 25.3543 137.883 25.3945 137.878C25.4751 137.867 25.595 137.851 25.7499 137.832C26.0598 137.795 26.5093 137.747 27.0645 137.703C28.1756 137.615 29.7049 137.545 31.3798 137.612C34.7671 137.749 38.6008 138.449 40.858 140.565L41.4682 141.137L41.6831 140.329L41.6831 140.329L41.6831 140.329L41.6832 140.329L41.684 140.326L41.6885 140.31C41.6907 140.302 41.6936 140.292 41.6972 140.28C41.7006 140.268 41.7046 140.255 41.7092 140.239C41.7284 140.176 41.7585 140.08 41.8009 139.957C41.8857 139.711 42.0194 139.354 42.2128 138.921C42.5998 138.055 43.2233 136.89 44.167 135.698C46.0482 133.322 49.2047 130.835 54.3423 130.398L54.3423 130.398L54.35 130.398L54.3 129.9L54.35 130.398L54.3504 130.398L54.3525 130.397L54.3614 130.397L54.3982 130.393C54.431 130.39 54.4802 130.385 54.5448 130.38C54.674 130.369 54.8647 130.353 55.1088 130.336C55.5969 130.301 56.2979 130.259 57.1461 130.231C58.8442 130.175 61.1246 130.176 63.4652 130.398C65.8118 130.621 68.1876 131.064 70.0925 131.879C71.9782 132.685 73.3216 133.818 73.8024 135.381C73.8031 135.409 73.8039 135.447 73.8043 135.493C73.8054 135.608 73.8047 135.775 73.7972 135.982C73.7822 136.397 73.74 136.968 73.6323 137.59C73.4317 138.749 73.0198 139.991 72.2247 140.793C72.1479 140.86 72.0869 140.915 72.0418 140.956C72.0123 140.983 71.9896 141.005 71.9737 141.02L71.955 141.038L71.9494 141.044L71.9476 141.045L71.947 141.046C71.9467 141.046 71.9464 141.047 72.3 141.4L71.9464 141.047L72.6166 141.787C72.7173 141.705 72.8133 141.618 72.9049 141.528C72.9174 141.517 72.9304 141.506 72.9439 141.494C73.146 141.324 73.4526 141.082 73.8618 140.802C74.6802 140.243 75.9077 139.536 77.5301 138.959C80.7656 137.808 85.5975 137.162 91.9322 139.271L91.9459 139.277C91.9613 139.283 91.9848 139.292 92.0159 139.304C92.0779 139.329 92.1699 139.366 92.2866 139.417C92.52 139.517 92.8514 139.667 93.2385 139.864C94.016 140.261 95.0041 140.84 95.8763 141.581C96.7533 142.326 97.4752 143.202 97.7823 144.181C98.0811 145.134 98.0062 146.244 97.1794 147.53L96.6842 148.3H97.5991H97.5993H97.5996H97.6L97.6006 148.3L97.6102 148.3C97.6151 148.3 97.6216 148.301 97.6295 148.301C97.6368 148.301 97.6454 148.301 97.6552 148.302C97.6961 148.303 97.7582 148.306 97.8378 148.312C97.9975 148.324 98.2261 148.348 98.4953 148.397C99.0382 148.495 99.7232 148.689 100.34 149.065C100.95 149.436 101.486 149.98 101.768 150.791C102.05 151.6 102.099 152.732 101.633 154.317C101.631 154.321 101.628 154.326 101.626 154.331C101.608 154.364 101.581 154.414 101.544 154.478C101.468 154.607 101.351 154.789 101.19 154.996C100.866 155.411 100.373 155.915 99.6871 156.285C98.3605 157.003 96.159 157.306 92.7746 155.082L92.3218 154.785L92.0628 155.258L92.0626 155.258L92.0623 155.258L92.062 155.259L92.0615 155.26L92.0577 155.266C92.0523 155.275 92.0428 155.291 92.0288 155.313C92.0008 155.357 91.9546 155.426 91.8873 155.517C91.7527 155.698 91.5335 155.966 91.2058 156.294C90.551 156.948 89.4599 157.844 87.7402 158.759C84.3005 160.588 78.3319 162.501 68.2901 162.7L68.2722 162.701L68.2545 162.702L68.2544 162.702L68.2543 162.702L68.2528 162.702L68.2461 162.703L68.2176 162.705C68.1919 162.708 68.1531 162.711 68.1019 162.715C67.9993 162.722 67.8469 162.733 67.65 162.744C67.2561 162.765 66.6847 162.789 65.9789 162.797C64.5664 162.813 62.6205 162.763 60.4853 162.504C56.1863 161.982 51.2427 160.624 48.2695 157.363L47.2332 156.227L47.4025 157.75C47.4024 157.749 47.4024 157.749 47.4025 157.75C47.4025 157.75 47.4025 157.75 47.4025 157.751C47.4027 157.753 47.4031 157.759 47.4034 157.768C47.4041 157.788 47.4045 157.822 47.402 157.869C47.3972 157.962 47.3811 158.105 47.3339 158.283C47.2404 158.635 47.0218 159.135 46.5052 159.663C45.4686 160.722 43.1668 161.954 38.0758 162.201L37.6 162.224V162.276C37.5397 162.283 37.4728 162.291 37.3997 162.3C36.998 162.345 36.4203 162.399 35.7255 162.447C34.3346 162.541 32.4868 162.606 30.6501 162.513C28.8031 162.42 27.01Line truncated
stroke="white"
/>
<path
d="M374.319 109.001L373.768 108.98L373.801 109.53C373.856 110.448 373.238 111.143 371.93 111.66C370.634 112.174 368.84 112.427 366.986 112.526C365.142 112.625 363.284 112.569 361.883 112.489C361.184 112.448 360.601 112.402 360.193 112.365C359.989 112.347 359.829 112.332 359.72 112.321C359.666 112.315 359.624 112.311 359.597 112.308L359.566 112.304L359.558 112.304L359.556 112.303L359.556 112.303L359.54 112.302L359.524 112.301C354.433 112.054 352.131 110.822 351.095 109.763C350.578 109.235 350.36 108.735 350.266 108.383C350.219 108.205 350.203 108.062 350.198 107.969C350.196 107.922 350.196 107.888 350.197 107.868C350.197 107.859 350.197 107.853 350.197 107.851L350.198 107.85L350.198 107.85L350.198 107.849L350.367 106.327L349.331 107.463C346.357 110.724 341.414 112.082 337.115 112.604C334.98 112.863 333.034 112.913 331.621 112.897C330.915 112.889 330.344 112.865 329.95 112.844C329.753 112.833 329.601 112.822 329.498 112.815C329.447 112.811 329.408 112.808 329.382 112.805L329.354 112.803L329.347 112.802L329.346 112.802L329.346 112.802L329.345 112.802L329.328 112.801L329.31 112.8C319.268 112.601 313.3 110.688 309.86 108.859C308.14 107.944 307.049 107.048 306.394 106.394C306.067 106.066 305.847 105.798 305.713 105.617C305.645 105.526 305.599 105.457 305.571 105.413C305.565 105.403 305.559 105.394 305.555 105.387C305.549 105.378 305.545 105.371 305.542 105.366L305.538 105.358L305.537 105.358L305.537 105.358L305.537 105.358L305.537 105.357L305.254 104.84L304.789 105.208C302.302 107.18 299.275 107.246 296.805 106.784C295.575 106.554 294.503 106.195 293.738 105.894C293.356 105.744 293.052 105.608 292.845 105.511C292.759 105.471 292.69 105.437 292.639 105.412C291.714 104.05 291.727 103.015 292.136 102.201C292.583 101.311 293.56 100.563 294.762 99.9595C295.949 99.3631 297.276 98.9455 298.317 98.6765C298.836 98.5425 299.278 98.4464 299.591 98.3841C299.747 98.3529 299.87 98.3302 299.954 98.3153C299.995 98.3079 300.027 98.3025 300.048 98.299L300.072 98.2951L300.077 98.2942L300.078 98.294L300.078 98.294L300.835 98.1742L300.421 97.5298C299.594 96.2436 299.519 95.1339 299.818 94.181C300.125 93.2016 300.847 92.3261 301.724 91.5813C302.596 90.8405 303.584 90.2608 304.361 89.8644C304.749 89.667 305.08 89.5169 305.313 89.4166C305.43 89.3665 305.522 89.3289 305.584 89.3041C305.615 89.2917 305.639 89.2825 305.654 89.2766L305.668 89.2713C312.002 87.1621 316.834 87.8076 320.07 88.9588C321.692 89.536 322.92 90.2429 323.738 90.8021C324.147 91.0817 324.454 91.324 324.656 91.4944C324.67 91.5058 324.683 91.5168 324.695 91.5275C324.787 91.6181 324.883 91.7048 324.983 91.7872L325.654 91.0466L325.3 91.4002C325.654 91.0466 325.653 91.0464 325.653 91.0461L325.652 91.0455L325.651 91.0437L325.645 91.0382L325.626 91.02C325.61 91.0047 325.588 90.9833 325.558 90.9561C325.513 90.9145 325.452 90.8596 325.375 90.7934C324.58 89.9911 324.168 88.749 323.968 87.5899C323.86 86.9677 323.818 86.3973 323.803 85.9821C323.795 85.775 323.795 85.6077 323.796 85.4933C323.796 85.447 323.797 85.4094 323.798 85.3815C324.278 83.8176 325.622 82.6849 327.507 81.8787C329.412 81.0643 331.788 80.6209 334.135 80.398C336.475 80.1756 338.756 80.1753 340.454 80.2312C341.302 80.2591 342.003 80.3009 342.491 80.3357C342.735 80.353 342.926 80.3686 343.055 80.3799C343.12 80.3855 343.169 80.39 343.202 80.3931L343.239 80.3966L343.248 80.3974L343.25 80.3976L343.25 80.3977V80.3977L343.258 80.3984C348.395 80.8346 351.552 83.3225 353.433 85.6981C354.377 86.8898 355 88.0551 355.387 88.9213C355.581 89.354 355.714 89.7108 355.799 89.9572C355.841 90.0804 355.872 90.1758 355.891 90.2392C355.9 90.2709 355.907 90.2946 355.911 90.3097L355.916 90.3258L355.917 90.3287L355.917 90.3289L355.917 90.3291L355.917 90.3292L356.132 91.137L356.742 90.565C358.999 88.4488 362.833 87.7493 366.22 87.6123C367.895 87.5445 369.424 87.6154 370.536 87.7033C371.091 87.7473 371.54 87.7954 371.85 87.8324C372.005 87.8509 372.125 87.8666 372.205 87.8776C372.246 87.8831 372.276 87.8874 372.296 87.8903L372.319 87.8936L372.324 87.8944L372.325 87.8945L372.325 87.8945L372.325 87.8946L372.335 87.896C377.611 88.5863 379.68 90.2676 380.482 91.6289C380.886 92.3157 380.992 92.9585 380.998 93.4262C381.002 93.6607 380.98 93.8508 380.959 93.9787C380.948 94.0426 380.938 94.0906 380.931 94.1204C380.927 94.1353 380.924 94.1456 380.923 94.1511L380.922 94.1525L380.922 94.1533L380.922 94.154L380.922 94.155L380.921 94.1556L380.73 94.774L381.38 94.7998C388.83 95.0958 391.521 97.5711 392.452 99.7702C392.929 100.897 392.975 102.015 392.897 102.861C392.859 103.282 392.79 103.631 392.731 103.873C392.701 103.993 392.675 104.086 392.656 104.148C392.649 104.172 392.643 104.191 392.638 104.205C391.989 105.451 390.599 106.413 388.756 107.136C386.912 107.86 384.698 108.313 382.548 108.592C380.403 108.87 378.344 108.972 376.819 109.005C376.057 109.021 375.431 109.02 374.995 109.015C374.777 109.012 374.607 109.009 374.492 109.006C374.435 109.004 374.391 109.003 374.362 109.002L374.329 109.001L374.321 109.001L374.319 109.001L374.319 109.001Z"
stroke="white"
/>
<path
d="M515.7 12.4L515.693 12.9L516.241 12.9077L516.199 12.3611C516.1 11.1052 516.983 10.1551 518.755 9.442C520.507 8.73725 522.925 8.36016 525.412 8.17362C527.889 7.98787 530.39 7.9938 532.283 8.03428C533.029 8.05024 533.678 8.07148 534.196 8.08841C534.335 8.09296 534.465 8.09721 534.584 8.10095C534.864 8.10971 535.092 8.11591 535.254 8.11657C535.333 8.1169 535.406 8.116 535.465 8.11219C535.489 8.11065 535.524 8.10787 535.561 8.10152C542.356 8.25933 545.519 9.7662 546.985 11.1287C547.717 11.8097 548.04 12.4671 548.185 12.9361C548.257 13.1724 548.286 13.3644 548.296 13.4918C548.302 13.5555 548.302 13.6029 548.302 13.6314C548.302 13.6456 548.302 13.6551 548.302 13.6595C548.302 13.6604 548.302 13.6602 548.302 13.6607C548.302 13.6613 548.301 13.6625 548.301 13.6622L548.173 15.1464L549.172 14.034C552.996 9.77431 559.589 7.89247 565.331 7.09527C568.187 6.69887 570.799 6.57479 572.699 6.54998C573.648 6.53758 574.418 6.54999 574.949 6.56544C575.215 6.57316 575.421 6.58164 575.56 6.58815C575.63 6.5914 575.682 6.59417 575.717 6.59609L575.757 6.59832L575.766 6.59888L575.768 6.59902L575.769 6.59904L575.784 6.60002H575.8C589.245 6.60002 597.291 8.89079 601.957 11.15C604.29 12.2795 605.78 13.4023 606.68 14.2305C607.131 14.6448 607.434 14.9861 607.622 15.2188C607.716 15.3352 607.781 15.4245 607.821 15.4822C607.841 15.5111 607.855 15.5321 607.863 15.5447L607.871 15.5566L607.871 15.5572L607.871 15.5574L607.872 15.558L607.872 15.5585L608.164 16.0476L608.612 15.691C611.903 13.0674 616.009 12.9036 619.36 13.4314C621.029 13.6943 622.489 14.1259 623.533 14.4921C624.054 14.6751 624.47 14.8412 624.754 14.961C624.896 15.0208 625.005 15.069 625.078 15.1019C625.078 15.102 625.078 15.102 625.078 15.1021C625.73 15.9654 626.059 16.7321 626.166 17.407C626.276 18.1003 626.156 18.7276 625.864 19.3064C625.268 20.4904 623.942 21.4919 622.337 22.3038C620.748 23.1072 618.969 23.6842 617.577 24.0612C616.883 24.2491 616.29 24.3861 615.871 24.476C615.662 24.5209 615.496 24.554 615.383 24.5758C615.327 24.5867 615.284 24.5947 615.255 24.6L615.223 24.6058L615.216 24.6072L615.214 24.6075L615.214 24.6075L615.214 24.6075L614.423 24.7458L614.896 25.3945C616.09 27.0333 616.247 28.4742 615.878 29.7243C615.5 31.0062 614.549 32.1602 613.389 33.1437C612.235 34.1222 610.919 34.8959 609.883 35.427C609.366 35.6916 608.924 35.8942 608.611 36.0301C608.454 36.0981 608.331 36.1493 608.247 36.1832C608.205 36.2002 608.173 36.2129 608.152 36.2211L608.129 36.2302C599.627 39.1628 594.407 39.5169 591.344 39.1658C589.815 38.9904 588.824 38.6394 588.228 38.3466C587.93 38.2001 587.73 38.0674 587.609 37.9771C587.607 37.976 587.606 37.9749 587.604 37.9738C587.543 37.9175 587.479 37.8623 587.411 37.8083L586.734 38.5409L587.1 38.2C586.734 38.5409 586.735 38.5414 586.735 38.5419L586.736 38.543L586.739 38.5456L586.745 38.5519L586.762 38.5689C586.775 38.5822 586.793 38.5992 586.816 38.6196C586.851 38.6515 586.898 38.6914 586.957 38.7375C587.407 39.1668 587.54 39.654 587.497 40.176C587.446 40.7934 587.142 41.4774 586.716 42.1431C586.296 42.801 585.784 43.3976 585.372 43.8333C585.168 44.0501 584.99 44.2247 584.864 44.3444C584.802 44.4042 584.752 44.4502 584.718 44.4808L584.681 44.5149L584.678 44.5177C582.886 45.9472 580.347 46.9476 577.5 47.6327C574.656 48.3171 571.547 48.6769 568.657 48.8509C565.77 49.0248 563.114 49.0124 561.179 48.9565C560.211 48.9285 559.425 48.8897 558.882 48.8579C558.61 48.842 558.399 48.8279 558.256 48.8178C558.185 48.8127 558.131 48.8087 558.094 48.8059L558.054 48.8027L558.044 48.8019L558.041 48.8017L558.041 48.8017L558 49.3L558.041 48.8016L558.032 48.801C551.086 48.3608 546.754 45.2377 544.153 42.2351C542.849 40.7299 541.977 39.2518 541.432 38.1516C541.16 37.6019 540.97 37.1476 540.849 36.8332C540.788 36.676 540.745 36.5539 540.717 36.4723C540.703 36.4316 540.693 36.401 540.687 36.3812L540.68 36.3598L540.679 36.3554L540.679 36.3552L540.678 36.3549L540.678 36.3548L540.448 35.5923L539.861 36.1322C538.365 37.5095 536.329 38.4557 534.078 39.0907C531.832 39.7247 529.404 40.0398 527.157 40.1759C524.912 40.312 522.86 40.2688 521.368 40.1913C520.623 40.1526 520.019 40.1054 519.602 40.068C519.393 40.0493 519.231 40.0331 519.123 40.0216C519.11 40.0202 519.098 40.0189 519.086 40.0177C519.049 40.0138 519.02 40.0105 518.999 40.0082L518.969 40.0047L518.961 40.0038L518.96 40.0036L518.96 40.0036L518.956 40.0031C511.872 39.2106 509.002 37.0964 507.857 35.3283C507.283 34.4409 507.119 33.6066 507.095 32.9993C507.083 32.6946 507.107 32.4468 507.132 32.2789C507.145 32.195 507.158 32.1315 507.167 32.0912C507.172 32.071 507.175 32.0568 507.178 32.0487L507.179 32.0422L507.18 32.0416L507.18 32.0414L507.18 32.0413L507.18 32.041L507.369 31.4099L506.707 31.4001C496.644 31.2514 492.884 28.1614 491.539 25.3235C490.856 23.8807 490.762 22.4392 490.844 21.3483C490.885 20.804 490.969 20.3521 491.042 20.0384C491.078 19.8817 491.112 19.7601 491.136 19.6794C491.147 19.6419 491.156 19.6133 491.162 19.5941C492.011 17.9504 493.885 16.6696 496.352 15.6914C498.817 14.7134 501.783 14.0735 504.Line truncated
stroke="white"
/>
<path
d="M348.2 247.6H855.4V285.4H348.2V247.6Z"
fill="#A2B2CB"
/>
<path
d="M402.4 284.7C402.4 292.763 395.863 299.3 387.8 299.3C379.737 299.3 373.2 292.763 373.2 284.7C373.2 276.637 379.737 270.1 387.8 270.1C395.863 270.1 402.4 276.637 402.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M387.8 294.8C382.2 294.8 377.7 290.3 377.7 284.7C377.7 279.1 382.2 274.6 387.8 274.6C393.4 274.6 397.9 279.1 397.9 284.7C397.9 290.2 393.3 294.8 387.8 294.8Z"
fill="#526D95"
/>
<path
d="M434.4 284.7C434.4 292.763 427.863 299.3 419.8 299.3C411.737 299.3 405.2 292.763 405.2 284.7C405.2 276.637 411.737 270.1 419.8 270.1C427.863 270.1 434.4 276.637 434.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M419.8 294.8C414.2 294.8 409.7 290.3 409.7 284.7C409.7 279.1 414.2 274.6 419.8 274.6C425.4 274.6 429.9 279.1 429.9 284.7C429.9 290.2 425.3 294.8 419.8 294.8Z"
fill="#526D95"
/>
<path
d="M487.4 284.7C487.4 292.763 480.863 299.3 472.8 299.3C464.737 299.3 458.2 292.763 458.2 284.7C458.2 276.637 464.737 270.1 472.8 270.1C480.863 270.1 487.4 276.637 487.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M472.8 294.8C467.2 294.8 462.7 290.3 462.7 284.7C462.7 279.1 467.2 274.6 472.8 274.6C478.4 274.6 482.9 279.1 482.9 284.7C482.9 290.2 478.3 294.8 472.8 294.8Z"
fill="#526D95"
/>
<path
d="M752.4 284.7C752.4 292.763 745.863 299.3 737.8 299.3C729.737 299.3 723.2 292.763 723.2 284.7C723.2 276.637 729.737 270.1 737.8 270.1C745.863 270.1 752.4 276.637 752.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M737.8 294.8C732.2 294.8 727.7 290.3 727.7 284.7C727.7 279.1 732.2 274.6 737.8 274.6C743.4 274.6 747.9 279.1 747.9 284.7C747.9 290.2 743.3 294.8 737.8 294.8Z"
fill="#526D95"
/>
<path
d="M784.4 284.7C784.4 292.763 777.863 299.3 769.8 299.3C761.737 299.3 755.2 292.763 755.2 284.7C755.2 276.637 761.737 270.1 769.8 270.1C777.863 270.1 784.4 276.637 784.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M769.8 294.8C764.2 294.8 759.7 290.3 759.7 284.7C759.7 279.1 764.2 274.6 769.8 274.6C775.4 274.6 779.9 279.1 779.9 284.7C779.9 290.2 775.3 294.8 769.8 294.8Z"
fill="#526D95"
/>
<path
d="M839.4 284.7C839.4 292.763 832.863 299.3 824.8 299.3C816.737 299.3 810.2 292.763 810.2 284.7C810.2 276.637 816.737 270.1 824.8 270.1C832.863 270.1 839.4 276.637 839.4 284.7Z"
fill="white"
stroke="#526D95"
/>
<path
d="M824.8 294.8C819.2 294.8 814.7 290.3 814.7 284.7C814.7 279.1 819.2 274.6 824.8 274.6C830.4 274.6 834.9 279.1 834.9 284.7C834.9 290.2 830.3 294.8 824.8 294.8Z"
fill="#526D95"
/>
<path
d="M326 267.883C326.283 267.71 326.278 267.407 326.255 267.276C326.231 267.139 326.167 267.002 326.098 266.878C326.023 266.745 325.919 266.584 325.779 266.39C325.516 266.026 325.11 265.519 324.5 264.813V235.967C324.505 235.949 324.511 235.928 324.518 235.903C324.542 235.818 324.578 235.691 324.627 235.525C324.726 235.194 324.875 234.709 325.078 234.096C325.484 232.871 326.104 231.137 326.961 229.107C328.675 225.044 331.329 219.814 335.09 215.112L335.108 215.091L335.122 215.068L334.7 214.8L335.122 215.067L335.123 215.067L335.124 215.066L335.127 215.06L335.143 215.035C335.158 215.012 335.179 214.979 335.207 214.935C335.264 214.847 335.349 214.717 335.46 214.549C335.681 214.213 336.007 213.725 336.422 213.123C337.254 211.918 338.445 210.255 339.881 208.421C342.73 204.783 346.515 200.511 350.358 197.8H351.5H351.614L351.717 197.751L351.5 197.3C351.717 197.751 351.717 197.751 351.717 197.75L351.718 197.75L351.72 197.749L351.728 197.745L351.754 197.732C351.776 197.721 351.808 197.705 351.851 197.683C351.935 197.639 352.059 197.572 352.223 197.48C352.552 197.296 353.039 197.01 353.687 196.594C354.983 195.764 356.919 194.419 359.5 192.353C364.638 188.24 372.333 181.268 382.623 169.8H383H383.916L326 267.883ZM326 267.883V298.523V267.883ZM383.389 166.917C383.063 167.409 383.06 167.916 383.151 168.33C383.198 168.543 383.268 168.723 383.327 168.851C383.356 168.914 383.382 168.962 383.399 168.993C383.408 169.009 383.415 169.02 383.418 169.026L383.421 169.03L383.421 169.03L383.421 169.03L383.421 169.031L383.422 169.032L383.389 166.917ZM383.389 166.917C383.408 166.898 383.435 166.87 383.471 166.834C383.546 166.758 383.658 166.646 383.805 166.503C384.098 166.216 384.531 165.803 385.084 165.302C386.192 164.298 387.781 162.941 389.708 161.528C393.573 158.695 398.747 155.674 404.081 154.793L404.081 154.793L404.094 154.791L404.094 154.791L404.095 154.791L404.1 154.79L404.12 154.786L404.199 154.771C404.269 154.759 404.374 154.739 404.512 154.715C404.788 154.666 405.195 154.596 405.717 154.512C406.761 154.345 408.268 154.12 410.11 153.896C413.797 153.448 418.826 153 424.2 153H471.9H472.4V152.5V150.6H495.7H496.2V150.1V149.5H505.3V150.1V150.6H505.8H524.3V152.3V152.748L524.745 152.797L524.8 152.3C524.745 152.797 524.746 152.797 524.746 152.797L524.747 152.797L524.748 152.797L524.754 152.798L524.775 152.8C524.793 152.802 524.819 152.804 524.852 152.807C524.919 152.813 525.017 152.82 525.144 152.828C525.398 152.843 525.771 152.859 526.251 152.861C527.21 152.866 528.6 152.818 530.335 152.609C533.805 152.191 538.657 151.129 544.21 148.554L544.257 148.532C544.26 148.531 544.265 148.529 544.269 148.528C544.332 148.507 544.471 148.469 544.738 148.42C545.271 148.321 546.282 148.186 548.161 148.049C551.918 147.775 559.104 147.5 572.8 147.5H863.8V153.8V154.3H864.3H871.3V156V156.525L871.825 156.499L383.389 166.917Z"
fill="white"
stroke="#5B7BAB"
/>
<path
d="M328.893 224.561L328.816 224.677C327.354 226.87 326.271 229.644 325.551 231.89C325.193 233.009 324.927 233.99 324.751 234.689C324.662 235.039 324.597 235.318 324.554 235.51C324.551 235.521 324.549 235.531 324.546 235.541C324.611 235.574 324.701 235.611 324.822 235.651C325.167 235.765 325.675 235.875 326.341 235.975C327.667 236.175 329.533 236.329 331.77 236.444C336.237 236.675 342.127 236.75 348 236.75C353.871 236.75 359.718 236.675 364.098 236.606C365.567 236.583 366.869 236.561 367.953 236.542C368.486 236.533 368.967 236.525 369.388 236.518C370.026 236.508 370.528 236.5 370.871 236.496C371.043 236.495 371.177 236.494 371.269 236.495C371.315 236.495 371.355 236.496 371.386 236.497C371.39 236.497 371.394 236.497 371.399 236.497C380.873 235.495 410.607 233.798 437.974 232.351C451.687 231.626 464.812 230.963 474.51 230.482C479.358 230.241 483.35 230.046 486.129 229.911C487.519 229.843 488.606 229.791 489.345 229.755L490.188 229.714L490.403 229.704L490.458 229.702L490.472 229.701L490.474 229.701L328.893 224.561ZM328.893 224.561L328.895 224.536M328.893 224.561L328.895 224.536M685 157.5C685.037 157.999 685.037 157.999 685.038 157.999L685.04 157.998L685.048 157.998L685.079 157.996L685.203 157.986L685.694 157.951C686.125 157.919 686.765 157.873 687.593 157.814C689.25 157.697 691.665 157.53 694.695 157.33C700.756 156.93 709.279 156.399 719.127 155.874C738.824 154.825 763.812 153.8 784.998 153.9H785H873.4V216.494C873.347 216.521 873.288 216.551 873.221 216.585C872.929 216.731 872.503 216.939 871.971 217.19C870.907 217.691 869.418 218.358 867.718 219.022C864.3 220.357 860.103 221.649 856.807 221.6L856.8 221.6L856.792 221.6L856.8 222.1C856.792 221.6 856.791 221.6 856.788 221.6L856.778 221.6L856.736 221.601L856.57 221.604L855.913 221.614L853.345 221.656C851.092 221.693 847.779 221.747 843.542 221.817C835.067 221.957 822.892 222.161 808.093 222.416C778.494 222.925 738.397 223.638 696.403 224.45C612.421 226.075 520.837 228.1 490.475 229.701L685 157.5ZM685 157.5L685.037 157.999L685.035 157.999C671.462 158.947 641.361 163.418 614.628 167.656C601.268 169.775 588.756 171.834 579.585 173.364C575 174.128 571.25 174.761 568.646 175.202C567.344 175.422 566.328 175.595 565.638 175.713L564.853 175.847L564.652 175.881L564.601 175.89L564.589 175.892L564.586 175.893C564.585 175.893 564.585 175.893 564.571 175.814L564.585 175.893L564.578 175.894C548.79 178.392 515.075 184.915 485.297 190.816C470.41 193.765 456.511 196.559 446.333 198.615C441.245 199.643 437.086 200.487 434.2 201.074C432.757 201.367 431.632 201.596 430.868 201.752L429.997 201.929L429.775 201.975L429.719 201.986L429.705 201.989L429.704 201.989L429.701 201.99L351.704 218.589L351.695 218.591L351.685 218.593C340.144 220.591 334.38 222.16 331.511 223.219C330.074 223.75 329.391 224.142 329.077 224.378C328.981 224.45 328.925 224.503 328.895 224.536M685 157.5L328.895 224.536M324.435 235.473C324.435 235.473 324.436 235.473 324.438 235.475C324.436 235.473 324.435 235.473 324.435 235.473Z"
fill="white"
stroke="#5B7BAB"
/>
<path
d="M324 256.9H873.9"
stroke="#5B7BAB"
/>
<mask
id="path-181-outside-1_114_2"
maskUnits="userSpaceOnUse"
x="380.737"
y="211.125"
width="40"
height="19"
fill="black"
>
<rect
fill="white"
x="380.737"
y="211.125"
width="40"
height="19"
/>
<path d="M381.96 224.993V223.472L388.672 212.852H389.775V215.209H389.03L383.959 223.233V223.352H392.997V224.993H381.96ZM389.149 228.125V224.531V223.822V212.852H390.909V228.125H389.149ZM400.773 228.334C399.65 228.334 398.693 228.028 397.902 227.417C397.112 226.8 396.508 225.908 396.09 224.739C395.672 223.566 395.463 222.149 395.463 220.489C395.463 218.838 395.672 217.429 396.09 216.26C396.512 215.087 397.119 214.192 397.909 213.576C398.705 212.954 399.659 212.643 400.773 212.643C401.887 212.643 402.839 212.954 403.629 213.576C404.425 214.192 405.031 215.087 405.449 216.26C405.871 217.429 406.083 218.838 406.083 220.489C406.083 222.149 405.874 223.566 405.456 224.739C405.039 225.908 404.435 226.8 403.644 227.417C402.854 228.028 401.897 228.334 400.773 228.334ZM400.773 226.693C401.887 226.693 402.752 226.156 403.368 225.082C403.985 224.009 404.293 222.477 404.293 220.489C404.293 219.166 404.151 218.04 403.868 217.11C403.59 216.181 403.187 215.472 402.66 214.985C402.138 214.498 401.509 214.254 400.773 214.254C399.669 214.254 398.807 214.799 398.185 215.887C397.564 216.971 397.253 218.505 397.253 220.489C397.253 221.811 397.392 222.935 397.671 223.859C397.949 224.784 398.349 225.488 398.871 225.97C399.398 226.452 400.032 226.693 400.773 226.693ZM408.559 224.993V223.472L415.27 212.852H416.374V215.209H415.628L410.557 223.233V223.352H419.596V224.993H408.559ZM415.748 228.125V224.531V223.822V212.852H417.508V228.125H415.748Z" />
</mask>
<path
d="M381.96 224.993V223.472L388.672 212.852H389.775V215.209H389.03L383.959 223.233V223.352H392.997V224.993H381.96ZM389.149 228.125V224.531V223.822V212.852H390.909V228.125H389.149ZM400.773 228.334C399.65 228.334 398.693 228.028 397.902 227.417C397.112 226.8 396.508 225.908 396.09 224.739C395.672 223.566 395.463 222.149 395.463 220.489C395.463 218.838 395.672 217.429 396.09 216.26C396.512 215.087 397.119 214.192 397.909 213.576C398.705 212.954 399.659 212.643 400.773 212.643C401.887 212.643 402.839 212.954 403.629 213.576C404.425 214.192 405.031 215.087 405.449 216.26C405.871 217.429 406.083 218.838 406.083 220.489C406.083 222.149 405.874 223.566 405.456 224.739C405.039 225.908 404.435 226.8 403.644 227.417C402.854 228.028 401.897 228.334 400.773 228.334ZM400.773 226.693C401.887 226.693 402.752 226.156 403.368 225.082C403.985 224.009 404.293 222.477 404.293 220.489C404.293 219.166 404.151 218.04 403.868 217.11C403.59 216.181 403.187 215.472 402.66 214.985C402.138 214.498 401.509 214.254 400.773 214.254C399.669 214.254 398.807 214.799 398.185 215.887C397.564 216.971 397.253 218.505 397.253 220.489C397.253 221.811 397.392 222.935 397.671 223.859C397.949 224.784 398.349 225.488 398.871 225.97C399.398 226.452 400.032 226.693 400.773 226.693ZM408.559 224.993V223.472L415.27 212.852H416.374V215.209H415.628L410.557 223.233V223.352H419.596V224.993H408.559ZM415.748 228.125V224.531V223.822V212.852H417.508V228.125H415.748Z"
fill="white"
/>
<path
d="M381.96 224.993H380.96V225.993H381.96V224.993ZM381.96 223.472L381.115 222.937L380.96 223.182V223.472H381.96ZM388.672 212.852V211.852H388.121L387.826 212.318L388.672 212.852ZM389.775 212.852H390.775V211.852H389.775V212.852ZM389.775 215.209V216.209H390.775V215.209H389.775ZM389.03 215.209V214.209H388.479L388.184 214.675L389.03 215.209ZM383.959 223.233L383.113 222.699L382.959 222.943V223.233H383.959ZM383.959 223.352H382.959V224.352H383.959V223.352ZM392.997 223.352H393.997V222.352H392.997V223.352ZM392.997 224.993V225.993H393.997V224.993H392.997ZM389.149 228.125H388.149V229.125H389.149V228.125ZM389.149 212.852V211.852H388.149V212.852H389.149ZM390.909 212.852H391.909V211.852H390.909V212.852ZM390.909 228.125V229.125H391.909V228.125H390.909ZM382.96 224.993V223.472H380.96V224.993H382.96ZM382.805 224.006L389.517 213.387L387.826 212.318L381.115 222.937L382.805 224.006ZM388.672 213.852H389.775V211.852H388.672V213.852ZM388.775 212.852V215.209H390.775V212.852H388.775ZM389.775 214.209H389.03V216.209H389.775V214.209ZM388.184 214.675L383.113 222.699L384.804 223.767L389.875 215.743L388.184 214.675ZM382.959 223.233V223.352H384.959V223.233H382.959ZM383.959 224.352H392.997V222.352H383.959V224.352ZM391.997 223.352V224.993H393.997V223.352H391.997ZM392.997 223.993H381.96V225.993H392.997V223.993ZM390.149 228.125V224.531H388.149V228.125H390.149ZM390.149 224.531V223.822H388.149V224.531H390.149ZM390.149 223.822V212.852H388.149V223.822H390.149ZM389.149 213.852H390.909V211.852H389.149V213.852ZM389.909 212.852V228.125H391.909V212.852H389.909ZM390.909 227.125H389.149V229.125H390.909V227.125ZM397.902 227.417L397.287 228.205L397.29 228.208L397.902 227.417ZM396.09 224.739L395.148 225.075L395.148 225.076L396.09 224.739ZM396.09 216.26L395.149 215.921L395.148 215.924L396.09 216.26ZM397.909 213.576L398.524 214.364L398.525 214.364L397.909 213.576ZM403.629 213.576L403.011 214.362L403.017 214.366L403.629 213.576ZM405.449 216.26L404.507 216.596L404.509 216.6L405.449 216.26ZM405.456 224.739L406.398 225.076L406.398 225.075L405.456 224.739ZM403.644 227.417L404.256 228.208L404.259 228.205L403.644 227.417ZM403.368 225.082L402.501 224.585L402.501 224.585L403.368 225.082ZM403.868 217.11L402.91 217.397L402.911 217.402L403.868 217.11ZM402.66 214.985L401.978 215.716L401.981 215.719L402.66 214.985ZM398.185 215.887L399.053 216.385L399.054 216.383L398.185 215.887ZM397.671 223.859L396.713 224.148L396.713 224.148L397.671 223.859ZM398.871 225.97L398.193 226.704L398.196 226.708L398.871 225.97ZM400.773 227.334C399.842 227.334 399.108 227.085 398.514 226.626L397.29 228.208C398.277 228.971 399.457 229.334 400.773 229.334V227.334ZM398.517 226.628C397.908 226.153 397.4 225.435 397.032 224.403L395.148 225.076C395.615 226.38 396.315 227.447 397.287 228.205L398.517 226.628ZM397.032 224.404C396.663 223.368 396.463 222.071 396.463 220.489H394.463C394.463 222.228 394.681 223.765 395.148 225.075L397.032 224.404ZM396.463 220.489C396.463 218.917 396.663 217.628 397.032 216.597L395.148 215.924C394.682 217.229 394.463 218.759 394.463 220.489H396.463ZM397.031 216.599C397.405 215.56 397.915 214.839 398.524 214.364L397.295 212.787C396.323 213.545 395.62 214.614 395.149 215.921L397.031 216.599ZM398.525 214.364C399.126 213.894 399.858 213.643 400.773 213.643V211.643C399.461 211.643 398.283 212.014 397.294 212.788L398.525 214.364ZM400.773 213.643C401.688 213.643 402.416 213.894 403.011 214.362L404.247 212.789C403.262 212.015 402.085 211.643 400.773 211.643V213.643ZM403.017 214.366C403.629 214.84 404.138 215.56 404.507 216.596L406.391 215.925C405.925 214.614 405.221 213.544 404.242 212.785L403.017 214.366ZM404.509 216.6C404.881 217.63 405.083 218.918 405.083 220.489H407.083C407.083 218.758 406.862 217.227 406.389 215.92L404.509 216.6ZM405.083 220.489C405.083 222.071 404.883 223.368 404.514 224.404L406.398 225.075C406.865 223.765 407.083 222.228 407.083 220.489H405.083ZM404.515 224.403C404.146 225.435 403.638 226.153 403.029 226.628L404.259 228.205C405.231 227.447 405.932 226.38 406.398 225.076L404.515 224.403ZM403.032 226.626C402.439 227.085 401.704 227.334 400.773 227.334V229.334C402.089 229.334 403.269 228.971 404.256 228.208L403.032 226.626ZM400.773 227.693C402.291 227.693 403.465 226.923 404.236 225.58L402.501 224.585C402.039 225.39 401.483 225.693 400.773 225.693V227.693ZM404.236 225.58C404.977 224.289 405.293 222.557 405.293 220.489H403.293C403.293 222.398 402.993 223.728 402.501 224.585L404.236 225.58ZM405.293 220.489C405.293 219.1 405.145 217.87 404.824 216.819L402.911 217.402C403.158 218.21 403.293 219.232 403.293 220.489H405.293ZM404.826 216.824C404.514 215.781 404.035 214.895 403.339 214.251L401.981 215.719C402.338 216.05 402.665 216.581 402.91 217.397L404.826 216.824ZM403.342 214.254C402.629 213.588 401.754 213.254 400.773 213.254V215.254C401.264 215.254 401.647 215.407 401.978 215.716L403.342 214.254ZM400.773 213.254C399.255 213.254 398.087 214.042 397.317 215.392L399.054 216.383C399.526 215.555 400.084 215.254 400.773 215.254V213.254ZM397.318 215.39C396.573 216.68Line truncated
fill="#5B7BAB"
mask="url(#path-181-outside-1_114_2)"
/>
<mask
id="path-183-outside-2_114_2"
maskUnits="userSpaceOnUse"
x="554.2"
y="193.069"
width="262"
height="25"
fill="black"
>
<rect
fill="white"
x="554.2"
y="193.069"
width="262"
height="25"
/>
<path
d="M570.376 200.057H568.26C568.135 199.448 567.916 198.913 567.603 198.453C567.296 197.992 566.921 197.605 566.477 197.292C566.039 196.974 565.553 196.735 565.018 196.576C564.483 196.416 563.926 196.337 563.346 196.337C562.288 196.337 561.33 196.604 560.471 197.139C559.618 197.673 558.938 198.461 558.432 199.502C557.931 200.543 557.681 201.82 557.681 203.333C557.681 204.846 557.931 206.123 558.432 207.163C558.938 208.204 559.618 208.992 560.471 209.527C561.33 210.061 562.288 210.329 563.346 210.329C563.926 210.329 564.483 210.249 565.018 210.09C565.553 209.931 566.039 209.694 566.477 209.382C566.921 209.063 567.296 208.674 567.603 208.213C567.916 207.746 568.135 207.212 568.26 206.609H570.376C570.217 207.502 569.927 208.301 569.506 209.006C569.085 209.712 568.562 210.312 567.936 210.806C567.31 211.296 566.608 211.668 565.829 211.924C565.055 212.18 564.227 212.308 563.346 212.308C561.856 212.308 560.53 211.944 559.37 211.216C558.21 210.488 557.297 209.453 556.632 208.11C555.966 206.768 555.633 205.176 555.633 203.333C555.633 201.49 555.966 199.897 556.632 198.555C557.297 197.213 558.21 196.178 559.37 195.45C560.53 194.721 561.856 194.357 563.346 194.357C564.227 194.357 565.055 194.485 565.829 194.741C566.608 194.997 567.31 195.373 567.936 195.868C568.562 196.357 569.085 196.954 569.506 197.659C569.927 198.359 570.217 199.158 570.376 200.057ZM572.771 200.841V198.965H583.419V200.841H579.119V212.069H577.105V200.841H572.771ZM586.356 216.983V198.965H588.301V201.046H588.54C588.688 200.819 588.892 200.529 589.154 200.176C589.421 199.818 589.802 199.499 590.297 199.22C590.798 198.936 591.475 198.794 592.328 198.794C593.431 198.794 594.404 199.07 595.246 199.621C596.087 200.173 596.744 200.955 597.216 201.968C597.688 202.98 597.924 204.175 597.924 205.551C597.924 206.939 597.688 208.142 597.216 209.16C596.744 210.172 596.09 210.957 595.254 211.515C594.418 212.066 593.454 212.342 592.362 212.342C591.52 212.342 590.846 212.203 590.34 211.924C589.834 211.64 589.444 211.318 589.171 210.96C588.898 210.596 588.688 210.295 588.54 210.056H588.369V216.983H586.356ZM588.335 205.517C588.335 206.506 588.48 207.38 588.77 208.136C589.06 208.887 589.484 209.475 590.041 209.902C590.599 210.323 591.281 210.533 592.089 210.533C592.931 210.533 593.633 210.312 594.196 209.868C594.765 209.419 595.192 208.816 595.476 208.059C595.766 207.297 595.911 206.45 595.911 205.517C595.911 204.595 595.769 203.765 595.484 203.026C595.206 202.28 594.782 201.692 594.213 201.26C593.65 200.822 592.942 200.603 592.089 200.603C591.27 200.603 590.582 200.81 590.024 201.225C589.467 201.635 589.046 202.209 588.762 202.949C588.477 203.683 588.335 204.539 588.335 205.517ZM604.852 212.376C604.022 212.376 603.268 212.22 602.591 211.907C601.914 211.588 601.377 211.131 600.979 210.533C600.581 209.931 600.382 209.202 600.382 208.349C600.382 207.599 600.529 206.99 600.825 206.524C601.121 206.051 601.516 205.682 602.011 205.414C602.506 205.147 603.052 204.948 603.649 204.817C604.252 204.681 604.858 204.573 605.466 204.493C606.263 204.391 606.908 204.314 607.403 204.263C607.904 204.206 608.268 204.112 608.495 203.981C608.728 203.85 608.845 203.623 608.845 203.299V203.23C608.845 202.389 608.615 201.734 608.154 201.268C607.699 200.802 607.008 200.568 606.081 200.568C605.119 200.568 604.366 200.779 603.82 201.2C603.274 201.621 602.89 202.07 602.668 202.548L600.757 201.865C601.098 201.069 601.553 200.449 602.122 200.005C602.696 199.556 603.322 199.243 603.999 199.067C604.682 198.885 605.353 198.794 606.012 198.794C606.433 198.794 606.917 198.845 607.463 198.947C608.015 199.044 608.546 199.246 609.058 199.553C609.576 199.86 610.005 200.324 610.347 200.944C610.688 201.564 610.858 202.394 610.858 203.435V212.069H608.845V210.295H608.743C608.606 210.579 608.379 210.883 608.06 211.207C607.742 211.532 607.318 211.807 606.789 212.035C606.26 212.262 605.614 212.376 604.852 212.376ZM605.159 210.568C605.956 210.568 606.627 210.411 607.173 210.098C607.724 209.785 608.14 209.382 608.418 208.887C608.703 208.392 608.845 207.872 608.845 207.326V205.483C608.76 205.585 608.572 205.679 608.282 205.764C607.997 205.844 607.668 205.915 607.292 205.978C606.922 206.034 606.561 206.086 606.209 206.131C605.862 206.171 605.58 206.205 605.364 206.233C604.841 206.302 604.352 206.413 603.897 206.566C603.447 206.714 603.083 206.939 602.805 207.24C602.532 207.536 602.395 207.94 602.395 208.452C602.395 209.151 602.654 209.68 603.171 210.039C603.695 210.391 604.357 210.568 605.159 210.568ZM623.304 204.595V206.472H616.069V204.595H623.304ZM616.547 198.965V212.069H614.533V198.965H616.547ZM624.84 198.965V212.069H622.826V198.965H624.84ZM630.53 209.1L636.844 198.965H639.164V212.069H637.151V201.934L630.871 212.069H628.517V198.965H630.53V209.1ZM653.158 198.965V210.192H655.001V216.301H652.987V212.069H642.852V198.965H644.865V210.192H651.145V198.965H653.158ZM661.958 212.376C661.128 212.376 660.374 212.22 659.698 211.907C659.021 211.588 658.483 211.131 658.085 210.533C657.687 209.931 657.488 209.202 657.488 Line truncated
655 209.1L748.968 198.965H751.289V212.069H749.275V201.934L742.996 212.069H740.641V198.965H742.655V209.1ZM747.637 194.596H749.514C749.514 195.518 749.196 196.271 748.558 196.857C747.921 197.443 747.057 197.736 745.965 197.736C744.89 197.736 744.034 197.443 743.397 196.857C742.766 196.271 742.45 195.518 742.45 194.596H744.327C744.327 195.04 744.449 195.43 744.694 195.765C744.944 196.101 745.368 196.269 745.965 196.269C746.562 196.269 746.989 196.101 747.245 195.765C747.506 195.43 747.637 195.04 747.637 194.596ZM753.577 215.823V210.192H754.669C754.937 209.913 755.167 209.612 755.36 209.288C755.554 208.964 755.721 208.58 755.864 208.136C756.012 207.687 756.137 207.141 756.239 206.498C756.341 205.85 756.432 205.067 756.512 204.152L756.956 198.965H765.283V210.192H767.296V215.823H765.283V212.069H755.591V215.823H753.577ZM756.956 210.192H763.269V200.841H758.833L758.491 204.152C758.349 205.523 758.173 206.72 757.962 207.744C757.752 208.767 757.416 209.584 756.956 210.192ZM775.463 212.342C774.2 212.342 773.111 212.063 772.195 211.506C771.285 210.943 770.583 210.158 770.088 209.151C769.599 208.139 769.354 206.962 769.354 205.619C769.354 204.277 769.599 203.094 770.088 202.07C770.583 201.041 771.271 200.239 772.153 199.664C773.04 199.084 774.075 198.794 775.258 198.794C775.941 198.794 776.615 198.908 777.28 199.135C777.946 199.363 778.551 199.732 779.097 200.244C779.643 200.75 780.079 201.422 780.403 202.258C780.727 203.094 780.889 204.123 780.889 205.346V206.199H770.788V204.459H778.841C778.841 203.719 778.694 203.06 778.398 202.48C778.108 201.899 777.693 201.442 777.152 201.106C776.618 200.77 775.986 200.603 775.258 200.603C774.456 200.603 773.762 200.802 773.176 201.2C772.596 201.592 772.15 202.104 771.837 202.736C771.524 203.367 771.368 204.044 771.368 204.766V205.926C771.368 206.916 771.538 207.755 771.88 208.443C772.227 209.126 772.707 209.646 773.321 210.004C773.936 210.357 774.65 210.533 775.463 210.533C775.992 210.533 776.47 210.459 776.896 210.312C777.328 210.158 777.701 209.931 778.014 209.629C778.327 209.322 778.568 208.941 778.739 208.486L780.684 209.032C780.479 209.692 780.135 210.272 779.652 210.772C779.168 211.267 778.571 211.654 777.86 211.933C777.149 212.206 776.35 212.342 775.463 212.342ZM792.722 204.595V206.472H785.488V204.595H792.722ZM785.965 198.965V212.069H783.952V198.965H785.965ZM794.258 198.965V212.069H792.245V198.965H794.258ZM801.791 212.376C800.961 212.376 800.207 212.22 799.531 211.907C798.854 211.588 798.316 211.131 797.918 210.533C797.52 209.931 797.321 209.202 797.321 208.349C797.321 207.599 797.469 206.99 797.765 206.524C798.06 206.051 798.456 205.682 798.95 205.414C799.445 205.147 799.991 204.948 800.589 204.817C801.191 204.681 801.797 204.573 802.406 204.493C803.202 204.391 803.848 204.314 804.342 204.263C804.843 204.206 805.207 204.112 805.434 203.981C805.668 203.85 805.784 203.623 805.784 203.299V203.23C805.784 202.389 805.554 201.734 805.093 201.268C804.638 200.802 803.947 200.568 803.02 200.568C802.059 200.568 801.305 200.779 800.759 201.2C800.213 201.621 799.829 202.07 799.607 202.548L797.696 201.865C798.038 201.069 798.493 200.449 799.061 200.005C799.636 199.556 800.261 199.243 800.938 199.067C801.621 198.885 802.292 198.794 802.952 198.794C803.373 198.794 803.856 198.845 804.402 198.947C804.954 199.044 805.486 199.246 805.998 199.553C806.515 199.86 806.945 200.324 807.286 200.944C807.627 201.564 807.798 202.394 807.798 203.435V212.069H805.784V210.295H805.682C805.545 210.579 805.318 210.883 804.999 211.207C804.681 211.532 804.257 211.807 803.728 212.035C803.199 212.262 802.554 212.376 801.791 212.376ZM802.099 210.568C802.895 210.568 803.566 210.411 804.112 210.098C804.664 209.785 805.079 209.382 805.358 208.887C805.642 208.392 805.784 207.872 805.784 207.326V205.483C805.699 205.585 805.511 205.679 805.221 205.764C804.937 205.844 804.607 205.915 804.231 205.978C803.862 206.034 803.501 206.086 803.148 206.131C802.801 206.171 802.519 206.205 802.303 206.233C801.78 206.302 801.291 206.413 800.836 206.566C800.387 206.714 800.023 206.939 799.744 207.24C799.471 207.536 799.334 207.94 799.334 208.452C799.334 209.151 799.593 209.68 800.111 210.039C800.634 210.391 801.297 210.568 802.099 210.568ZM812.94 212.206C812.519 212.206 812.158 212.055 811.857 211.753C811.555 211.452 811.404 211.091 811.404 210.67C811.404 210.249 811.555 209.888 811.857 209.586C812.158 209.285 812.519 209.134 812.94 209.134C813.361 209.134 813.722 209.285 814.024 209.586C814.325 209.888 814.476 210.249 814.476 210.67C814.476 210.949 814.405 211.205 814.263 211.438C814.126 211.671 813.941 211.859 813.708 212.001C813.48 212.137 813.225 212.206 812.94 212.206Z"
/>
</mask>
<path
d="M570.376 200.057H568.26C568.135 199.448 567.916 198.913 567.603 198.453C567.296 197.992 566.921 197.605 566.477 197.292C566.039 196.974 565.553 196.735 565.018 196.576C564.483 196.416 563.926 196.337 563.346 196.337C562.288 196.337 561.33 196.604 560.471 197.139C559.618 197.673 558.938 198.461 558.432 199.502C557.931 200.543 557.681 201.82 557.681 203.333C557.681 204.846 557.931 206.123 558.432 207.163C558.938 208.204 559.618 208.992 560.471 209.527C561.33 210.061 562.288 210.329 563.346 210.329C563.926 210.329 564.483 210.249 565.018 210.09C565.553 209.931 566.039 209.694 566.477 209.382C566.921 209.063 567.296 208.674 567.603 208.213C567.916 207.746 568.135 207.212 568.26 206.609H570.376C570.217 207.502 569.927 208.301 569.506 209.006C569.085 209.712 568.562 210.312 567.936 210.806C567.31 211.296 566.608 211.668 565.829 211.924C565.055 212.18 564.227 212.308 563.346 212.308C561.856 212.308 560.53 211.944 559.37 211.216C558.21 210.488 557.297 209.453 556.632 208.11C555.966 206.768 555.633 205.176 555.633 203.333C555.633 201.49 555.966 199.897 556.632 198.555C557.297 197.213 558.21 196.178 559.37 195.45C560.53 194.721 561.856 194.357 563.346 194.357C564.227 194.357 565.055 194.485 565.829 194.741C566.608 194.997 567.31 195.373 567.936 195.868C568.562 196.357 569.085 196.954 569.506 197.659C569.927 198.359 570.217 199.158 570.376 200.057ZM572.771 200.841V198.965H583.419V200.841H579.119V212.069H577.105V200.841H572.771ZM586.356 216.983V198.965H588.301V201.046H588.54C588.688 200.819 588.892 200.529 589.154 200.176C589.421 199.818 589.802 199.499 590.297 199.22C590.798 198.936 591.475 198.794 592.328 198.794C593.431 198.794 594.404 199.07 595.246 199.621C596.087 200.173 596.744 200.955 597.216 201.968C597.688 202.98 597.924 204.175 597.924 205.551C597.924 206.939 597.688 208.142 597.216 209.16C596.744 210.172 596.09 210.957 595.254 211.515C594.418 212.066 593.454 212.342 592.362 212.342C591.52 212.342 590.846 212.203 590.34 211.924C589.834 211.64 589.444 211.318 589.171 210.96C588.898 210.596 588.688 210.295 588.54 210.056H588.369V216.983H586.356ZM588.335 205.517C588.335 206.506 588.48 207.38 588.77 208.136C589.06 208.887 589.484 209.475 590.041 209.902C590.599 210.323 591.281 210.533 592.089 210.533C592.931 210.533 593.633 210.312 594.196 209.868C594.765 209.419 595.192 208.816 595.476 208.059C595.766 207.297 595.911 206.45 595.911 205.517C595.911 204.595 595.769 203.765 595.484 203.026C595.206 202.28 594.782 201.692 594.213 201.26C593.65 200.822 592.942 200.603 592.089 200.603C591.27 200.603 590.582 200.81 590.024 201.225C589.467 201.635 589.046 202.209 588.762 202.949C588.477 203.683 588.335 204.539 588.335 205.517ZM604.852 212.376C604.022 212.376 603.268 212.22 602.591 211.907C601.914 211.588 601.377 211.131 600.979 210.533C600.581 209.931 600.382 209.202 600.382 208.349C600.382 207.599 600.529 206.99 600.825 206.524C601.121 206.051 601.516 205.682 602.011 205.414C602.506 205.147 603.052 204.948 603.649 204.817C604.252 204.681 604.858 204.573 605.466 204.493C606.263 204.391 606.908 204.314 607.403 204.263C607.904 204.206 608.268 204.112 608.495 203.981C608.728 203.85 608.845 203.623 608.845 203.299V203.23C608.845 202.389 608.615 201.734 608.154 201.268C607.699 200.802 607.008 200.568 606.081 200.568C605.119 200.568 604.366 200.779 603.82 201.2C603.274 201.621 602.89 202.07 602.668 202.548L600.757 201.865C601.098 201.069 601.553 200.449 602.122 200.005C602.696 199.556 603.322 199.243 603.999 199.067C604.682 198.885 605.353 198.794 606.012 198.794C606.433 198.794 606.917 198.845 607.463 198.947C608.015 199.044 608.546 199.246 609.058 199.553C609.576 199.86 610.005 200.324 610.347 200.944C610.688 201.564 610.858 202.394 610.858 203.435V212.069H608.845V210.295H608.743C608.606 210.579 608.379 210.883 608.06 211.207C607.742 211.532 607.318 211.807 606.789 212.035C606.26 212.262 605.614 212.376 604.852 212.376ZM605.159 210.568C605.956 210.568 606.627 210.411 607.173 210.098C607.724 209.785 608.14 209.382 608.418 208.887C608.703 208.392 608.845 207.872 608.845 207.326V205.483C608.76 205.585 608.572 205.679 608.282 205.764C607.997 205.844 607.668 205.915 607.292 205.978C606.922 206.034 606.561 206.086 606.209 206.131C605.862 206.171 605.58 206.205 605.364 206.233C604.841 206.302 604.352 206.413 603.897 206.566C603.447 206.714 603.083 206.939 602.805 207.24C602.532 207.536 602.395 207.94 602.395 208.452C602.395 209.151 602.654 209.68 603.171 210.039C603.695 210.391 604.357 210.568 605.159 210.568ZM623.304 204.595V206.472H616.069V204.595H623.304ZM616.547 198.965V212.069H614.533V198.965H616.547ZM624.84 198.965V212.069H622.826V198.965H624.84ZM630.53 209.1L636.844 198.965H639.164V212.069H637.151V201.934L630.871 212.069H628.517V198.965H630.53V209.1ZM653.158 198.965V210.192H655.001V216.301H652.987V212.069H642.852V198.965H644.865V210.192H651.145V198.965H653.158ZM661.958 212.376C661.128 212.376 660.374 212.22 659.698 211.907C659.021 211.588 658.483 211.131 658.085 210.533C657.687 209.931 657.488 209.202 657.488 20Line truncated
655 209.1L748.968 198.965H751.289V212.069H749.275V201.934L742.996 212.069H740.641V198.965H742.655V209.1ZM747.637 194.596H749.514C749.514 195.518 749.196 196.271 748.558 196.857C747.921 197.443 747.057 197.736 745.965 197.736C744.89 197.736 744.034 197.443 743.397 196.857C742.766 196.271 742.45 195.518 742.45 194.596H744.327C744.327 195.04 744.449 195.43 744.694 195.765C744.944 196.101 745.368 196.269 745.965 196.269C746.562 196.269 746.989 196.101 747.245 195.765C747.506 195.43 747.637 195.04 747.637 194.596ZM753.577 215.823V210.192H754.669C754.937 209.913 755.167 209.612 755.36 209.288C755.554 208.964 755.721 208.58 755.864 208.136C756.012 207.687 756.137 207.141 756.239 206.498C756.341 205.85 756.432 205.067 756.512 204.152L756.956 198.965H765.283V210.192H767.296V215.823H765.283V212.069H755.591V215.823H753.577ZM756.956 210.192H763.269V200.841H758.833L758.491 204.152C758.349 205.523 758.173 206.72 757.962 207.744C757.752 208.767 757.416 209.584 756.956 210.192ZM775.463 212.342C774.2 212.342 773.111 212.063 772.195 211.506C771.285 210.943 770.583 210.158 770.088 209.151C769.599 208.139 769.354 206.962 769.354 205.619C769.354 204.277 769.599 203.094 770.088 202.07C770.583 201.041 771.271 200.239 772.153 199.664C773.04 199.084 774.075 198.794 775.258 198.794C775.941 198.794 776.615 198.908 777.28 199.135C777.946 199.363 778.551 199.732 779.097 200.244C779.643 200.75 780.079 201.422 780.403 202.258C780.727 203.094 780.889 204.123 780.889 205.346V206.199H770.788V204.459H778.841C778.841 203.719 778.694 203.06 778.398 202.48C778.108 201.899 777.693 201.442 777.152 201.106C776.618 200.77 775.986 200.603 775.258 200.603C774.456 200.603 773.762 200.802 773.176 201.2C772.596 201.592 772.15 202.104 771.837 202.736C771.524 203.367 771.368 204.044 771.368 204.766V205.926C771.368 206.916 771.538 207.755 771.88 208.443C772.227 209.126 772.707 209.646 773.321 210.004C773.936 210.357 774.65 210.533 775.463 210.533C775.992 210.533 776.47 210.459 776.896 210.312C777.328 210.158 777.701 209.931 778.014 209.629C778.327 209.322 778.568 208.941 778.739 208.486L780.684 209.032C780.479 209.692 780.135 210.272 779.652 210.772C779.168 211.267 778.571 211.654 777.86 211.933C777.149 212.206 776.35 212.342 775.463 212.342ZM792.722 204.595V206.472H785.488V204.595H792.722ZM785.965 198.965V212.069H783.952V198.965H785.965ZM794.258 198.965V212.069H792.245V198.965H794.258ZM801.791 212.376C800.961 212.376 800.207 212.22 799.531 211.907C798.854 211.588 798.316 211.131 797.918 210.533C797.52 209.931 797.321 209.202 797.321 208.349C797.321 207.599 797.469 206.99 797.765 206.524C798.06 206.051 798.456 205.682 798.95 205.414C799.445 205.147 799.991 204.948 800.589 204.817C801.191 204.681 801.797 204.573 802.406 204.493C803.202 204.391 803.848 204.314 804.342 204.263C804.843 204.206 805.207 204.112 805.434 203.981C805.668 203.85 805.784 203.623 805.784 203.299V203.23C805.784 202.389 805.554 201.734 805.093 201.268C804.638 200.802 803.947 200.568 803.02 200.568C802.059 200.568 801.305 200.779 800.759 201.2C800.213 201.621 799.829 202.07 799.607 202.548L797.696 201.865C798.038 201.069 798.493 200.449 799.061 200.005C799.636 199.556 800.261 199.243 800.938 199.067C801.621 198.885 802.292 198.794 802.952 198.794C803.373 198.794 803.856 198.845 804.402 198.947C804.954 199.044 805.486 199.246 805.998 199.553C806.515 199.86 806.945 200.324 807.286 200.944C807.627 201.564 807.798 202.394 807.798 203.435V212.069H805.784V210.295H805.682C805.545 210.579 805.318 210.883 804.999 211.207C804.681 211.532 804.257 211.807 803.728 212.035C803.199 212.262 802.554 212.376 801.791 212.376ZM802.099 210.568C802.895 210.568 803.566 210.411 804.112 210.098C804.664 209.785 805.079 209.382 805.358 208.887C805.642 208.392 805.784 207.872 805.784 207.326V205.483C805.699 205.585 805.511 205.679 805.221 205.764C804.937 205.844 804.607 205.915 804.231 205.978C803.862 206.034 803.501 206.086 803.148 206.131C802.801 206.171 802.519 206.205 802.303 206.233C801.78 206.302 801.291 206.413 800.836 206.566C800.387 206.714 800.023 206.939 799.744 207.24C799.471 207.536 799.334 207.94 799.334 208.452C799.334 209.151 799.593 209.68 800.111 210.039C800.634 210.391 801.297 210.568 802.099 210.568ZM812.94 212.206C812.519 212.206 812.158 212.055 811.857 211.753C811.555 211.452 811.404 211.091 811.404 210.67C811.404 210.249 811.555 209.888 811.857 209.586C812.158 209.285 812.519 209.134 812.94 209.134C813.361 209.134 813.722 209.285 814.024 209.586C814.325 209.888 814.476 210.249 814.476 210.67C814.476 210.949 814.405 211.205 814.263 211.438C814.126 211.671 813.941 211.859 813.708 212.001C813.48 212.137 813.225 212.206 812.94 212.206Z"
fill="white"
/>
<path
d="M570.376 200.057V201.057H571.569L571.361 199.882L570.376 200.057ZM568.26 200.057L567.281 200.258L567.445 201.057H568.26V200.057ZM567.603 198.453L566.771 199.007L566.776 199.014L567.603 198.453ZM566.477 197.292L565.889 198.101L565.895 198.105L565.901 198.11L566.477 197.292ZM565.018 196.576L564.733 197.534L564.733 197.534L565.018 196.576ZM560.471 197.139L559.942 196.29L559.94 196.291L560.471 197.139ZM558.432 199.502L557.532 199.065L557.53 199.069L558.432 199.502ZM558.432 207.163L557.53 207.597L557.532 207.601L558.432 207.163ZM560.471 209.527L559.94 210.374L559.942 210.376L560.471 209.527ZM565.018 210.09L564.733 209.131L564.733 209.131L565.018 210.09ZM566.477 209.382L567.058 210.195L567.06 210.194L566.477 209.382ZM567.603 208.213L566.773 207.656L566.771 207.658L567.603 208.213ZM568.26 206.609V205.609H567.446L567.281 206.406L568.26 206.609ZM570.376 206.609L571.36 206.784L571.57 205.609H570.376V206.609ZM569.506 209.006L568.647 208.494L568.647 208.494L569.506 209.006ZM567.936 210.806L568.552 211.594L568.556 211.591L567.936 210.806ZM565.829 211.924L565.517 210.974L565.514 210.975L565.829 211.924ZM559.37 211.216L558.839 212.063L558.839 212.063L559.37 211.216ZM556.632 208.11L557.527 207.666L557.527 207.666L556.632 208.11ZM565.829 194.741L565.514 195.691L565.517 195.691L565.829 194.741ZM567.936 195.868L567.316 196.652L567.32 196.655L567.936 195.868ZM569.506 197.659L568.647 198.172L568.649 198.175L569.506 197.659ZM570.376 199.057H568.26V201.057H570.376V199.057ZM569.24 199.855C569.091 199.133 568.825 198.472 568.43 197.891L566.776 199.014C567.007 199.354 567.179 199.763 567.281 200.258L569.24 199.855ZM568.435 197.898C568.061 197.337 567.6 196.86 567.053 196.475L565.901 198.11C566.242 198.35 566.531 198.647 566.771 199.007L568.435 197.898ZM567.065 196.484C566.533 196.096 565.944 195.808 565.304 195.617L564.733 197.534C565.162 197.662 565.545 197.851 565.889 198.101L567.065 196.484ZM565.304 195.617C564.673 195.429 564.019 195.337 563.346 195.337V197.337C563.833 197.337 564.294 197.404 564.733 197.534L565.304 195.617ZM563.346 195.337C562.11 195.337 560.967 195.652 559.942 196.29L560.999 197.988C561.692 197.556 562.466 197.337 563.346 197.337V195.337ZM559.94 196.291C558.905 196.94 558.108 197.881 557.532 199.065L559.331 199.939C559.768 199.041 560.33 198.407 561.002 197.986L559.94 196.291ZM557.53 199.069C556.948 200.28 556.681 201.714 556.681 203.333H558.681C558.681 201.925 558.914 200.806 559.333 199.935L557.53 199.069ZM556.681 203.333C556.681 204.951 556.948 206.386 557.53 207.597L559.333 206.73C558.914 205.86 558.681 204.74 558.681 203.333H556.681ZM557.532 207.601C558.108 208.784 558.905 209.726 559.94 210.374L561.002 208.679C560.33 208.258 559.768 207.624 559.331 206.726L557.532 207.601ZM559.942 210.376C560.967 211.014 562.11 211.329 563.346 211.329V209.329C562.466 209.329 561.692 209.109 560.999 208.678L559.942 210.376ZM563.346 211.329C564.019 211.329 564.673 211.236 565.304 211.048L564.733 209.131C564.294 209.262 563.833 209.329 563.346 209.329V211.329ZM565.304 211.048C565.94 210.859 566.527 210.575 567.058 210.195L565.896 208.568C565.551 208.814 565.165 209.002 564.733 209.131L565.304 211.048ZM567.06 210.194C567.603 209.804 568.062 209.327 568.435 208.768L566.771 207.658C566.53 208.02 566.238 208.322 565.894 208.569L567.06 210.194ZM568.434 208.77C568.825 208.186 569.09 207.529 569.239 206.812L567.281 206.406C567.18 206.894 567.007 207.307 566.773 207.656L568.434 208.77ZM568.26 207.609H570.376V205.609H568.26V207.609ZM569.391 206.433C569.251 207.22 569 207.903 568.647 208.494L570.364 209.519C570.853 208.699 571.182 207.784 571.36 206.784L569.391 206.433ZM568.647 208.494C568.287 209.098 567.843 209.605 567.316 210.022L568.556 211.591C569.28 211.018 569.883 210.325 570.364 209.519L568.647 208.494ZM567.32 210.019C566.788 210.434 566.189 210.753 565.517 210.974L566.141 212.874C567.026 212.583 567.832 212.157 568.552 211.594L567.32 210.019ZM565.514 210.975C564.85 211.194 564.129 211.308 563.346 211.308V213.308C564.325 213.308 565.26 213.166 566.143 212.873L565.514 210.975ZM563.346 211.308C562.027 211.308 560.889 210.988 559.902 210.369L558.839 212.063C560.172 212.9 561.685 213.308 563.346 213.308V211.308ZM559.902 210.369C558.912 209.748 558.119 208.859 557.527 207.666L555.736 208.555C556.475 210.046 557.508 211.228 558.839 212.063L559.902 210.369ZM557.527 207.666C556.944 206.49 556.633 205.056 556.633 203.333H554.633C554.633 205.295 554.988 207.046 555.736 208.555L557.527 207.666ZM556.633 203.333C556.633 201.609 556.944 200.175 557.527 198.999L555.736 198.111C554.988 199.619 554.633 201.371 554.633 203.333H556.633ZM557.527 198.999C558.119 197.806 558.912 196.917 559.902 196.297L558.839 194.602C557.508 195.438 556.475 196.619 555.736 198.111L557.527 198.999ZM559.902 196.297C560.889 195.677 562.027 195.357 563.346 195.357V193.357C561.685 193.357 560.172 193.766 558.839 194.602L559.902 196.297ZM563.346 195.357C564.129 195.357 564.85 195.471 565.514 195.691L566.143 193.792C565.26 193.5 564.325 193.357 563.346 193.3Line truncated
533V209.533C591.463 209.533 591.001 209.374 590.644 209.104L589.439 210.7ZM592.089 211.533C593.114 211.533 594.046 211.259 594.815 210.653L593.577 209.082C593.22 209.364 592.747 209.533 592.089 209.533V211.533ZM594.816 210.653C595.544 210.078 596.071 209.318 596.412 208.411L594.54 207.707C594.312 208.314 593.986 208.759 593.576 209.083L594.816 210.653ZM596.411 208.415C596.75 207.524 596.911 206.554 596.911 205.517H594.911C594.911 206.345 594.782 207.07 594.541 207.704L596.411 208.415ZM596.911 205.517C596.911 204.492 596.753 203.538 596.418 202.667L594.551 203.385C594.785 203.992 594.911 204.698 594.911 205.517H596.911ZM596.421 202.675C596.084 201.774 595.554 201.023 594.818 200.463L593.608 202.056C594.01 202.361 594.328 202.787 594.548 203.376L596.421 202.675ZM594.827 200.47C594.056 199.87 593.119 199.603 592.089 199.603V201.603C592.765 201.603 593.244 201.773 593.599 202.049L594.827 200.47ZM592.089 199.603C591.094 199.603 590.186 199.858 589.427 200.423L590.622 202.027C590.977 201.763 591.445 201.603 592.089 201.603V199.603ZM589.432 200.42C588.699 200.958 588.171 201.698 587.828 202.59L589.695 203.308C589.921 202.72 590.235 202.312 590.616 202.031L589.432 200.42ZM587.829 202.587C587.491 203.461 587.335 204.444 587.335 205.517H589.335C589.335 204.633 589.464 203.904 589.694 203.31L587.829 202.587ZM602.591 211.907L602.165 212.812L602.172 212.815L602.591 211.907ZM600.979 210.533L600.144 211.084L600.147 211.088L600.979 210.533ZM600.825 206.524L601.67 207.059L601.673 207.054L600.825 206.524ZM603.649 204.817L603.863 205.794L603.87 205.793L603.649 204.817ZM605.466 204.493L605.339 203.501L605.337 203.501L605.466 204.493ZM607.403 204.263L607.506 205.257L607.516 205.256L607.403 204.263ZM608.495 203.981L608.006 203.109L607.997 203.114L608.495 203.981ZM608.154 201.268L607.438 201.966L607.442 201.971L608.154 201.268ZM603.82 201.2L604.43 201.992L604.43 201.992L603.82 201.2ZM602.668 202.548L602.332 203.49L603.191 203.796L603.575 202.969L602.668 202.548ZM600.757 201.865L599.838 201.471L599.419 202.449L600.421 202.807L600.757 201.865ZM602.122 200.005L602.737 200.794L602.738 200.793L602.122 200.005ZM603.999 199.067L604.251 200.035L604.257 200.033L603.999 199.067ZM607.463 198.947L607.279 199.93L607.29 199.932L607.463 198.947ZM609.058 199.553L608.544 200.411L608.548 200.413L609.058 199.553ZM610.347 200.944L609.47 201.426L609.47 201.426L610.347 200.944ZM610.858 212.069V213.069H611.858V212.069H610.858ZM608.845 212.069H607.845V213.069H608.845V212.069ZM608.845 210.295H609.845V209.295H608.845V210.295ZM608.743 210.295V209.295H608.113L607.841 209.862L608.743 210.295ZM608.06 211.207L608.773 211.908L608.773 211.908L608.06 211.207ZM606.789 212.035L606.394 211.116L606.394 211.116L606.789 212.035ZM607.173 210.098L606.68 209.228L606.676 209.231L607.173 210.098ZM608.418 208.887L607.551 208.389L607.547 208.396L608.418 208.887ZM608.845 205.483H609.845V202.721L608.077 204.843L608.845 205.483ZM608.282 205.764L608.552 206.727L608.564 206.724L608.282 205.764ZM607.292 205.978L607.444 206.966L607.457 206.964L607.292 205.978ZM606.209 206.131L606.323 207.125L606.337 207.123L606.209 206.131ZM605.364 206.233L605.493 207.225L605.495 207.225L605.364 206.233ZM603.897 206.566L604.209 207.516L604.216 207.514L603.897 206.566ZM602.805 207.24L602.07 206.561L602.07 206.562L602.805 207.24ZM603.171 210.039L602.602 210.861L602.613 210.868L603.171 210.039ZM604.852 211.376C604.15 211.376 603.542 211.245 603.011 210.999L602.172 212.815C602.994 213.195 603.893 213.376 604.852 213.376V211.376ZM603.017 211.002C602.498 210.758 602.104 210.418 601.811 209.979L600.147 211.088C600.65 211.843 601.331 212.419 602.165 212.812L603.017 211.002ZM601.813 209.982C601.541 209.571 601.382 209.043 601.382 208.349H599.382C599.382 209.362 599.62 210.29 600.144 211.084L601.813 209.982ZM601.382 208.349C601.382 207.722 601.506 207.317 601.67 207.059L599.981 205.988C599.553 206.662 599.382 207.475 599.382 208.349H601.382ZM601.673 207.054C601.876 206.73 602.142 206.48 602.486 206.294L601.536 204.535C600.89 204.883 600.366 205.373 599.978 205.993L601.673 207.054ZM602.486 206.294C602.892 206.075 603.349 205.907 603.863 205.794L603.435 203.84C602.755 203.989 602.12 204.219 601.536 204.535L602.486 206.294ZM603.87 205.793C604.443 205.663 605.018 205.56 605.596 205.485L605.337 203.501C604.697 203.585 604.061 203.699 603.428 203.842L603.87 205.793ZM605.594 205.485C606.386 205.383 607.023 205.307 607.506 205.257L607.3 203.268C606.794 203.32 606.139 203.398 605.339 203.501L605.594 205.485ZM607.516 205.256C608.05 205.196 608.583 205.084 608.994 204.848L607.997 203.114C607.952 203.14 607.757 203.216 607.29 203.269L607.516 205.256ZM608.984 204.853C609.605 204.505 609.845 203.894 609.845 203.299H607.845C607.845 203.344 607.837 203.316 607.868 203.256C607.883 203.226 607.905 203.195 607.933 203.166C607.96 203.138 607.986 203.12 608.006 203.109L608.984 204.853ZM609.845 203.299V203.23H607.845V203.299H609.845ZM609.845 203.23C609.845 202.204 609.559 201.268 608.865 200.565L607.442 201.971C607.67 202.201 607Line truncated
547V197.965H614.533V199.965ZM623.84 198.965V212.069H625.84V198.965H623.84ZM624.84 211.069H622.826V213.069H624.84V211.069ZM623.826 212.069V198.965H621.826V212.069H623.826ZM622.826 199.965H624.84V197.965H622.826V199.965ZM630.53 209.1H629.53V212.597L631.379 209.629L630.53 209.1ZM636.844 198.965V197.965H636.288L635.995 198.436L636.844 198.965ZM639.164 198.965H640.164V197.965H639.164V198.965ZM639.164 212.069V213.069H640.164V212.069H639.164ZM637.151 212.069H636.151V213.069H637.151V212.069ZM637.151 201.934H638.151V198.421L636.301 201.407L637.151 201.934ZM630.871 212.069V213.069H631.428L631.721 212.596L630.871 212.069ZM628.517 212.069H627.517V213.069H628.517V212.069ZM628.517 198.965V197.965H627.517V198.965H628.517ZM630.53 198.965H631.53V197.965H630.53V198.965ZM631.379 209.629L637.692 199.493L635.995 198.436L629.681 208.571L631.379 209.629ZM636.844 199.965H639.164V197.965H636.844V199.965ZM638.164 198.965V212.069H640.164V198.965H638.164ZM639.164 211.069H637.151V213.069H639.164V211.069ZM638.151 212.069V201.934H636.151V212.069H638.151ZM636.301 201.407L630.021 211.542L631.721 212.596L638.001 202.46L636.301 201.407ZM630.871 211.069H628.517V213.069H630.871V211.069ZM629.517 212.069V198.965H627.517V212.069H629.517ZM628.517 199.965H630.53V197.965H628.517V199.965ZM629.53 198.965V209.1H631.53V198.965H629.53ZM653.158 198.965H654.158V197.965H653.158V198.965ZM653.158 210.192H652.158V211.192H653.158V210.192ZM655.001 210.192H656.001V209.192H655.001V210.192ZM655.001 216.301V217.301H656.001V216.301H655.001ZM652.987 216.301H651.987V217.301H652.987V216.301ZM652.987 212.069H653.987V211.069H652.987V212.069ZM642.852 212.069H641.852V213.069H642.852V212.069ZM642.852 198.965V197.965H641.852V198.965H642.852ZM644.865 198.965H645.865V197.965H644.865V198.965ZM644.865 210.192H643.865V211.192H644.865V210.192ZM651.145 210.192V211.192H652.145V210.192H651.145ZM651.145 198.965V197.965H650.145V198.965H651.145ZM652.158 198.965V210.192H654.158V198.965H652.158ZM653.158 211.192H655.001V209.192H653.158V211.192ZM654.001 210.192V216.301H656.001V210.192H654.001ZM655.001 215.301H652.987V217.301H655.001V215.301ZM653.987 216.301V212.069H651.987V216.301H653.987ZM652.987 211.069H642.852V213.069H652.987V211.069ZM643.852 212.069V198.965H641.852V212.069H643.852ZM642.852 199.965H644.865V197.965H642.852V199.965ZM643.865 198.965V210.192H645.865V198.965H643.865ZM644.865 211.192H651.145V209.192H644.865V211.192ZM652.145 210.192V198.965H650.145V210.192H652.145ZM651.145 199.965H653.158V197.965H651.145V199.965ZM659.698 211.907L659.272 212.812L659.278 212.815L659.698 211.907ZM658.085 210.533L657.251 211.084L657.253 211.088L658.085 210.533ZM657.932 206.524L658.776 207.059L658.779 207.054L657.932 206.524ZM660.755 204.817L660.969 205.794L660.976 205.793L660.755 204.817ZM662.573 204.493L662.445 203.501L662.443 203.501L662.573 204.493ZM664.509 204.263L664.612 205.257L664.622 205.256L664.509 204.263ZM665.601 203.981L665.112 203.109L665.103 203.114L665.601 203.981ZM665.26 201.268L664.544 201.966L664.549 201.971L665.26 201.268ZM660.926 201.2L661.537 201.992L661.537 201.992L660.926 201.2ZM659.774 202.548L659.438 203.49L660.297 203.796L660.681 202.969L659.774 202.548ZM657.863 201.865L656.944 201.471L656.525 202.449L657.527 202.807L657.863 201.865ZM659.228 200.005L659.843 200.794L659.844 200.793L659.228 200.005ZM661.105 199.067L661.357 200.035L661.363 200.033L661.105 199.067ZM664.569 198.947L664.385 199.93L664.396 199.932L664.569 198.947ZM666.165 199.553L665.65 200.411L665.654 200.413L666.165 199.553ZM667.453 200.944L666.577 201.426L666.577 201.426L667.453 200.944ZM667.965 212.069V213.069H668.965V212.069H667.965ZM665.951 212.069H664.951V213.069H665.951V212.069ZM665.951 210.295H666.951V209.295H665.951V210.295ZM665.849 210.295V209.295H665.22L664.947 209.862L665.849 210.295ZM665.166 211.207L665.88 211.908L665.88 211.908L665.166 211.207ZM663.895 212.035L663.5 211.116L663.5 211.116L663.895 212.035ZM664.279 210.098L663.786 209.228L663.782 209.231L664.279 210.098ZM665.525 208.887L664.658 208.389L664.653 208.396L665.525 208.887ZM665.951 205.483H666.951V202.721L665.183 204.843L665.951 205.483ZM665.388 205.764L665.658 206.727L665.67 206.724L665.388 205.764ZM664.398 205.978L664.551 206.966L664.563 206.964L664.398 205.978ZM663.315 206.131L663.429 207.125L663.443 207.123L663.315 206.131ZM662.47 206.233L662.6 207.225L662.601 207.225L662.47 206.233ZM661.003 206.566L661.316 207.516L661.323 207.514L661.003 206.566ZM659.911 207.24L659.177 206.561L659.176 206.562L659.911 207.24ZM660.278 210.039L659.708 210.861L659.719 210.868L660.278 210.039ZM661.958 211.376C661.256 211.376 660.648 211.245 660.117 210.999L659.278 212.815C660.101 213.195 661 213.376 661.958 213.376V211.376ZM660.123 211.002C659.604 210.758 659.21 210.418 658.917 209.979L657.253 211.088C657.757 211.843 658.437 212.419 659.272 212.812L660.123 211.002ZM658.92 209.982C658.648 209.571 658.488 209.043 658.488 208.349H656.488C656.488 209.362 656.726 210.29 657.251 211.084L658.92 209.982ZM658.488 208.349C658.488 207.722 658.612 207.317 658.776 207.0Line truncated
861L660.847 209.216C660.632 209.068 660.501 208.866 660.501 208.452H658.501ZM659.719 210.868C660.447 211.358 661.318 211.568 662.266 211.568V209.568C661.609 209.568 661.155 209.424 660.837 209.209L659.719 210.868ZM687.167 204.595H688.167V203.595H687.167V204.595ZM687.167 206.472V207.472H688.167V206.472H687.167ZM679.932 206.472H678.932V207.472H679.932V206.472ZM679.932 204.595V203.595H678.932V204.595H679.932ZM680.41 198.965H681.41V197.965H680.41V198.965ZM680.41 212.069V213.069H681.41V212.069H680.41ZM678.397 212.069H677.397V213.069H678.397V212.069ZM678.397 198.965V197.965H677.397V198.965H678.397ZM688.703 198.965H689.703V197.965H688.703V198.965ZM688.703 212.069V213.069H689.703V212.069H688.703ZM686.689 212.069H685.689V213.069H686.689V212.069ZM686.689 198.965V197.965H685.689V198.965H686.689ZM686.167 204.595V206.472H688.167V204.595H686.167ZM687.167 205.472H679.932V207.472H687.167V205.472ZM680.932 206.472V204.595H678.932V206.472H680.932ZM679.932 205.595H687.167V203.595H679.932V205.595ZM679.41 198.965V212.069H681.41V198.965H679.41ZM680.41 211.069H678.397V213.069H680.41V211.069ZM679.397 212.069V198.965H677.397V212.069H679.397ZM678.397 199.965H680.41V197.965H678.397V199.965ZM687.703 198.965V212.069H689.703V198.965H687.703ZM688.703 211.069H686.689V213.069H688.703V211.069ZM687.689 212.069V198.965H685.689V212.069H687.689ZM686.689 199.965H688.703V197.965H686.689V199.965ZM694.607 211.506L694.081 212.356L694.087 212.36L694.607 211.506ZM692.499 209.151L691.599 209.586L691.602 209.592L692.499 209.151ZM692.499 202.07L691.598 201.637L691.597 201.639L692.499 202.07ZM694.564 199.664L695.11 200.502L695.111 200.501L694.564 199.664ZM699.692 199.135L700.015 198.189L700.015 198.189L699.692 199.135ZM701.509 200.244L700.825 200.974L700.829 200.978L701.509 200.244ZM702.814 202.258L703.747 201.896L703.747 201.896L702.814 202.258ZM703.3 206.199V207.199H704.3V206.199H703.3ZM693.199 206.199H692.199V207.199H693.199V206.199ZM693.199 204.459V203.459H692.199V204.459H693.199ZM701.253 204.459V205.459H702.253V204.459H701.253ZM700.809 202.48L699.915 202.927L699.918 202.934L700.809 202.48ZM699.564 201.106L699.032 201.953L699.036 201.955L699.564 201.106ZM695.588 201.2L696.148 202.028L696.15 202.027L695.588 201.2ZM694.248 202.736L693.352 202.292L693.352 202.292L694.248 202.736ZM694.291 208.443L693.395 208.887L693.4 208.896L694.291 208.443ZM695.733 210.004L695.229 210.868L695.235 210.872L695.733 210.004ZM699.308 210.312L699.635 211.256L699.642 211.254L699.308 210.312ZM700.425 209.629L701.119 210.349L701.126 210.343L700.425 209.629ZM701.151 208.486L701.421 207.523L700.537 207.275L700.214 208.135L701.151 208.486ZM703.096 209.032L704.051 209.328L704.355 208.347L703.366 208.069L703.096 209.032ZM702.063 210.772L702.779 211.471L702.783 211.467L702.063 210.772ZM700.272 211.933L700.63 212.866L700.637 212.864L700.272 211.933ZM697.874 211.342C696.762 211.342 695.86 211.098 695.127 210.652L694.087 212.36C695.185 213.029 696.461 213.342 697.874 213.342V211.342ZM695.133 210.656C694.391 210.196 693.813 209.557 693.397 208.71L691.602 209.592C692.176 210.759 693.003 211.69 694.081 212.356L695.133 210.656ZM693.4 208.716C692.988 207.863 692.766 206.84 692.766 205.619H690.766C690.766 207.083 691.033 208.414 691.599 209.586L693.4 208.716ZM692.766 205.619C692.766 204.399 692.988 203.368 693.402 202.501L691.597 201.639C691.033 202.82 690.766 204.155 690.766 205.619H692.766ZM693.401 202.503C693.823 201.624 694.395 200.968 695.11 200.502L694.018 198.826C692.97 199.509 692.165 200.457 691.598 201.637L693.401 202.503ZM695.111 200.501C695.817 200.039 696.658 199.794 697.67 199.794V197.794C696.316 197.794 695.085 198.129 694.017 198.827L695.111 200.501ZM697.67 199.794C698.239 199.794 698.804 199.888 699.368 200.081L700.015 198.189C699.249 197.927 698.466 197.794 697.67 197.794V199.794ZM699.368 200.081C699.891 200.26 700.376 200.553 700.825 200.974L702.193 199.515C701.55 198.912 700.823 198.465 700.015 198.189L699.368 200.081ZM700.829 200.978C701.243 201.362 701.601 201.896 701.882 202.619L703.747 201.896C703.379 200.947 702.866 200.139 702.189 199.511L700.829 200.978ZM701.882 202.619C702.148 203.306 702.3 204.205 702.3 205.346H704.3C704.3 204.042 704.128 202.881 703.747 201.896L701.882 202.619ZM702.3 205.346V206.199H704.3V205.346H702.3ZM703.3 205.199H693.199V207.199H703.3V205.199ZM694.199 206.199V204.459H692.199V206.199H694.199ZM693.199 205.459H701.253V203.459H693.199V205.459ZM702.253 204.459C702.253 203.583 702.077 202.764 701.7 202.025L699.918 202.934C700.133 203.355 700.253 203.856 700.253 204.459H702.253ZM701.704 202.032C701.333 201.29 700.792 200.692 700.091 200.256L699.036 201.955C699.416 202.191 699.706 202.509 699.915 202.927L701.704 202.032ZM700.095 200.259C699.378 199.809 698.556 199.603 697.67 199.603V201.603C698.239 201.603 698.68 201.732 699.032 201.953L700.095 200.259ZM697.67 199.603C696.695 199.603 695.799 199.847 695.026 200.373L696.15 202.027C696.549 201.756 697.041 201.603 697.67 201.603V199.603ZM695.028 200.372C694.303 200.862 693.742 201.506 693.352 202.292L695Line truncated
979L726.255 211.088C726.758 211.843 727.439 212.419 728.273 212.812L729.125 211.002ZM727.921 209.982C727.649 209.571 727.489 209.043 727.489 208.349H725.489C725.489 209.362 725.728 210.29 726.252 211.084L727.921 209.982ZM727.489 208.349C727.489 207.722 727.614 207.317 727.778 207.059L726.089 205.988C725.661 206.662 725.489 207.475 725.489 208.349H727.489ZM727.78 207.054C727.983 206.73 728.25 206.48 728.594 206.294L727.644 204.535C726.998 204.883 726.474 205.373 726.086 205.993L727.78 207.054ZM728.594 206.294C729 206.075 729.457 205.907 729.971 205.794L729.543 203.84C728.862 203.989 728.227 204.219 727.644 204.535L728.594 206.294ZM729.978 205.793C730.551 205.663 731.126 205.56 731.704 205.485L731.444 203.501C730.805 203.585 730.169 203.699 729.536 203.842L729.978 205.793ZM731.702 205.485C732.494 205.383 733.131 205.307 733.614 205.257L733.408 203.268C732.902 203.32 732.247 203.398 731.447 203.501L731.702 205.485ZM733.624 205.256C734.158 205.196 734.691 205.084 735.101 204.848L734.104 203.114C734.06 203.14 733.865 203.216 733.398 203.269L733.624 205.256ZM735.092 204.853C735.713 204.505 735.953 203.894 735.953 203.299H733.953C733.953 203.344 733.945 203.316 733.975 203.256C733.991 203.226 734.013 203.195 734.04 203.166C734.068 203.138 734.094 203.12 734.114 203.109L735.092 204.853ZM735.953 203.299V203.23H733.953V203.299H735.953ZM735.953 203.23C735.953 202.204 735.667 201.268 734.973 200.565L733.55 201.971C733.778 202.201 733.953 202.574 733.953 203.23H735.953ZM734.977 200.57C734.267 199.841 733.27 199.568 732.188 199.568V201.568C732.961 201.568 733.347 201.762 733.546 201.966L734.977 200.57ZM732.188 199.568C731.092 199.568 730.095 199.808 729.317 200.408L730.538 201.992C730.852 201.75 731.363 201.568 732.188 201.568V199.568ZM729.317 200.408C728.682 200.898 728.176 201.465 727.869 202.127L729.683 202.969C729.819 202.675 730.081 202.344 730.538 201.992L729.317 200.408ZM729.112 201.606L727.201 200.924L726.528 202.807L728.44 203.49L729.112 201.606ZM727.784 202.259C728.072 201.587 728.433 201.115 728.845 200.794L727.615 199.217C726.889 199.783 726.34 200.551 725.946 201.471L727.784 202.259ZM728.846 200.793C729.317 200.424 729.82 200.175 730.359 200.035L729.855 198.099C729.04 198.311 728.291 198.688 727.614 199.218L728.846 200.793ZM730.364 200.033C730.969 199.872 731.553 199.794 732.12 199.794V197.794C731.368 197.794 730.61 197.898 729.849 198.101L730.364 200.033ZM732.12 199.794C732.463 199.794 732.883 199.836 733.386 199.93L733.755 197.965C733.166 197.854 732.619 197.794 732.12 197.794V199.794ZM733.398 199.932C733.821 200.007 734.238 200.162 734.652 200.411L735.681 198.696C735.07 198.33 734.424 198.082 733.743 197.962L733.398 199.932ZM734.656 200.413C734.995 200.614 735.308 200.935 735.578 201.426L737.33 200.462C736.918 199.713 736.372 199.106 735.676 198.693L734.656 200.413ZM735.578 201.426C735.809 201.845 735.966 202.489 735.966 203.435H737.966C737.966 202.299 737.782 201.283 737.33 200.462L735.578 201.426ZM735.966 203.435V212.069H737.966V203.435H735.966ZM736.966 211.069H734.953V213.069H736.966V211.069ZM735.953 212.069V210.295H733.953V212.069H735.953ZM734.953 209.295H734.85V211.295H734.953V209.295ZM733.949 209.862C733.876 210.014 733.727 210.229 733.454 210.507L734.881 211.908C735.246 211.537 735.552 211.144 735.752 210.727L733.949 209.862ZM733.454 210.507C733.248 210.717 732.942 210.927 732.501 211.116L733.292 212.954C733.909 212.688 734.451 212.346 734.881 211.908L733.454 210.507ZM732.501 211.116C732.131 211.276 731.628 211.376 730.96 211.376V213.376C731.816 213.376 732.605 213.249 733.292 212.954L732.501 211.116ZM731.267 211.568C732.192 211.568 733.045 211.386 733.778 210.966L732.783 209.231C732.424 209.437 731.934 209.568 731.267 209.568V211.568ZM733.774 210.968C734.465 210.576 735.019 210.049 735.397 209.378L733.655 208.396C733.475 208.715 733.2 208.994 732.787 209.228L733.774 210.968ZM735.393 209.385C735.761 208.746 735.953 208.054 735.953 207.326H733.953C733.953 207.689 733.86 208.038 733.659 208.389L735.393 209.385ZM735.953 207.326V205.483H733.953V207.326H735.953ZM734.184 204.843C734.229 204.789 734.268 204.758 734.286 204.746C734.304 204.733 734.311 204.73 734.301 204.735C734.291 204.74 734.271 204.75 734.237 204.762C734.204 204.775 734.161 204.789 734.107 204.805L734.672 206.724C734.851 206.671 735.031 206.606 735.195 206.524C735.349 206.447 735.554 206.323 735.721 206.123L734.184 204.843ZM734.12 204.801C733.878 204.869 733.585 204.933 733.236 204.991L733.564 206.964C733.966 206.897 734.332 206.819 734.659 206.727L734.12 204.801ZM733.248 204.989C732.886 205.045 732.533 205.095 732.188 205.139L732.444 207.123C732.806 207.076 733.175 207.024 733.552 206.966L733.248 204.989ZM732.202 205.138C731.853 205.178 731.565 205.213 731.341 205.242L731.602 207.225C731.811 207.198 732.086 207.164 732.43 207.125L732.202 205.138ZM731.342 205.242C730.761 205.318 730.207 205.442 729.685 205.619L730.324 207.514C730.712 207.383 731.136 207.286 731.601 207.225L731.342 205.242ZM729.692 205.616C729.106 205.809 728.589 206.117Line truncated
341C755.155 206.949 755.04 207.44 754.914 207.823L756.814 208.449ZM757.227 206.654C757.334 205.975 757.427 205.169 757.508 204.238L755.516 204.065C755.438 204.966 755.349 205.724 755.251 206.342L757.227 206.654ZM757.508 204.237L757.952 199.05L755.959 198.879L755.516 204.067L757.508 204.237ZM756.956 199.965H765.283V197.965H756.956V199.965ZM764.283 198.965V210.192H766.283V198.965H764.283ZM765.283 211.192H767.296V209.192H765.283V211.192ZM766.296 210.192V215.823H768.296V210.192H766.296ZM767.296 214.823H765.283V216.823H767.296V214.823ZM766.283 215.823V212.069H764.283V215.823H766.283ZM765.283 211.069H755.591V213.069H765.283V211.069ZM754.591 212.069V215.823H756.591V212.069H754.591ZM755.591 214.823H753.577V216.823H755.591V214.823ZM756.956 211.192H763.269V209.192H756.956V211.192ZM764.269 210.192V200.841H762.269V210.192H764.269ZM763.269 199.841H758.833V201.841H763.269V199.841ZM757.838 200.739L757.497 204.049L759.486 204.254L759.827 200.944L757.838 200.739ZM757.497 204.049C757.357 205.396 757.185 206.559 756.983 207.542L758.942 207.945C759.161 206.88 759.341 205.649 759.486 204.255L757.497 204.049ZM756.983 207.542C756.792 208.471 756.502 209.134 756.158 209.589L757.753 210.796C758.331 210.033 758.712 209.064 758.942 207.945L756.983 207.542ZM772.195 211.506L771.669 212.356L771.675 212.36L772.195 211.506ZM770.088 209.151L769.188 209.586L769.191 209.592L770.088 209.151ZM770.088 202.07L769.187 201.637L769.186 201.639L770.088 202.07ZM772.153 199.664L772.699 200.502L772.7 200.501L772.153 199.664ZM777.28 199.135L777.604 198.189L777.604 198.189L777.28 199.135ZM779.097 200.244L778.413 200.974L778.418 200.978L779.097 200.244ZM780.403 202.258L781.335 201.896L781.335 201.896L780.403 202.258ZM780.889 206.199V207.199H781.889V206.199H780.889ZM770.788 206.199H769.788V207.199H770.788V206.199ZM770.788 204.459V203.459H769.788V204.459H770.788ZM778.841 204.459V205.459H779.841V204.459H778.841ZM778.398 202.48L777.503 202.927L777.507 202.934L778.398 202.48ZM777.152 201.106L776.621 201.953L776.625 201.955L777.152 201.106ZM773.176 201.2L773.737 202.028L773.739 202.027L773.176 201.2ZM771.837 202.736L770.941 202.292L770.941 202.292L771.837 202.736ZM771.88 208.443L770.984 208.887L770.988 208.896L771.88 208.443ZM773.321 210.004L772.818 210.868L772.824 210.872L773.321 210.004ZM776.896 210.312L777.224 211.256L777.231 211.254L776.896 210.312ZM778.014 209.629L778.708 210.349L778.714 210.343L778.014 209.629ZM778.739 208.486L779.009 207.523L778.125 207.275L777.803 208.135L778.739 208.486ZM780.684 209.032L781.639 209.328L781.944 208.347L780.955 208.069L780.684 209.032ZM779.652 210.772L780.367 211.471L780.371 211.467L779.652 210.772ZM777.86 211.933L778.219 212.866L778.225 212.864L777.86 211.933ZM775.463 211.342C774.351 211.342 773.448 211.098 772.715 210.652L771.675 212.36C772.774 213.029 774.05 213.342 775.463 213.342V211.342ZM772.721 210.656C771.979 210.196 771.402 209.557 770.985 208.71L769.191 209.592C769.764 210.759 770.591 211.69 771.669 212.356L772.721 210.656ZM770.988 208.716C770.576 207.863 770.354 206.84 770.354 205.619H768.354C768.354 207.083 768.621 208.414 769.188 209.586L770.988 208.716ZM770.354 205.619C770.354 204.399 770.576 203.368 770.99 202.501L769.186 201.639C768.622 202.82 768.354 204.155 768.354 205.619H770.354ZM770.989 202.503C771.412 201.624 771.983 200.968 772.699 200.502L771.607 198.826C770.559 199.509 769.754 200.457 769.187 201.637L770.989 202.503ZM772.7 200.501C773.406 200.039 774.246 199.794 775.258 199.794V197.794C773.904 197.794 772.674 198.129 771.605 198.827L772.7 200.501ZM775.258 199.794C775.827 199.794 776.392 199.888 776.957 200.081L777.604 198.189C776.837 197.927 776.054 197.794 775.258 197.794V199.794ZM776.957 200.081C777.479 200.26 777.964 200.553 778.413 200.974L779.781 199.515C779.139 198.912 778.412 198.465 777.604 198.189L776.957 200.081ZM778.418 200.978C778.832 201.362 779.19 201.896 779.47 202.619L781.335 201.896C780.967 200.947 780.455 200.139 779.777 199.511L778.418 200.978ZM779.47 202.619C779.737 203.306 779.889 204.205 779.889 205.346H781.889C781.889 204.042 781.717 202.881 781.335 201.896L779.47 202.619ZM779.889 205.346V206.199H781.889V205.346H779.889ZM780.889 205.199H770.788V207.199H780.889V205.199ZM771.788 206.199V204.459H769.788V206.199H771.788ZM770.788 205.459H778.841V203.459H770.788V205.459ZM779.841 204.459C779.841 203.583 779.665 202.764 779.289 202.025L777.507 202.934C777.722 203.355 777.841 203.856 777.841 204.459H779.841ZM779.292 202.032C778.921 201.29 778.38 200.692 777.68 200.256L776.625 201.955C777.005 202.191 777.294 202.509 777.503 202.927L779.292 202.032ZM777.684 200.259C776.967 199.809 776.145 199.603 775.258 199.603V201.603C775.828 201.603 776.268 201.732 776.621 201.953L777.684 200.259ZM775.258 199.603C774.283 199.603 773.387 199.847 772.614 200.373L773.739 202.027C774.137 201.756 774.629 201.603 775.258 201.603V199.603ZM772.616 200.372C771.892 200.862 771.33 201.506 770.941 202.292L772.733 203.179C772.97 202.702 773.301 202.323 773.737 202.028L772.616 200.372ZM770.941 202.292C770.558 2Line truncated
211.002ZM798.753 209.982C798.481 209.571 798.321 209.043 798.321 208.349H796.321C796.321 209.362 796.559 210.29 797.084 211.084L798.753 209.982ZM798.321 208.349C798.321 207.722 798.445 207.317 798.609 207.059L796.92 205.988C796.492 206.662 796.321 207.475 796.321 208.349H798.321ZM798.612 207.054C798.815 206.73 799.081 206.48 799.426 206.294L798.475 204.535C797.83 204.883 797.306 205.373 796.917 205.993L798.612 207.054ZM799.426 206.294C799.832 206.075 800.289 205.907 800.802 205.794L800.375 203.84C799.694 203.989 799.059 204.219 798.475 204.535L799.426 206.294ZM800.809 205.793C801.382 205.663 801.958 205.56 802.535 205.485L802.276 203.501C801.637 203.585 801 203.699 800.368 203.842L800.809 205.793ZM802.533 205.485C803.326 205.383 803.962 205.307 804.445 205.257L804.24 203.268C803.733 203.32 803.078 203.398 802.278 203.501L802.533 205.485ZM804.455 205.256C804.99 205.196 805.522 205.084 805.933 204.848L804.936 203.114C804.892 203.14 804.696 203.216 804.229 203.269L804.455 205.256ZM805.924 204.853C806.544 204.505 806.784 203.894 806.784 203.299H804.784C804.784 203.344 804.776 203.316 804.807 203.256C804.822 203.226 804.844 203.195 804.872 203.166C804.899 203.138 804.926 203.12 804.945 203.109L805.924 204.853ZM806.784 203.299V203.23H804.784V203.299H806.784ZM806.784 203.23C806.784 202.204 806.499 201.268 805.805 200.565L804.382 201.971C804.609 202.201 804.784 202.574 804.784 203.23H806.784ZM805.809 200.57C805.098 199.841 804.101 199.568 803.02 199.568V201.568C803.793 201.568 804.178 201.762 804.377 201.966L805.809 200.57ZM803.02 199.568C801.923 199.568 800.927 199.808 800.149 200.408L801.37 201.992C801.683 201.75 802.194 201.568 803.02 201.568V199.568ZM800.149 200.408C799.513 200.898 799.007 201.465 798.7 202.127L800.514 202.969C800.651 202.675 800.913 202.344 801.37 201.992L800.149 200.408ZM799.944 201.606L798.033 200.924L797.36 202.807L799.271 203.49L799.944 201.606ZM798.615 202.259C798.904 201.587 799.265 201.115 799.676 200.794L798.446 199.217C797.72 199.783 797.172 200.551 796.777 201.471L798.615 202.259ZM799.677 200.793C800.149 200.424 800.652 200.175 801.19 200.035L800.686 198.099C799.871 198.311 799.123 198.688 798.445 199.218L799.677 200.793ZM801.196 200.033C801.8 199.872 802.385 199.794 802.952 199.794V197.794C802.199 197.794 801.441 197.898 800.681 198.101L801.196 200.033ZM802.952 199.794C803.295 199.794 803.715 199.836 804.218 199.93L804.586 197.965C803.998 197.854 803.45 197.794 802.952 197.794V199.794ZM804.23 199.932C804.653 200.007 805.069 200.162 805.483 200.411L806.512 198.696C805.902 198.33 805.255 198.082 804.575 197.962L804.23 199.932ZM805.487 200.413C805.826 200.614 806.139 200.935 806.41 201.426L808.162 200.462C807.75 199.713 807.204 199.106 806.508 198.693L805.487 200.413ZM806.41 201.426C806.64 201.845 806.798 202.489 806.798 203.435H808.798C808.798 202.299 808.614 201.283 808.162 200.462L806.41 201.426ZM806.798 203.435V212.069H808.798V203.435H806.798ZM807.798 211.069H805.784V213.069H807.798V211.069ZM806.784 212.069V210.295H804.784V212.069H806.784ZM805.784 209.295H805.682V211.295H805.784V209.295ZM804.78 209.862C804.707 210.014 804.558 210.229 804.286 210.507L805.713 211.908C806.077 211.537 806.383 211.144 806.583 210.727L804.78 209.862ZM804.286 210.507C804.079 210.717 803.773 210.927 803.333 211.116L804.123 212.954C804.741 212.688 805.282 212.346 805.713 211.908L804.286 210.507ZM803.333 211.116C802.962 211.276 802.459 211.376 801.791 211.376V213.376C802.648 213.376 803.436 213.249 804.123 212.954L803.333 211.116ZM802.099 211.568C803.024 211.568 803.877 211.386 804.609 210.966L803.615 209.231C803.255 209.437 802.766 209.568 802.099 209.568V211.568ZM804.605 210.968C805.296 210.576 805.851 210.049 806.229 209.378L804.486 208.396C804.307 208.715 804.031 208.994 803.619 209.228L804.605 210.968ZM806.225 209.385C806.592 208.746 806.784 208.054 806.784 207.326H804.784C804.784 207.689 804.692 208.038 804.491 208.389L806.225 209.385ZM806.784 207.326V205.483H804.784V207.326H806.784ZM805.016 204.843C805.061 204.789 805.1 204.758 805.117 204.746C805.136 204.733 805.143 204.73 805.132 204.735C805.122 204.74 805.102 204.75 805.068 204.762C805.035 204.775 804.992 204.789 804.939 204.805L805.503 206.724C805.683 206.671 805.862 206.606 806.027 206.524C806.181 206.447 806.385 206.323 806.552 206.123L805.016 204.843ZM804.952 204.801C804.71 204.869 804.417 204.933 804.067 204.991L804.396 206.964C804.797 206.897 805.164 206.819 805.491 206.727L804.952 204.801ZM804.079 204.989C803.717 205.045 803.364 205.095 803.02 205.139L803.276 207.123C803.637 207.076 804.006 207.024 804.384 206.966L804.079 204.989ZM803.034 205.138C802.684 205.178 802.397 205.213 802.173 205.242L802.434 207.225C802.642 207.198 802.918 207.164 803.262 207.125L803.034 205.138ZM802.174 205.242C801.592 205.318 801.039 205.442 800.516 205.619L801.156 207.514C801.543 207.383 801.968 207.286 802.433 207.225L802.174 205.242ZM800.523 205.616C799.937 205.809 799.42 206.117 799.01 206.561L800.478 207.919C800.625 207.76 800.836 207.619 801.149 207.516L80Line truncated
fill="#5B7BAB"
mask="url(#path-183-outside-2_114_2)"
/>
<path
d="M438 258H454.8V291.8H438V258Z"
fill="white"
stroke="#5B7BAB"
/>
<path
d="M437.5 280.2H455.2"
stroke="#5B7BAB"
/>
<path
d="M437.5 267.7H455.2"
stroke="#5B7BAB"
/>
<path
d="M856 257.4H872.8V291.2H856V257.4Z"
fill="white"
stroke="#5B7BAB"
/>
<path
d="M855.5 267.7H873.2"
stroke="#5B7BAB"
/>
<path
d="M855.5 280.2H873.2"
stroke="#5B7BAB"
/>
<path
d="M382.735 169.426C382.736 169.425 382.737 169.424 382.738 169.423C382.761 169.436 382.784 169.449 382.807 169.461C383.033 169.584 383.312 169.682 383.618 169.763C384.233 169.926 385.042 170.042 385.961 170.124C387.805 170.291 390.175 170.331 392.504 170.312C394.837 170.294 397.143 170.215 398.865 170.142C399.433 170.117 399.937 170.094 400.359 170.073L369.208 199.295C361.571 199.082 357.92 198.02 356.192 197.063C355.329 196.584 354.969 196.145 354.819 195.873C354.785 195.813 354.762 195.76 354.744 195.715C354.797 195.675 354.858 195.629 354.926 195.577C355.201 195.368 355.598 195.065 356.093 194.684C357.082 193.922 358.46 192.852 360.013 191.617C363.115 189.149 366.929 186.013 369.743 183.364C372.535 180.736 375.872 177.148 378.504 174.224C379.822 172.761 380.965 171.462 381.78 170.528C382.187 170.061 382.512 169.685 382.735 169.426Z"
stroke="#5B7BAB"
/>
<path
d="M744 161.9H857.5V187H744V161.9Z"
fill="white"
stroke="#5B7BAB"
/>
<path
d="M743.5 182.6H858"
stroke="#5B7BAB"
/>
<path
d="M743.5 177.6H858"
stroke="#5B7BAB"
/>
<path
d="M743.5 172.6H858"
stroke="#5B7BAB"
/>
<path
d="M743.5 167.6H858"
stroke="#5B7BAB"
/>
<rect
x="386"
y="284"
width="89"
height="3"
rx="1.5"
fill="#6F95CF"
/>
<rect
x="737"
y="284"
width="89"
height="3"
rx="1.5"
fill="#6F95CF"
/>
</g>
<defs>
<filter
id="filter0_d_114_2"
x="0"
y="0"
width="1008"
height="363"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
/>
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
/>
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite
in2="hardAlpha"
operator="out"
/>
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
/>
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow_114_2"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow_114_2"
result="shape"
/>
</filter>
<linearGradient
id="paint0_linear_114_2"
x1="504"
y1="0"
x2="504"
y2="203.1"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" />
<stop
offset="1"
stop-color="#FFFAED"
/>
</linearGradient>
<linearGradient
id="paint1_linear_114_2"
x1="504"
y1="354"
x2="504"
y2="200.667"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" />
<stop
offset="1"
stop-color="#B3DCDF"
/>
</linearGradient>
<linearGradient
id="paint2_linear_114_2"
x1="504"
y1="273.4"
x2="504"
y2="354"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#C0CBDD" />
<stop
offset="1"
stop-color="#ABB7CA"
/>
</linearGradient>
<clipPath id="clip0_114_2">
<rect
width="1000"
height="355"
fill="white"
transform="translate(4)"
/>
</clipPath>
</defs>
</svg>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,159 @@
<template>
<div class="hidden 2xl:flex w-full h-[100vh] overflow-hidden bg-white">
<div class="h-full overflow-hidden flex flex-col justify-between translate-y-1">
<div class="text-3xl text-sky-800 pl-10 pt-10">
<router-link to="/">
Вернуться на главную
</router-link>
</div>
<img
width="100%"
src="/images/not_found.jpg"
alt="404"
>
</div>
</div>
<div class="2xl:hidden flex w-full items-center justify-center h-[100vh] ">
<div
id="notfound"
class="notfound h-fit"
>
<div class="notfound-404">
<div />
<h1>404</h1>
</div>
<h2 class="mb-4">
Страница не найдена
</h2>
<router-link to="/">
На главную
</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
<style scoped>
#notfound {
position: relative;
}
#notfound .notfound {
position: absolute;
left: 50%;
top: 50%;
-webkit-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
transform: translate(-50%,-50%)
}
.notfound {
max-width: 460px;
width: 100%;
text-align: center;
line-height: 1.4
}
.notfound .notfound-404 {
position: relative;
width: 180px;
height: 180px;
margin: 0 auto 50px
}
.notfound .notfound-404>div:first-child {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: #ffa200;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
border: 5px dashed #000;
border-radius: 5px
}
.notfound .notfound-404>div:first-child:before {
content: '';
position: absolute;
left: -5px;
right: -5px;
bottom: -5px;
top: -5px;
-webkit-box-shadow: 0 0 0 5px rgba(0,0,0,.1) inset;
box-shadow: 0 0 0 5px rgba(0,0,0,.1) inset;
border-radius: 5px
}
.notfound .notfound-404 h1 {
font-family: cabin,sans-serif;
color: #000;
font-weight: 700;
margin: 0;
font-size: 90px;
position: absolute;
top: 50%;
-webkit-transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
left: 50%;
text-align: center;
height: 40px;
line-height: 40px
}
.notfound h2 {
font-family: cabin,sans-serif;
font-size: 33px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 7px
}
.notfound p {
font-family: cabin,sans-serif;
font-size: 16px;
color: #000;
font-weight: 400
}
.notfound a {
font-family: cabin,sans-serif;
display: inline-block;
padding: 10px 25px;
background-color: #8f8f8f;
border: none;
border-radius: 40px;
color: #fff;
font-size: 14px;
font-weight: 700;
text-transform: uppercase;
text-decoration: none;
-webkit-transition: .2s all;
transition: .2s all
}
.notfound a:hover {
background-color: #2c2c2c
}
</style>
@@ -0,0 +1,194 @@
<script>
import {mapGetters, mapActions, mapMutations} from "vuex";
export default {
name: 'LogIn',
components: {
},
data() {
return {
login: "",
password: ""
}
},
computed: {
...mapGetters('auth', ['errorMsg', "my_comp"])
},
mounted() {
localStorage.removeItem('token')
},
unmounted() {
this.login = ""
this.password = ""
},
methods: {
...mapActions('auth', ['signin']),
...mapMutations('auth', ["set_my_comp" ])
},
}
</script>
<template>
<main>
<div
class="login"
style="max-height: 100vh"
>
<div class="container sm:px-10">
<div class="block xl:grid grid-cols-2 gap-4">
<!-- BEGIN: Login Info -->
<div
style="z-index: 1;"
class="hidden relative xl:flex flex-col min-h-screen"
>
<div class="my-auto">
<div class="-intro-x text-white font-medium text-4xl leading-tight">
<img
alt="логотип"
class="mb-8"
src="/images/illustration.svg"
>
<div>
<a
href="/"
class="-intro-x flex items-center gap-2 pt-5 text-white hover:text-white"
>
<img
alt="логотип"
style="width: 40px; height: 40px"
src="/images/favicon-white.ico"
>
Live Monitor
</a>
</div>
</div>
<div class="-intro-x mt-5 text-lg text-white text-opacity-70 dark:text-slate-400">
Мониторинг СПС максимально приближенный
<br>
к режиму реального времени
</div>
</div>
</div>
<!-- END: Login Info -->
<!-- BEGIN: Login Form -->
<div class="h-screen xl:h-auto flex py-5 xl:py-0 my-10 xl:my-0">
<div class="my-auto mx-auto xl:ml-20 bg-white dark:bg-darkmode-600 xl:bg-transparent px-5 sm:px-8 py-8 xl:p-0 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto">
<h2 class="intro-x font-bold text-2xl xl:text-3xl dark:text-gray-100 text-center xl:text-left">
Вход
</h2>
<div class="intro-x mt-2 text-slate-400 xl:hidden text-center">
Мониторинг СПС максимально приближенный
к режиму реального времени
</div>
<div
id="login_form"
phx-update="ignore"
>
<div class="space-y-4 bg-white mt-10 dark:bg-darkmode-600">
<div phx-feedback-for="user[login]">
<label
for="user_login"
class="block text-sm leading-6 text-zinc-800 dark:text-white"
>
Логин
</label>
<input
id="user_login"
v-model="login"
type="text"
name="login"
class="block w-full rounded-lg border-zinc-300 py-[1px] px-[11px] text-zinc-900 focus:outline-none focus:ring-4 text-sm sm:leading-6 phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400 phx-no-feedback:focus:ring-zinc-800/5 border-zinc-300 focus:border-zinc-400 focus:ring-zinc-800/5 "
required=""
>
</div>
<div
class="relative"
phx-feedback-for="user[password]"
>
<label
for="user_password"
class="block text-sm leading-6 text-zinc-800 dark:text-white"
>
Пароль
</label>
<input
id="user_password"
v-model="password"
type="password"
name="password"
class="block w-full rounded-lg border-zinc-300 py-[1px] px-[11px] text-zinc-900 focus:outline-none focus:ring-4 text-sm sm:leading-6 phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400 phx-no-feedback:focus:ring-zinc-800/5 border-zinc-300 focus:border-zinc-400 focus:ring-zinc-800/5 "
required=""
>
<i
id="show_password"
class="cursor-pointer transition-all text-slate-500 hover:text-slate-800 absolute ri-eye-line bottom-[4px] right-[7px]"
/>
<i
id="hide_password"
class="hidden cursor-pointer transition-all text-slate-500 hover:text-slate-800 absolute ri-eye-off-line bottom-[4px] right-[7px]"
/>
<div
v-if="errorMsg"
class="text-red-600 mt-1 ml-1"
>
{{ errorMsg }}
</div>
</div>
<div class="intro-x flex text-slate-600 dark:text-slate-500 text-xs sm:text-sm mt-4">
<div class="flex items-center mr-auto">
<div phx-feedback-for="user[remember_me]">
<label class="flex items-center gap-4 text-sm leading-6 text-zinc-600 dark:text-white">
<input
type="hidden"
name="user[remember_me]"
value="false"
>
<input
id="user_remember_me"
:value="my_comp"
type="checkbox"
name="user[remember_me]"
class="rounded border-zinc-300 text-zinc-900 focus:ring-zinc-900"
@input="set_my_comp"
>
Мой компьютер
</label>
</div>
</div>
<span />
</div>
<div class="intro-x mt-5 xl:mt-8 text-center xl:text-left flex">
<button
type="button"
class="phx-submit-loading:opacity-75 rounded-lg bg-slate-700 hover:bg-slate-800 py-1 px-3 text-sm font-semibold leading-6 text-white active:text-white/80 mr-3 px-4"
phx-disable-with="Вход..."
style="width: 150px"
@click="signin({login, password})"
>
Войти
</button>
<button
type="button"
style="width: 150px"
onclick="toggleDark()"
class="btn btn-outline-secondary py-1 w-full xl:w-36 mt-3 xl:mt-0 align-top"
>
Темный режим
</button>
</div>
</div>
</div>
<div class="text-center my-6">
<span class="inline-block text-sm text-blue-500 align-baseline hover:text-blue-800"> Восстановить пароль можно через обращение в НИАЦ АО ВНИИЖТ</span>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</template>
@@ -0,0 +1,22 @@
<script>
import Login from './Login.vue'
export default {
name: 'Auth',
components: {
Login
},
data() {
return {}
},
computed: {
},
methods: {
}
}
</script>
<template>
<Login />
</template>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,96 @@
<template>
<div class="flex justify-between w-full border border-slate-300 p-1 flex-col md:flex-row hover:bg-slate-300 hover:border-slate-600 transition-all">
<div class="flex gap-1 items-center">
<div>
<div class="flex justify-center">
<div class="form-check form-switch">
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
value=""
class="sr-only peer"
checked
>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-2 peer-focus:ring-green-700 dark:peer-focus:ring-green-800 dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-green-600" />
</label>
</div>
</div>
</div>
<div>Название: askr_locations_speed_limit_calc</div>
<div>Описание: Поиск случаев превышения автомобилями скорости передвижения по автодорогам</div>
<div
id="calc_period_59"
current-period="3600000"
phx-hook="TimeConvert"
>
Период: кажды(й/е) час
</div>
</div>
<div class="flex gap-2">
<div>
<TaskHistory>
<template #button>
<i class="cursor-pointer ri-eye-line" />
</template>
</TaskHistory>
</div>
<div>
<TaskModal>
<template #button>
<i class="cursor-pointer ri-pencil-line" />
</template>
</TaskModal>
</div>
<div x-data="{ open: false }">
<i
x-show="!open"
class="cursor-pointer ri-delete-bin-line text-red-500"
@click="open = true"
/>
<div
class="flex my_margin-2 items-center"
x-show="open"
style="display: none;"
@click="open = false"
>
<span
phx-click="delete_period"
phx-value-id="59"
class="cursor-pointer p-0.5 border rounded text-red-600 border-red-600 mr-2"
>Удалить</span>
<span
class="cursor-pointer rounded p-0.5 border border-slate-600 text-slate-600"
@click="open = false"
>Отмена</span>
</div>
</div>
</div>
</div>
</template>
<script>
import TaskModal from './TaskModal.vue'
import TaskHistory from './TaskHistory.vue'
import ButtonModal from '@molecules/ButtonModal/index.vue'
export default {
name: 'Task',
components: {
TaskModal,
TaskHistory
},
props: {
index: {
default: 0,
type: Number
}
},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,47 @@
<template>
<ButtonModal
forcedBtnClass=" "
containerClass="pt-10 px-4 w-[80vw] 2xl:w-[70vw]"
:onToggle="onToggle"
>
<template #button>
<span>
<slot name="button" />
</span>
</template>
1234
</ButtonModal>
</template>
<script>
import ButtonModal from '@molecules/ButtonModal/index.vue'
import {data} from './historyData'
export default {
name: 'TaskHistory',
components: {
ButtonModal,
},
props: {
taskId: {
default: "",
type: String
}
},
data() {
return {
task: "",
task_description: "",
task_name: ""
}
},
computed: {
},
methods: {
onToggle: ({isOpen}) => {
console.log(isOpen)
}
}
}
</script>
@@ -0,0 +1,256 @@
<template>
<ButtonModal
forcedBtnClass=" "
containerClass="pt-10 px-4 w-[80vw] 2xl:w-[70vw]"
:onToggle="onToggle"
>
<template #button>
<span>
<slot name="button" />
</span>
</template>
<form
id="period_modal_form"
phx-submit="form_validation"
class="flex flex-col gap-3 p-4 w-full min-h-[70vh]"
>
<div class="relative hidden">
<label
for="id"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
Период (мс)
</label>
<input
id="id"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder="Период (мс)"
name="id"
type="text"
value=""
autocomplete="off"
>
</div>
<h2 class="font-bold text-slate-600 text-xl">
Описание
</h2>
<div class="flex justify-between items-center w-full gap-3">
<div class="min-w-[150px]">
<label
for="dt_start"
class="block font-w-700 text-sm text-gray-900 dark:text-gray-300"
>Дата начала</label>
<div
class="block mb-2 text-sm text-gray-500 dark:text-gray-300"
>
календарь
</div>
<Datepicker
:defaultDate="dtStart"
:enableTime="true"
:onchange="setDtStart"
/>
</div>
<div class="w-fit flex gap-2 items-end">
<div class="px-4">
<div class="font-semibold">
Период
</div>
<div class="flex w-[800px] gap-2">
<div class="relative ">
<label
for="month"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
месяц
</label>
<input
id="period_month"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder=""
name="month"
type="number"
value=""
autocomplete="off"
>
</div><div class="relative ">
<label
for="day"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
день
</label>
<input
id="period_day"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder=""
name="day"
type="number"
value=""
autocomplete="off"
>
</div><div class="relative ">
<label
for="hour"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
час
</label>
<input
id="period_hour"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder=""
name="hour"
type="number"
value=""
autocomplete="off"
>
</div><div class="relative ">
<label
for="minute"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
минута
</label>
<input
id="period_minute"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder=""
name="minute"
type="number"
value=""
autocomplete="off"
>
</div><div class="relative ">
<label
for="second"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
секунда
</label>
<input
id="period_second"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder=""
name="second"
type="number"
value=""
autocomplete="off"
>
</div>
</div>
</div>
</div>
<div class="flex justify-start items-start flex-col mb-2 w-full col-span-4 w-fit pl-4">
<label
for="dt_start"
class="block mb-2 text-sm font-medium text-gray-900 text-left dark:text-gray-300 font-semibold"
>Задача включена</label>
<div class="block mb-2 text-sm text-gray-500 dark:text-gray-300">
переключить
</div>
<div class="form-check form-switch">
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
value=""
class="sr-only peer"
:checked="false"
>
<div class="w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-2 peer-focus:ring-green-700 dark:peer-focus:ring-green-800 dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full after:content-[''] after:absolute after:top-0.5 after:start-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 transition-all peer-checked:bg-green-600" />
</label>
</div>
</div>
</div>
<div class="relative ">
<label
for="name"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
Название задачи
</label>
<input
id="name"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder="Название задачи"
name="name"
type="text"
value=""
autocomplete="off"
>
</div>
<fwb-textarea
v-model="task_description"
:rows="4"
label="Описание задачи"
placeholder="Описание задачи"
class="w-full"
/>
<fwb-textarea
v-model="task"
:rows="14"
label="Тело задачи"
placeholder="тело задачи"
class="w-full"
/>
<button
v-if="taskId != 'new'"
id="btn_update_period"
type="submit"
class="cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full px-5 py-1 text-center transition-all w-[150px]"
event="update_period"
from-id="period_modal_form"
phx-hook="SendFormData"
>
Обновить задачу
</button>
<button
v-if="taskId == 'new'"
id="btn_create_period"
type="submit"
class="cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full px-5 py-1 text-center transition-all w-[150px]"
event="create_period"
from-id="period_modal_form"
phx-hook="SendFormData"
>
Добавить задачу
</button>
</form>
</ButtonModal>
</template>
<script>
import ButtonModal from '@molecules/ButtonModal/index.vue'
import Datepicker from "@molecules/Datepicker/index.vue"
import { FwbTextarea } from 'flowbite-vue'
export default {
name: 'TaskModal',
components: {
Datepicker,
ButtonModal,
FwbTextarea
},
props: {
taskId: {
default: "",
type: String
}
},
data() {
return {
task: "",
task_description: "",
task_name: ""
}
},
computed: {
},
methods: {
onToggle: ({isOpen}) => {
console.log(isOpen)
}
}
}
</script>
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,161 @@
<template>
<div class="flex flex-col gap-3 bg-light h-full w-full col-span-12 rounded-xl p-2">
<TaskModal taskId="new">
<template #button>
<button class="cursor-pointer bg-primary hover:bg-transparent text-white font-semibold hover:text-primary py-1 text-ssm px-4 border border-primary rounded-lg text-center transition_up w-[200px]">
Добавить задачу
</button>
</template>
</TaskModal>
<div
id="phx-F6ih6f3ULA2TYReS-4-0"
data-phx-component="4"
class="flex flex-col gap-2"
>
<div>
<button
type="button"
class="hidden"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;}]]"
/>
<div
id="period_table_modal"
class="fixed z-[10000] inset-0 hidden"
phx-remove="[[&quot;transition&quot;,{&quot;time&quot;:200,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;fade-out&quot;],[],[]]}]]"
>
<!-- Modal container -->
<div class="h-screen flex items-center justify-center p-4">
<!-- Overlay -->
<div
class="absolute z-0 inset-0 bg-gray-500 opacity-75"
aria-hidden="true"
/>
<!-- Modal box -->
<div
class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl "
role="dialog"
aria-modal="true"
tabindex="0"
autofocus=""
phx-window-keydown="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#period_table_modal-return&quot;}]]"
phx-click-away="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#period_table_modal-return&quot;}]]"
phx-key="escape"
>
<button
type="button"
class="absolute top-2 right-2 text-gray-400 flex space-x-1 items-center"
aria_label="close modal"
phx-click="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#period_table_modal-return&quot;}]]"
>
<span class="text-sm">(esc)</span>
<i class="ri ri-close-line text-2xl" />
</button>
<div class="min-w-[350px] min-h-[400px] py">
<div class=" p-4 font-bold" />
<div
id="logs_loader"
style="display: none; width: 95vw; height: 95vh;"
class="w-full h-full"
>
<div
style=""
class="h-full absolute inset-0 flex flex-row items-center grow transition items-center justify-center appear_from_opacity-3 z-30 rounded p-2"
>
<div class="flex flex-row items-center grow transition items-center justify-center rounded shadow bg-white h-full">
<div
type-node="loader"
class="flex gap-3 items-center justify-center "
>
<span class="text-slate-600 text-md font-medium">Загрузка</span>
<div role="status">
<svg
aria-hidden="true"
class="mr-2 w-6 h-6 text-gray-300 animate-spin dark:text-gray-400 fill-slate-700"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-2 px-4 pb-4">
<div class="flex gap-2 items-end">
<div class="relative ">
<label
for=""
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
Название
</label>
<input
id="period_view_name"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder="Название"
name=""
type="text"
value=""
disabled=""
autocomplete="off"
>
</div>
<div
id="display_modal_period"
phx-hook="TimeConvert"
/>
</div>
<div
id="period_report"
style="width: 95vw"
phx-hook="MakeTabulator"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<Task
v-for="_i in tasks"
:key="`${_i}_key`"
:index="_i"
/>
</div>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import Task from './Task.vue'
import TaskModal from './TaskModal.vue'
export default {
name: 'Cron',
components: {
Task,
TaskModal,
},
data() {
},
computed: {
tasks () {
return [1,2,3,4]
}
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,162 @@
<template>
<div class="grid grid-cols-12 gap-6 col-span-12 h-full">
<form
phx-submit="fetch_pack"
class="rounded-md bg-light px-4 pt-4 pb-6 gap-2 shadow lg:col-span-4 xl:col-span-3 2xl:col-span-2 col-span-12"
autocomplete="off"
>
<h1 class="font-medium leading-tight text-xl mb-4 text-slate-600">
Последние пакеты
</h1>
<div class="grid gap-3 mb-3 ">
<div class="relative ">
<label
for="imei"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
imei
</label>
<input
id="selected_imei"
required=""
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder="imei"
name="imei"
type="text"
autocomplete="off"
:value="imei"
@input="(e) => setImei(e.target.value)"
>
</div>
</div>
<div class="mb-3">
<label
for="dt_start"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Дата начала</label>
<Datepicker
:defaultDate="dtStart"
:enableTime="true"
:onchange="setDtStart"
/>
</div>
<div class="mb-3">
<label
for="dt_finish"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Дата окончания</label>
<Datepicker
:defaultDate="dtFinish"
:enableTime="true"
:onchange="setDtFinish"
/>
</div>
<div class="grid gap-3 grid-cols-4">
<button
type="button"
class="col-span-4 lg:col-span-4 cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full sm:w-auto px-5 py-1 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 transition_up"
@click="uploadData"
>
Выгрузить
</button>
<MachinesModal />
<ButtonModal btnClass="cursor-pointer col-span-4 bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up">
<template #button>
<div id="question">
<i class="ri-question-mark" />
</div>
</template>
<div class="overflow-auto min-w-[70vw] h-[70vh] p-4">
<div style="display: flex; flex-direction: column; gap: 1rem;">
<span style="font-size: 1.1em; font-weight: bold;">Страница предназначена для получения сведений о последних полученных пакетах от машин<br></span><span style="border-left: 5px solid rgba(0, 0, 0, 0.45); padding: 10px 5px;"><span style="font-weight: bold;">Работа со страницей</span><br><span>Поля выбора находятся на странице. Чтобы загразить данные необходимо -<ol style="font-size: 0.9em; margin-bottom: 0px;"><li>1. ввести пакеты через запятую</li><li>2. ввести imei или выбрать машину/устройство</li><li>3. нажать на кнопку 'загрузить' и дождаться загрузки</li><li style="font-size: 0.9em; color: gray;">(опционально) изменить даты</li></ol></span></span>
</div>
</div>
</ButtonModal>
</div>
</form>
<div class="lg:col-span-8 xl:col-span-9 2xl:col-span-10 col-span-12 bg-light shadow">
<div
v-if="pageState == 'await'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md "
>
<span> Выберите машину и нажмите выгрузить</span>
</div>
<div
v-if="pageState == 'loading'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md w-full h-full flex items-center justify-center"
>
<Spinner />
</div>
<div
v-if="pageState == 'isLoaded'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md "
>
<Tabulator v-bind="tabulatorOtps" />
</div>
</div>
</div>
</template>
<script>
import ButtonModal from '@molecules/ButtonModal/index.vue'
import Datepicker from "@molecules/Datepicker/index.vue"
import {mapGetters, mapMutations, mapActions} from 'vuex'
import Tabulator from "@molecules/Tabulator/index.vue"
import Spinner from "@molecules/Spinner/index.vue"
import MachinesModal from "@organisms/MachinesModal/index.vue"
export default {
name: 'LastPacks',
components: {
Datepicker,
ButtonModal,
Tabulator,
Spinner,
MachinesModal
},
data() {
return {
packNum: null,
}
},
computed: {
...mapGetters('last_packs', ['dtStart', 'dtFinish', 'imei', 'pageState', 'packsData']),
tabulatorOtps() {
return {
dataSource: this.packsData,
columns: [
{
title: "Нормер пакета",
field: "pack_number",
width: 150
},
{
title: "Количество записей",
field: "count",
width: 150
},
{
title: "Последняя дата пакета",
field: "pack_dt",
width: 150,
render: "{{ dt_format (dt_shift pack_dt 3 'hour') }}"
}
],
height: "550px"
}
}
},
methods: {
...mapMutations('last_packs', [
"setDtStart",
"setDtFinish",
"setImei"
]),
...mapActions('last_packs', ['uploadData'])
}
}
</script>
@@ -0,0 +1,173 @@
<template>
<div class="grid grid-cols-12 gap-6 col-span-12 h-full">
<form
phx-submit="fetch_pack"
class="rounded-md bg-light px-4 pt-4 pb-6 gap-2 shadow lg:col-span-4 xl:col-span-3 2xl:col-span-2 col-span-12"
autocomplete="off"
>
<h1 class="font-medium leading-tight text-xl mb-4 text-slate-600">
Последние пакеты
</h1>
<div class="grid gap-3 mb-3 ">
<div class="relative ">
<label
for="imei"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300 text-left "
>
Номер пакета
</label>
<input
id="selected_imei"
required=""
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full py-1 pl-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 "
placeholder="Номер пакета"
name="imei"
type="text"
autocomplete="off"
:value="imei"
@input="(e) => setImei(e.target.value)"
>
</div>
</div>
<div class="grid gap-3 grid-cols-4">
<button
type="button"
class="col-span-4 lg:col-span-4 cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full sm:w-auto px-5 py-1 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 transition_up"
@click="uploadData"
>
Выгрузить
</button>
<ButtonModal btnClass="cursor-pointer col-span-4 bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up">
<template #button>
<div id="question">
<i class="ri-question-mark" />
</div>
</template>
<div class="overflow-auto min-w-[70vw] h-[70vh] pt-10 px-4">
<div style="display: flex; flex-direction: column; gap: 1rem;">
<span style="font-size: 1.1em; font-weight: bold;">Страница предназначена для получения сведений о последних полученных пакетах, в результате выдающая список машин, которые имели данный пакет за 3х месячный период (иначе запрос становится слишком долгим)<br></span><span style="border-left: 5px solid rgba(0, 0, 0, 0.45); padding: 10px 5px;"><span style="font-weight: bold;">Работа со страницей</span><br><span>Поля выбора находятся на странице. Чтобы загразить данные необходимо -<ol style="font-size: 0.9em; margin-bottom: 0px;"><li>1. ввести номер пакета</li><li>2. нажать на кнопку 'загрузить' и дождаться загрузки</li></ol></span></span>
</div>
</div>
</ButtonModal>
</div>
</form>
<div class="lg:col-span-8 xl:col-span-9 2xl:col-span-10 col-span-12 bg-light shadow">
<div
v-if="pageState == 'await'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md "
>
<span> Выберите машину и нажмите выгрузить</span>
</div>
<div
v-if="pageState == 'loading'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md w-full h-full flex items-center justify-center"
>
<Spinner />
</div>
<div
v-if="pageState == 'isLoaded'"
id="report_packs"
class="tab-pane active relative text-center min-h-[300px] relative rounded-md "
>
<Tabulator v-bind="tabulatorOtps" />
</div>
</div>
</div>
</template>
<script>
import ButtonModal from '@molecules/ButtonModal/index.vue'
import {mapGetters, mapMutations, mapActions} from 'vuex'
import Tabulator from "@molecules/Tabulator/index.vue"
import Spinner from "@molecules/Spinner/index.vue"
export default {
name: 'LastPacksNum',
components: {
ButtonModal,
Tabulator,
Spinner,
},
data() {
return {
packNum: null,
}
},
computed: {
...mapGetters('last_packs_num', ['packNum', 'pageState', 'packsData']),
tabulatorOtps() {
console.log(this.packsData)
return {
dataSource: this.packsData,
columns: [
{
title: "Нормер пакета",
field: "pack_number",
width: 150
},
{
title: "Последняя дата пакета",
field: "pack_dt",
width: 150,
render: "{{ dt_format (dt_shift pack_dt 3 'hour') }}"
},
{
title: "Тип/Модификация",
field: "machine_type",
width: 150
},
{
title: "Номер",
field: "zav_nomer",
width: 150
},
{
title: "id машины",
field: "machine_id",
width: 150
},
{
title: "id устройства",
field: "device_id",
width: 150
},
{
title: "IMEI",
field: "imei",
width: 150
},
{
title: "Дорога",
field: "railway_name",
width: 150
},
{
title: "Предприятие",
field: "org_name",
width: 150
},
{
title: "Номер 8 зн.",
field: "nomer_zn8",
width: 150
},
{
title: "Номер устройства",
field: "device_number",
width: 150
}
]
}
}
},
methods: {
...mapMutations('last_packs_num', [
"setPackNum"
]),
...mapActions('last_packs_num', ['uploadData'])
}
}
</script>
@@ -0,0 +1,49 @@
<script>
import {mapGetters, mapActions, mapMutations, useStore} from "vuex"
import Button from "@/components/1_atoms/Button.vue"
import Tabulator from "@molecules/Tabulator/index.vue"
export default {
name: 'AddUsers',
components: {
Button,
Tabulator
},
setup() {
const store = useStore()
store.dispatch('add_users/uploadData')
},
data() {
return {
}
},
computed: {
...mapGetters('add_users', ['adminData']),
},
unmounted() {
const store = useStore()
store.dispatch('add_users/resetStore')
},
methods: {
}
}
</script>
<!-- eslint-disable -->
<template>
{{ console.log('adminData', adminData)
}}
<div class="rounded w-full pt-4 pb-1 bg-light cursor-pointer transition-all">
<div class="mx-2 mb-3">
<a :href="`/html/admin/manage/new?prev_page=/\?page_display_mode=users`">
<Button title="Создать" classBtn="cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full px-5 py-1 text-center transition-all " />
</a>
</div>
<div id="admin_list" class="tab-pane m-4 active bg-light relative col-span-12 text-center min-h-[300px] rounded-md tabulator">
<Tabulator v-bind="adminData" />
</div>
</div>
</template>
@@ -0,0 +1,105 @@
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import Spinner from "@molecules/Spinner/index.vue"
import {PushAfterTimeout} from '@store/hooks/PushAfterTimeout'
import Accordion from '@atoms/Accordion.vue'
export default {
name: 'FinderPacks',
components: {
Spinner,
Accordion
},
setup() {
const store = useStore()
store.dispatch('finder_packs/uploadData')
},
data() {
return {
}
},
computed: {
...mapGetters('finder_packs', ['packsGroups', 'searchValue', 'isOpen']),
},
unmounted() {
const store = useStore();
store.dispatch('finder_packs/resetStore')
},
methods: {
...mapActions('finder_packs', ['updateSearch']),
updateSearchValue: function(e) {
const searchInput = new PushAfterTimeout('searchInput', 1500, 'right-[25px] top-[53%]')
if (searchInput.input) {
setTimeout(() => {
this.updateSearch(e.target.value)
}, 1500)
}
},
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="rounded w-full transition-all min-h-[500px] relative">
<div v-if="!packsGroups" id="dataLoader" class="h-full absolute inset-0 flex flex-row items-center grow transition items-center justify-center bg-light appear_from_opacity-3 z-30 rounded p-2">
<Spinner style="padding: 0"/>
</div>
<div v-if="packsGroups" phx-change="update_pack_filter" class="p-5 flex flex-col gap-4 bg-light relative items-center">
<div id="searchInput" class="w-full flex flex-col gap-2" >
<div class="font-bold">Поиск (название и номер): </div>
<input v-bind:value="searchValue" v-on:change="updateSearchValue" :status="status" class="pl-2 py-1 rounded-r-md w-full max-h-[29px] text-sm border-none focus-visible:outline-blue-300" placeholder="Название и номер через пробел"/>
</div>
<div class="border-2 rounded-lg w-full bottom-0"></div>
</div>
<div v-if="packsGroups?.length === 0" id="no_data" class="w-full min-h-[80vh] bg-light dark:bg-slate-900 dark:text-slate-300 relative">
<div class="flex items-center justify-center text-lg font-bold pt-5">Пакеты не найдены</div>
</div>
<div class="bg-light">
<div v-if="packsGroups?.length > 0" class="grid grid-cols-12 gap-3 p-4">
<div :key="idx" v-for="(group, idx) in packsGroups" class="col-span-12 sm:col-span-6 lg:col-span-4">
<Accordion :isOpen="isOpen" :id="`pack_group_${group.pack_group_id}`">
<template #header>
<div v-if="group.order_index" id="packsInfoTitle" class="font-bold text-sm">
<span class="text-slate-500 font-bold">
{{ group.order_index }}.
</span>
<span class="font-bold">
{{ group.title }}
</span></div>
</template>
<template #body>
<div class="flex flex-col gap-2">
<table >
<thead>
<tr>
<th class="items-center justify-center pb-2">Название</th>
</tr>
</thead>
<tbody>
<tr v-for="struct in group.pack_structs" >
<td class="items-center justify-center py-2">
<div class="flex gap-2 w-full">
<div class="w-full text-left decoration-dashed cursor-pointer hover:text-slate-800 transition-all">
{{ struct.name }}
</div>
<a class="bg-slate-500 hover:bg-slate-700 text-white text-xs font-bold py-1 px-3 rounded transition-all h-fit" target="_blank" :href="`/html/packs/${group.link}?pack=${struct.name}`">
таблица
</a>
<a class="bg-slate-500 hover:bg-slate-700 text-white text-xs font-bold py-1 px-3 rounded transition-all h-fit" target="_blank" :href="`/admin_panel/packs/${group.pack_group_id}/${struct.id}`">карточка</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
</Accordion>
</div>
</div>
</div>
</div>
</template>
@@ -0,0 +1,129 @@
<!-- eslint-disable -->
<script>
import {ref, toRaw, computed} from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex";
import {useRoute, useRouter} from 'vue-router'
import Button from "@/components/1_atoms/Button.vue";
import DoubleSwitch from "@/components/1_atoms/DoubleSwitch.vue";
import Chart from "@/components/2_molecules/Chart/index.vue";
import ECharts from '@store/hooks/Echarts';
export default {
name: 'Charts',
components: {
Button,
Chart,
DoubleSwitch
},
setup() {
const route = useRoute()
const path = computed(() => route.path)
const query = computed(() => route.query)
const typeCharts = path.value === '/' && query?.value?.mode && query?.value?.type ? query?.value?.type : ''
const isChartTypesUrl = typeCharts === 'main' ? false : true
return {
isChartTypesUrl
}
},
data() {
return {
isChartTypes: this.isChartTypesUrl,
}
},
provide() {
return {
isChecked: computed(() => this.isChartTypes),
}
},
watch: {
activeChartData: {
handler(newValue, oldValue) {
console.log('newValue - activeChartData', newValue)
if (newValue.id && newValue.type && newValue.data) {
const chart = new ECharts(newValue.id)
const isSelectedLegend = this.legendFiltersParams.length > 0 && this.legendFiltersParams[0].id === newValue.id
console.log('this.legendFiltersParams', this.legendFiltersParams)
newValue.data.legendFilterData = isSelectedLegend ? this.legendFiltersParams : []
this.setTypeFunc(newValue.type, chart, newValue.data)
}
},
// deep: true
},
activeMenuChartData: {
handler(newValue, oldValue) {
// console.log('newValue - activeMenuChartData', newValue)
if (newValue.groupByInfo && newValue.groupByInfo.chartId) {
const chart = new ECharts(newValue.groupByInfo.chartId)
chart.setMenuChart(newValue)
if (newValue.groupByInfo.isDownloadChart) {
chart.downloadChart(newValue, this.updateCharts)
}
}
},
},
},
computed: {
...mapGetters('machines', ['selectedSelects', 'selectsData', 'toggleFilter', 'chartsData', 'activeChartData', 'prevGroupByData', 'activeMenuChartData', 'openMenuChart', 'legendFiltersParams']),
},
mounted () {
},
methods: {
...mapMutations('machines', ['setSelectedSelect', 'setToggleFilter']),
...mapActions('machines', ['updateSelects', 'updateCharts']),
updateSelectedSelects: function(value) {
if (value.length > 0) {
this.updateSelects({key: 'set', value: value})
}
},
removedOption: function(value) {
this.updateSelects({key: 'removed', value: value})
},
onToggleCharts: function(value) {
console.log('onToggleCharts', value)
const path = this.$route.path
const query = this.$route.query
const chartsType = value ? 'types' : 'main'
this.$router.push({ path: path, query: {...query, type: chartsType} })
this.isChartTypes = value
},
setTypeFunc: function(type, chart, data) {
switch(type) {
case 'pie': return chart.makePieCharts({...data, type: type})
case 'mount': return chart.makeBarMountCharts({...data, type: type, height: this.height})
case 'mainMl': return chart.makeBarCharts({...data, type: type})
case 'default': return chart.makeBarTypesCharts({...data, type: type})
case 'types': return chart.makeBarTypesCharts({...data, type: type})
case 'without_first_layout': return chart.makeBarTypesCharts({...data, type: type})
case 'ml': return chart.makeBarTypesCharts({...data, type: type})
}
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="px-2 sm:px-5 pt-1 bg-light">
<div class="relative">
<div class="doubleSwitch flex justify-end mt-3 mb-2">
<DoubleSwitch name="chartsView" @switched="onToggleCharts" firstColor="#8b5cf6" secondColor="#2563eb" firstTitle="Общий вид" secondTitle="По типам" />
</div>
<div v-if="!isChartTypes" id="main" class="relative flex flex-wrap justify-center w-full h-full mb-3" >
<div v-for="(chart, idx) in chartsData.main">
<Chart :idx="idx" :id="chart.id" :type="chart.type" :height="chart.height" :maxHeight="chart.maxHeight" :data="chart.data"/>
</div>
</div>
<div v-if="isChartTypes" id="types" class="relative flex flex-wrap justify-center w-full h-full mb-3" >
<div v-for="(chart, idx) in chartsData.types">
<Chart :idx="idx" :id="chart.id" :type="chart.type" :height="chart.height" :maxHeight="chart.maxHeight" :data="chart.data"/>
</div>
</div>
</div>
</div>
</template>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
@@ -0,0 +1,170 @@
<!-- eslint-disable -->
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import Modal from '@molecules/Modal/index.vue'
import Accordion from '@atoms/Accordion.vue'
import PackView from '@organisms/PackView/index.vue'
import Spinner from "@molecules/Spinner/index.vue"
import {getMachineInfo} from './helpers'
export default {
name: 'DataModal',
components: {
Modal,
Accordion,
PackView,
Spinner
},
props: {
open: {
type: Boolean,
default: false
},
close: {
type: Function,
default: () => {}
},
id: {
type: String,
default: ""
},
text: {
type: String,
default: ""
},
headerClass: {
type: String,
default: ""
},
containerClass: {
type: String,
default: ""
}
},
data () {
return {
machineMainInfo: null,
machineOverInfo: null
}
},
updated () {
this.$nextTick(function () {
if (this.data && this.id && this.open) {
// const newMap = new CoordsMap(this.id)
// newMap.loadMap(this.data.coords, this.data.machine)
console.log('this.data', this.data)
this.machineMainInfo = getMachineInfo(this.machineInfo.machine_info, true)
this.machineOverInfo = getMachineInfo(this.machineInfo.machine_info, false)
}
})
},
computed: {
...mapGetters('machines', ['machineInfo', 'reportPacks']),
},
methods: {
...mapActions('machines', ['loadPack']),
}
}
</script>
<!-- eslint-disable -->
<template>
<Modal
v-if="open"
:close="close"
:isOpen="true"
>
<div class="border-b p-4 font-bold">
Последние данные
</div>
{{ console.log('reportPacks', reportPacks)
}}
<div :id="id" class="w-[95vw] h-[85vh] m-auto p-5">
<div v-if="!machineInfo" id="dataLoader" class=" h-full flex justify-center items-center">
<Spinner style="padding: 0"/>
</div>
<div v-if="machineInfo" class="w-full flex flex-col gap-5">
<div class="w-full">
<Accordion id="machineInfo">
<template #header>
<div v-if="machineInfo?.machine_name" id="machineInfoTitle" class="font-bold text-xl">{{ machineInfo.machine_name }}</div>
</template>
<template #body>
<div v-if="machineInfo.machine_info?.length > 0" id="machineInfoBody" class="flex w-full justify-start 2xl:justify-between gap-3 flex-wrap">
<div class="flex p-4">
<div class="flex mr-8 flex-col bg-slate-200 p-3 rounded">
<h2 clas="font-bold flex items-center"><i class="ri-flashlight-line mr-1" /> Основная информация</h2>
<div class="mt-2 p-2 rounded-md flex flex-col bg-slate-300">
<div v-if="machineMainInfo?.length > 0" class="flex flex-col gap-2">
<div :key="idx" v-for="(info, idx) in machineMainInfo" class="flex items-center items-center bg-slate-200 px-3 py-2 rounded-md">
<div class="text-slate-700 font-bold mr-2">{{ info.title }}</div>
<div class="text-slate-500">{{ info.val }}</div>
</div>
</div>
</div>
</div>
<div style="min-width: 300px;" class="flex flex-col bg-slate-200 p-3 rounded">
<h2 clas="font-bold flex items-center"><i class="ri-git-pull-request-line mr-1"></i> Дополнительно</h2>
<div class="mt-2 p-2 rounded-md flex flex-col bg-slate-300">
<div v-if="machineOverInfo?.length > 0" class="flex flex-col gap-2">
<div :key="idx" v-for="(info, idx) in machineOverInfo" class="flex items-center items-center bg-slate-200 px-3 py-2 rounded-md">
<div class="text-slate-700 font-bold mr-2">{{ info.title }}</div>
<div class="text-slate-500">{{ info.val }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
</Accordion>
</div>
<Accordion :isOpen="true" id="machine_packs">
<template #header>
<div class="font-bold text-xl">Пакеты</div>
</template>
<template #body>
<div class="flex flex-col gap-4">
<div id="packReportButtons">
<div class="flex flex-col gap-5">
<div class="grid grid-cols-12 gap-3">
<div v-if="reportPacks.length === 0" class="flex items-center justify-center text-slate-700 text-2xl font-bold col-span-12">
<h2>Нет данных по машине</h2>
</div>
<div :key="idx" v-if="reportPacks.length > 0" v-for="(pack, idx) in reportPacks" class="col-span-6 sm:col-span-4 lg:col-span-3 xl:col-span-2 flex items-start flex gap-1 justify-center rounded-md bg-slate-300">
<div class="flex-col flex-grow flex py-1">
<div class="flex text-slate-700 font-medium px-3 text-slate-700">
<div class="mr-1">Номер</div>
<div>{{ pack.pack_number }}</div>
</div>
<div class="text-slate-600 flex px-3 text-slate-600">
<div class="mr-1">Количество</div>
<div>{{ pack.count }}</div>
</div>
</div>
<div @click="loadPack(pack.pack_number)" class="h-full flex items-center justify-center text-white rounded-md px-3 text-center cursor-pointer transitiona-all bg-slate-400 hover:bg-slate-600">
<i class="ri-eye-line"></i>
</div>
</div>
</div>
</div>
</div>
<div class="w-full h-[1px] bg-slate-300"></div>
<PackView :isLoader="false" id="machineReport">
<template #description>
<span class="text-slate-400 text-sm">последние 30</span>
</template>
<template #error>
Слишком много пакетов для запроса
</template>
</PackView>
</div>
</template>
</Accordion>
</div>
</div>
</Modal>
</template>
@@ -0,0 +1,28 @@
const getMachineInfo = (info, isMain) => {
const mainInfo = ["Текущая симкарта", "Последняя коммуникация", "Предприятие приписки", "Текущее устройство"]
if (isMain) {
return info.reduce((acc, el, idx) => {
if (mainInfo.includes(Object.keys(el)[0])) {
const item = {title: Object.keys(el)[0], val: Object.values(el)[0]}
acc.push(item)
} else {
acc
}
return acc
}, [])
}
if (!isMain) {
return info.reduce((acc, el, idx) => {
if (!mainInfo.includes(Object.keys(el)[0])) {
const item = {title: Object.keys(el)[0], val: Object.values(el)[0]}
acc.push(item)
} else {
acc
}
return acc
}, [])
}
}
export { getMachineInfo }
@@ -0,0 +1,71 @@
<!-- eslint-disable -->
<script>
import {ref, toRaw, computed} from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex";
import Button from "@/components/1_atoms/Button.vue";
import VueMultiselect from 'vue-multiselect'
export default {
name: 'Filters',
components: {
Button,
VueMultiselect
},
setup() {
// const store = useStore()
// store.dispatch('main/updateSelects')
},
data() {
return {
isOpenOptions: false,
}
},
computed: {
...mapGetters('machines', ['selectedSelects', 'selectsData', 'toggleFilter', 'selectedData']),
},
mounted () {
},
methods: {
...mapMutations('machines', ['setSelectedSelect', 'setToggleFilter']),
...mapActions('machines', ['updateSelects']),
updateSelectedSelects: function(value) {
if (value.length > 0) {
this.updateSelects({key: 'set', value: value})
}
},
removedOption: function(value) {
this.updateSelects({key: 'removed', value: value})
},
}
}
</script>
<!-- eslint-disable -->
<template>
<div id="ignore:2Qxwqz" class="pb-1">
{{ console.log('selectedData' ,selectedData)
}}
<div v-if="toggleFilter" wrapper_class="grid grid-cols-12 gap-2" selects_class="col-span-12 sm:col-span-6 lg:col-span-3" class="grid grid-cols-12 gap-2 mb-3 border-t border-slate-300 pt-3" fields={@filters_fields} id="SelectFilters" phx-hook="SelectFilters">
<div v-for="selectData in selectsData" class="col-span-12 sm:col-span-6 lg:col-span-3">
<VueMultiselect
:multiple="true"
v-model="selectedSelects[selectData.key]"
label="name"
track-by="name"
:options="selectData.data"
:placeholder="selectData.title"
@update:model-value="updateSelectedSelects"
@remove="removedOption"
:showLabels="false"
>
</VueMultiselect>
</div>
</div>
</div>
</template>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
@@ -0,0 +1,62 @@
<script>
import {mapGetters, mapMutations, useStore} from "vuex"
import Tabulator from "@molecules/Tabulator/index.vue"
import { EventStore } from "@store/hooks/EventStore"
export default {
name: 'HistoryViews',
components: {Tabulator},
setup () {
const initLocalStorage = new EventStore()
const historyData = initLocalStorage.localStoreGetData({key: 'historyViews', onlyGet: true})
const isHistoryData = historyData && historyData.length > 0
// const updatedData = historyData.filter((el, idx, arr) => arr.findIndex((item) => item.machine_id === el.machine_id) === idx).slice(0, 50) // uniq values
const updatedData = isHistoryData ? historyData.slice(0, 50) : []
const store = useStore()
store.commit('machines/setHistoryData', updatedData)
},
computed: {
...mapGetters('machines', ['historyData']),
tabulatorOtps() {
return {
dataSource: this.historyData,
columns: [
{
title: "ID машины",
field: "machine_id",
width: 150
},
{
title: "Тип машины",
field: "machine_type",
width: 150
},
{
title: "Карточка машины",
field: "link",
log: true,
width: 180,
render: `<div class="flex items-center gap-2">
<a target="_blank" class="underline decoration-dashed" href="{{ link }}">Открыть карточку машины</i></a>
</div>`
},
{
title: "Время просмотра",
field: "created_at",
width: 150
}
],
height: "550px"
}
}
},
methods: {
...mapMutations('machines', ['setHistoryData']),
}
}
</script>
<template>
<Tabulator v-bind="tabulatorOtps" />
</template>
@@ -0,0 +1,121 @@
<!-- eslint-disable -->
<script>
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import Button from "@/components/1_atoms/Button.vue"
import { EventStore } from "@store/hooks/EventStore"
import moment from 'moment'
export default {
name: 'Machine',
components: {
Button,
},
props: {
machine: {
type: Object,
default: () => ({})
},
openMap: {
type: Function,
default: () => {}
},
openData: {
type: Function,
default: () => {}
}
},
data() {
return {
initLocalStorage: null,
}
},
computed: {
...mapGetters('machines', ['machinesData', 'historyData', 'historyMachines']),
},
mounted () {
},
methods: {
...mapMutations('machines', ['setInitHistory', 'setHistoryData', 'setHistoryMachines']),
...mapActions('machines', ['openMapModal', 'openDataModal']),
buttonClass: function(value) {
return `border border-slate-400 flex items-center justify-center cursor-pointer rounded transition-all text-xs text-center py-[5px] px-2 ${value}`
},
cardFunc: function(value) {
this.initLocalStorage = new EventStore()
if (this.machine.machine_id) {
const valueStorage = {machine_id: this.machine.machine_id, machine_type: this.machine.machine_type, link: `/html/askr_devices/analyze_device/${this.machine.device_number}`, created_at: moment().format('HH:mm:ss DD-MM-YY')}
return this.initLocalStorage.localStoreGetData({key: 'historyViews', newData: valueStorage}, this.updateLocalData)
}
},
updateLocalData: function(value) {
const historyData = value.data ? value.data : []
const mergedHistory = value.newData !== 'mount' ? [value.newData, ...historyData] : historyData
// const updatedHistory = mergedHistory.filter((el, idx, arr) => arr.findIndex((item) => item.machine_id === el.machine_id) === idx).slice(0, 50) // uniq values
const updatedHistory = mergedHistory.slice(0, 50)
const initHistory = (Array.isArray(value.newData) && value.newData.length > 0 || value.newData === 'mount') ? true : false
this.setInitHistory(initHistory)
this.setHistoryData(updatedHistory)
return this.initLocalStorage.localStoreSetData({key: 'historyViews', value: updatedHistory})
},
mapFunc: function(machine) {
// console.log('machine', machine)
this.openMapModal(machine)
return this.openMap()
},
dataFunc: function(machine) {
// console.log('machine', machine)
this.openDataModal(machine)
return this.openData()
},
}
}
</script>
<!-- eslint-disable -->
<template>
<div id="card" class="pt-3 border w-[23%] min-w-[310px] border-slate-300 rounded transition-all bg-slate-300}">
<div class="h-full flex flex-col gap-5 relative text-left justify-between">
<div class="flex flex-col items-end absolute text-white top-[-0.75rem] right-0 h-full min-w-[173px] p-2" style="backdrop-filter: blur(10px);">
<div class="mb-2 text-right drop-shadow" search-mode="machine_id">ID: {{ machine?.machine_id }}</div>
<div class="flex flex-row">
<span>{{ machine.railway_name }}</span><i class="ri-route-line ml-2" title="Дорога:"/>
</div>
<div class="flex flex-row">
<span>{{ machine.org_name }}</span><i class="ri-building-2-line ml-2" title="Компания:"/>
</div>
<div class="flex flex-row">
<span>{{ machine.nomer_zn8 }}</span><i class="ri-number-8 ml-2" title="8зн номер:"/>
</div>
<div class="flex flex-row" search-mode="device_number">
<span>{{ machine.device_number }}</span><i class="ri-rss-line ml-2" title="Устройство:"/>
</div>
</div>
<div class="flex flex-col w-full mt-4 mb-2 pl-4 mt-1 z-[1]">
<div class="text-slate-800 font-bold m-0" search-mode="machine_type">{{ machine.machine_type }}</div>
<div :machine_id="machine.machine_id" history_click="/html/askr_devices/analyze_comm/#{@machine?.imei}" class="text-slate-500 text-xs underline-offset-4" search-mode="imei">{{ machine.imei }}</div>
</div>
<div class="flex lg:flex-row flex-col gap-1 my-2 px-2 z-[1]">
<div class="flex">
<a :href="`/html/askr_devices/analyze_device/${machine.device_number}`" id="to_card_machine" no-init="false" @click="cardFunc(machine)" target="_blank" :class="buttonClass('bg-primary text-white')">Карточка</a>
</div>
<div class="flex gap-1">
<div @click="mapFunc(machine)" :class="buttonClass('bg-white/25 hover:bg-slate-400 hover:text-white')"><i class="ri-map-pin-line"/></div>
<div @click="dataFunc(machine)" :class="buttonClass('bg-white/25 hover:bg-slate-400 hover:text-white')">30</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
#card {
background: linear-gradient(96.95deg,rgba(236,236,243,.95) 1.45%,hsla(0,0%,100%,.703) 20.43%,hsla(0,0%,100%,0) 98.58%),linear-gradient(277.71deg,rgba(0,8,38,.3),rgba(0,5,20,.076) 45.46%,hsla(0,0%,100%,0) 97.87%),linear-gradient(0deg,rgba(123,134,152,.7),rgba(123,134,152,.7));
}
</style>
@@ -0,0 +1,241 @@
<!-- eslint-disable -->
<script>
import {computed} from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex"
import {useRoute, useRouter} from 'vue-router'
import Button from "@/components/1_atoms/Button.vue"
import VueMultiselect from 'vue-multiselect'
import Filters from "./Filters.vue"
import Machine from "./Machine.vue"
import Charts from "./Charts.vue"
import HistoryViews from "./HistoryViews.vue"
import Table from "./Table.vue"
import MapModal from '@molecules/MapModal/index.vue'
import DataModal from "./DataModal/DataModal.vue"
import Spinner from "@molecules/Spinner/index.vue"
import {PushAfterTimeout} from '@store/hooks/PushAfterTimeout'
import {EventStore} from '@store/hooks/EventStore'
export default {
name: 'Machines',
components: {
Button,
Filters,
VueMultiselect,
Machine,
Table,
Charts,
HistoryViews,
MapModal,
DataModal,
Spinner
},
props: {
status: {
type: String,
default: 'static',
},
},
data () {
return {
openMap: false,
openData: false,
}
},
setup () {
const initLocalStorage = new EventStore()
const store = useStore()
const route = useRoute()
const path = computed(() => route.path)
const query = computed(() => route.query)
const historyData = initLocalStorage.localStoreGetData({key: 'historyViews', onlyGet: true})
const isHistoryData = historyData && historyData.length > 0
const updatedData = isHistoryData ? historyData.filter((el, idx, arr) => arr.findIndex((item) => item.machine_id === el.machine_id) === idx).slice(0, 20) : []
const urlParams = path.value === '/' && query?.value?.mode ? query.value.mode : ''
const externalParams = {historyData: updatedData, urlParams: urlParams}
store.commit('machines/setHistoryData', updatedData)
store.dispatch('machines/uploadData', externalParams)
},
computed: {
...mapGetters('machines', ['searchModes', 'searchValue', 'selectedSearchMode', 'leftTopButtons', 'rightTopButtons', 'selectedMode', 'toggleFilter', 'activeFilterBtn', 'mapData', 'machinesData']),
...mapGetters('layout', ['isOpenMenu']),
},
mounted () {
},
updated () {
this.$nextTick(function () {
const path = this.$route.path
const query = this.$route.query
if (path === '/' && query?.mode) {
this.setSelectedMode(query?.mode)
}
if (path === '/' && !query?.mode) {
this.setSelectedMode('cards')
}
})
},
unmounted() {
const store = useStore();
store.dispatch('machines/resetStore')
},
watch: {
isSuccessSearch: {
handler(newValue, oldValue) {
console.log('isSuccessSearch', newValue, oldValue)
},
deep: true
},
},
methods: {
...mapMutations('machines', ['setSearchMode', 'setSelectedMode', 'setToggleFilter', 'setActiveFilterBtn']),
...mapActions('machines', ['updateSearch', 'clearFilters']),
updateSearchMode: function(selectedSearchMode) {
this.setSearchMode(selectedSearchMode)
},
updateSearchValue: function(e) {
const searchInput = new PushAfterTimeout('searchInput', 1500)
if (searchInput.input) {
setTimeout(() => {
this.updateSearch(e.target.value)
}, 1500)
}
},
setFilteredAction (filteredAction) {
this.setActiveFilterBtn(filteredAction)
if (filteredAction === 'filter') {
this.setToggleFilter()
}
if (filteredAction ==='clearFilter') {
this.clearFilters()
}
},
updateMode: function(mode) {
if (mode) {
const path = this.$route.path
const params = mode === 'charts' ? {mode: mode, type: 'main'} : {mode: mode}
this.$router.push({ path: path, query: params })
return this.setSelectedMode(mode)
}
},
toggleMap () {
this.openMap = !this.openMap
},
toggleData () {
this.openData = !this.openData
},
closeMap () {
return this.openMap = !this.openMap
},
closeData () {
return this.openData = !this.openData
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="px-2 sm:px-5 pt-1 bg-light">
<div class="relative">
<div class="grid grid-cols-12 gap-4 md:gap-3 lg:gap-4 pb-3">
<div class="flex items-center col-span-12 sm:col-span-8 lg:col-span-5 xl:col-span-4 2xl:col-span-3">
<div id="search" class="relative w-full max-w-[150px] h-[31px] pl-1 py-1 bg-white border border-primary rounded-l-md z-30">
<VueMultiselect
v-model="selectedSearchMode"
label="name"
:options="searchModes"
:searchable="false"
placeholder=""
:showLabels="false"
@update:model-value="updateSearchMode"
>
</VueMultiselect>
</div>
<div id="searchInput" class="relative w-full border-r border-t border-b border-primary rounded-r-md">
<input v-bind:value="searchValue" v-on:change="updateSearchValue" :status="status" class="pl-1 py-1 rounded-r-md w-full max-h-[29px] border-none focus-visible:outline-blue-300"/>
</div>
</div>
<!-- {{ console.log('machinesData', machinesData) }} -->
<ul id="machinesMode" class="flex col-span-6 sm:col-span-4 md:col-span-4 lg:col-span-3 xl:col-span-2 justify-center">
<li v-bind:key="idx" v-for="(mode, idx) in leftTopButtons">
<Button :onClick="updateMode" :selected="mode.key" :classIcon="mode.class" :classBtn="[mode.key === 'cards' ? 'rounded-r-none' : 'rounded-l-none', selectedMode === mode.key ? 'text-white bg-primary focus:ring-4 focus:outline-none focus:ring-blue-300' : 'bg-transparent', 'w-full min-w-[88px] h-[30px] border border-primary rounded-md']" />
</li>
</ul>
<div id="historyMode" class="flex col-span-6 sm:col-span-4 md:col-span-4 lg:col-span-3 xl:col-span-2 justify-center">
<Button :onClick="updateMode" selected="history" title="Просмотры" :classBtn="[selectedMode === 'history' ? 'focus:ring-4 focus:outline-none focus:ring-blue-300' : '', 'w-full min-w-[88px] h-[30px] bg-primary text-white border border-primary rounded-md']" />
</div>
<div id="chartsMode" class="flex col-span-6 sm:col-span-4 md:col-span-4 lg:col-span-3 xl:col-span-2 justify-center">
<Button :onClick="updateMode" selected="charts" classIcon="ri-pie-chart-2-line" :classBtn="[selectedMode === 'charts' ? 'focus:ring-4 focus:outline-none focus:ring-blue-300' : '', 'w-full min-w-[88px] h-[30px] bg-primary text-white border border-primary rounded-md']" />
</div>
<ul id="filtersButtons" class="flex col-span-6 sm:col-span-4 md:col-span-4 lg:col-span-3 xl:col-span-2 justify-center">
<li v-bind:key="idx" v-for="(mode, idx) in rightTopButtons">
<Button :onClick="setFilteredAction" :selected="mode.key" :title="mode.name" :classIcon="mode.iconClass" :classBtn="[activeFilterBtn === mode.key ? 'focus:ring-4 focus:outline-none focus:ring-blue-300' : '', mode.key === 'filter' ? 'rounded-r-none' : 'rounded-l-none', mode.classBtn, 'w-full min-w-[88px] h-[30px] border border-primary rounded-md']" />
</li>
</ul>
</div>
<div id="filters" v-if="toggleFilter">
<Filters />
</div>
<div class="border-2 rounded-lg w-full bottom-0"></div>
<div id="listMachines" v-if="selectedMode === 'cards'" class="w-full min-h-[80vh] p-4 bg-light dark:bg-slate-900 dark:text-slate-300 relative">
<ul v-if="selectedMode === 'cards'" class="flex flex-wrap gap-4 w-full justify-center align-center">
<!-- <li v-for="machine in machinesData" class="w-full sm:w-1/2 md:w-1/2 lg:w-1/2 xl:w-1/2 p-2 border-2 border-primary rounded-md"> -->
<li v-for="machine in machinesData" class="">
<Machine :machine="machine" :openMap="toggleMap" :openData="toggleData" />
</li>
</ul>
</div>
<div id="tableMachines" v-if="selectedMode === 'table'" class="tab-pane bg-light relative py-4 text-center min-h-[300px] relative rounded-md w-full">
<Table />
</div>
<div id="historyViews" v-if="selectedMode === 'history'" class="tab-pane bg-light relative shadow text-center min-h-[300px] relative w-full p-4">
<HistoryViews />
</div>
<div id="charts" v-if="selectedMode === 'charts'" class="w-full min-h-[80vh] pb-5 bg-light dark:bg-slate-900 dark:text-slate-300 relative">
<!-- <.loader :if={@display_config.is_loading_charts} /> -->
<div>
<Charts />
</div>
</div>
<!-- <div id="no_data" :if={@display_config.no_data && @display_config.machines_display_mode != "history_views"} class="w-full min-h-[80vh] bg-light dark:bg-slate-900 dark:text-slate-300 relative">
<.loader :if={@list == [] && !@display_config.no_data || @display_config.is_loading_list} />
<div class="flex items-center justify-center text-lg font-bold pt-5">Нет данных</div>
</div> -->
<MapModal
:open="openMap"
:close="closeMap"
:data="mapData"
id="mapModal"
headerClass="border-b p-4 font-bold"
>
<template #header>
<div id="headerTitle">
Карта
</div>
</template>
<div v-if="!mapData" id="map_loader" class="flex justify-center" style="width: 95vw; height: 82vh;">
<Spinner />
</div>
<div v-if="mapData" id="mapModal" style="width: 95vw; height: 82vh;"></div>
<div id="popup" class="ol-popup hidden transition-all">
<a href="#" id="popup-closer" class="ol-popup-closer hover:no-underline top-2 right-2 ">
<i class="ri-close-fill"></i>
</a>
<div id="popup-content"></div>
</div>
</MapModal>
<DataModal id="dataModal" :open="openData" :close="closeData" />
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,94 @@
<script>
import {mapGetters, mapActions, mapMutations, useStore} from "vuex"
import Tabulator from "@molecules/Tabulator/index.vue"
import { EventStore } from "@store/hooks/EventStore"
export default {
name: 'Table',
components: {Tabulator},
data () {
return {
}
},
computed: {
...mapGetters('machines', ['tableData', 'pagination']),
tabulatorOtps() {
return {
dataSource: this.tableData,
pagination: {...this.pagination, updatePagination: this.updatePagination},
columns:
[
{
title: "Тип и № машины",
field: "machine_type",
width: 150,
log: true,
render: `<div class="flex items-center gap-2">
<a target="_blank" class="underline decoration-dashed" href="/html/askr_devices/analyze_machine/{{ machine_id }}">{{machine_type}}</i></a>
</div>`
},
{
title: "Imei",
field: "imei",
width: 150,
log: true,
render: `<div class="flex items-center gap-2">
<a target="_blank" class="underline decoration-dashed" href="/html/askr_devices/analyze_comm/{{ imei }}">{{imei}}</i></a>
</div>`
},
{
title: "Дорога",
field: "railway_name",
width: 90
},
{
title: "Предприятие",
field: "org_name",
width: 150
},
{
title: "КР устройства",
field: "device_number",
width: 120,
log: true,
render: `<div class="flex items-center gap-2">
<a target="_blank" class="underline decoration-dashed" href="/html/askr_devices/analyze_device/{{ device_number }}">{{device_number}}</i></a>
</div>`
},
{
title: "Номер",
field: "nomer_zn8",
width: 90
},
{
title: "Заводской номер",
field: "zav_nomer",
width: 90
},
{
title: "ID машины",
field: "machine_id",
width: 90
},
{
title: "ID устройства",
field: "device_id",
width: 90
}
],
height: "550px"
}
}
},
methods: {
...mapMutations('machines', ['setHistoryData']),
...mapActions('machines', ['updatePagination']),
}
}
</script>
<template>
<Tabulator v-bind="tabulatorOtps" />
</template>
@@ -0,0 +1,89 @@
<!-- eslint-disable -->
<script>
import {computed} from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex";
import {useRoute} from 'vue-router'
import Machines from "./Machines/Machines.vue";
import FinderPacks from "./FinderPacks/FinderPacks.vue";
import AddUsers from "./AddUsers/AddUsers.vue";
import Button from "@/components/1_atoms/Button.vue";
export default {
name: 'Main',
components: {
Machines,
FinderPacks,
AddUsers,
Button
},
setup() {
const store = useStore()
const route = useRoute()
const path = computed(() => route.path)
store.dispatch('main/uploadData', path.value)
store.commit('main/setPath', path.value)
},
data() {
return {
enabled: true,
dragging: false,
isTopScrolled: true,
containerOffset: 0
}
},
computed: {
...mapGetters('main', ['pages', 'activePath', 'selectedPage']),
...mapGetters('layout', ['isOpenMenu']),
},
mounted () {
},
unmounted() {
const store = useStore();
store.dispatch('main/resetStore')
},
updated() {
this.$nextTick(function () {
const path = this.$route.path
if (path !== this.activePath) {
const path = this.$route.path
this.$router.push({ path: path, query: {} })
this.setPath(path)
this.uploadData(path)
}
})
},
methods: {
...mapMutations('main', ['setPage', 'setPath']),
...mapActions('main', ['uploadData']),
setSelPage: function(page) {
console.log('page', page)
return this.setPage(page)
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="col-span-12">
<div class="hidden" id="ignore:21QxOfq">
{{ console.log('selectedPage', selectedPage) }}
</div>
<div class="p-2 sm:px-5 pt-1 bg-light rounded-t-xl">
<div class="relative">
<div class="flex gap-3 sm:gap-7 w-full lg:w-[50%]">
<router-link :to="{name: page.link}" @click="setPage(page)" v-for="(page, idx) in pages" class="flex w-full">
<Button v-bind:key="idx" type="button" :title="page.name" :classBtn="[selectedPage.key !== page.key ? 'border-transparent': 'border-slate-500', 'flex border-b-4']" />
</router-link>
</div>
<div class="border-2 rounded-lg w-full bottom-0 absolute"/>
</div>
</div>
<router-view />
</div>
</template>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,65 @@
<!-- eslint-disable -->
<script>
import Chart from "@/components/2_molecules/Chart/index.vue";
import moment from 'moment'
export default {
name: 'PackChart',
components: {
Chart,
},
props: {
curerentTableData: {
type: Array,
default: () => []
},
displayView: {
type: String,
default: ""
},
},
data() {
return {
currentSizes: {
width: document.documentElement.clientWidth - 700,
height: document.documentElement.clientHeight - 350
},
maxWidth: document.documentElement.clientWidth - 300,
maxHeight: document.documentElement.clientHeight - 300,
}
},
computed: {
},
methods: {
chartData: function(params) {
console.log('params', params)
const yAxisAata = params.columns.map(column => {
const value = params.dataSource.map((el) => {
const dt = moment(el[column.pack_dt]).format('DD.MM.YYYY HH:mm')
return {
datetime: dt, param: column.key, state: el[column.key] ? el[column.key] : 0
}
})
return {key: column.key, name: column.title, value}
})
console.log('yAxisAata', yAxisAata)
const xAxisData = params.dataSource.map((el) => {
const dt = moment(el.pack_dt).format('DD.MM.YYYY HH:mm')
return dt
})
return {charts: yAxisAata, names: {yAxis: ' ', xAxis: 'Дата и время'}, xAxisData, title: 'График'}
},
}
}
</script>
<!-- eslint-disable -->
<template>
{{ console.log('currentSizes.heigth', maxHeight) }}
<div v-if="displayView === 'chart'" id="packChartWrapper" class="relative flex flex-wrap justify-center w-full h-full mb-3" >
<Chart id="packChart" type="packLines" height="800" :data="chartData(curerentTableData)" :sizes="currentSizes" :maxWidth="`${maxWidth}px`" :maxHeight="`${maxHeight}px`" />
</div>
</template>
@@ -0,0 +1,558 @@
<script>
import {ref, toRaw, computed} from 'vue'
import {mapGetters, mapMutations, mapActions, useStore} from "vuex";
import {useRoute, useRouter} from 'vue-router'
import Datepicker from "@molecules/Datepicker/index.vue"
import Spinner from "@molecules/Spinner/index.vue"
import Tabulator from "@molecules/Tabulator/index.vue"
import PackChart from './Chart/Chart.vue'
import VueMultiselect from 'vue-multiselect'
import moment from 'moment'
export default {
name: 'Packs',
components: {
Datepicker,
VueMultiselect,
Tabulator,
PackChart,
Spinner
},
provide() {
return {
defaultDate: computed(() => this.setPickerFormat(this.packsSettings.dates.selectedDate)),
}
},
setup() {
const store = useStore()
const route = useRoute()
const path = computed(() => route.path)
const query = computed(() => route.query)
console.log('path', path)
console.log('query', query)
store.commit('packs/setIsLoading', true)
store.dispatch('packs/uploadData', {path: path.value, query: query.value})
},
data() {
return {
columnSelector: false,
displayView: 'table',
}
},
computed: {
...mapGetters('packs', ['selectedPacksGroup', 'selectedPack', 'tableData', 'packsSettings', 'columnsSelect', 'selectedColumnsSelect', 'isLoading']),
curerentTableData () {
const pagination = {...this.packsSettings.pagination, updatePagination: this.updatePagination}
return {
...this.tableData, pagination
}
},
},
unmounted() {
const store = useStore()
store.dispatch('packs/resetStore')
},
methods: {
...mapActions('packs', ['uploadData', 'updatePagination', 'updatePackSettings', 'updateSelects']),
...mapMutations('packs', ['setSelectedPack']),
updateSelectedPack: function(value) {
this.setSelectedPack(value)
},
setPackSettings: function(value, key) {
console.log('value', value)
console.log('key', key)
switch (key) {
case 'date':
this.updatePackSettings({dates: {...this.packsSettings.dates, selectedDate: value}})
break
case 'imei':
this.updatePackSettings({imei: value})
break
case 'selectedPack':
this.updatePackSettings({selectedPack: value})
break
}
},
setDate: function(value) {
console.log('value', value)
this.updatePackSettings({dates: {...this.packsSettings.dates, selectedDate: value}})
},
setImei: function(e) {
console.log('value', e.target.value)
this.updatePackSettings({imei: e.target.value})
},
setPack: function(value) {
console.log('value', value)
this.updatePackSettings({selectedPack: value})
},
fetchPack: function(params) {
console.log('params', params)
if (params.imei && params.dates.selectedDate && params.selectedPack) {
const path = this.$route.path
const query = this.$route.query
const dateUrl = moment(params.dates.selectedDate).format('YYYY-MM-DD')
console.log('params.selectedPack', params.selectedPack)
const queryUrl = {...query, date: dateUrl, imei: params.imei, pack: params.selectedPack.name}
this.$router.push({ path: path, query: queryUrl})
const currentQuery = {...queryUrl, date: params.dates.selectedDate}
this.uploadData({path: path, query: currentQuery})
}
},
setPickerFormat: (date) => {
console.log('moment(date)', moment(date).format('DD.MM.YYYY'))
if (!date) return null
return moment(date).format('DD.MM.YYYY')
},
setOpenColumnSelect: function() {
return this.columnSelector = !this.columnSelector
},
toggleDisplayView: function() {
this.displayView = this.displayView === 'chart' ? 'table' : 'chart'
},
addedOption: function(value) {
console.log('value', value)
this.updateSelects({key: 'added', select: value})
},
removedOption: function(value) {
this.updateSelects({key: 'removed', select: value})
},
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="grid grid-cols-12 gap-6 col-span-12 h-full">
{{
// console.log('tableData', tableData)
console.log('packsSettings', packsSettings)
}}
<div class="rounded-md bg-light px-4 pt-4 pb-6 gap-2 shadow lg:col-span-4 xl:col-span-3 2xl:col-span-2 col-span-12">
<h1 class="font-medium leading-tight text-xl mb-4 text-slate-600">
Первый пакет
</h1>
<div class="grid gap-3 mb-3 ">
<div class="grid grid-cols-1">
<label for="date" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">
Дата
</label>
<Datepicker id="date" :onchange="setDate" />
</div>
<div phx-feedback-for="imei">
<label for="selectedImei" class="block text-sm leading-6 text-zinc-800 dark:text-white">
imei
</label>
<input
id="selectedImei"
type="text"
name="imei"
v-bind:value="packsSettings.imei"
v-on:change="setImei"
class="block w-full rounded-lg border-zinc-300 py-[1px] px-[11px] text-zinc-900 focus:outline-none focus:ring-4 text-sm sm:leading-6 phx-no-feedback:border-zinc-300 phx-no-feedback:focus:border-zinc-400 phx-no-feedback:focus:ring-zinc-800/5 border-zinc-300 focus:border-zinc-400 focus:ring-zinc-800/5 "
placeholder="imei"
required=""
>
</div>
<div>
<label for="selectedPack" class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400"
>Выбрать пакет</label>
<div id="selected_settings_name_wrapper" class="w-full">
<div id="packsOptions" class="col-span-12 sm:col-span-6 lg:col-span-3 rounded-lg">
<VueMultiselect
name="selectedPack"
v-model="packsSettings.selectedPack"
label="name"
track-by="name"
:options="packsSettings.selectedPackGroup"
:placeholder="packsSettings.selectedPack?.name"
@update:model-value="setPack"
:showLabels="false"
>
</VueMultiselect>
</div>
</div>
</div>
</div>
<div class="grid gap-3 grid-cols-4">
<input
type="submit"
@click="fetchPack(packsSettings)"
class="col-span-4 lg:col-span-4 cursor-pointer text-white bg-primary hover:brightness-90 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-ssm w-full sm:w-auto px-5 py-1 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 transition_up"
value="Выгрузить"
>
<div
phx-click="load_pack_settings"
class="cursor-pointer col-span-4 xl:col-span-4 bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up"
>
Настройки
</div>
<div
class="cursor-pointer col-span-4 lg:col-span-4 bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up"
phx-click="[[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#select_machines_modal&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;show&quot;,{&quot;display&quot;:null,&quot;time&quot;:200,&quot;to&quot;:&quot;#machines_modal_loader&quot;,&quot;transition&quot;:[[],[],[]]}],[&quot;push&quot;,{&quot;event&quot;:&quot;load_machines_table&quot;,&quot;value&quot;:{&quot;mode&quot;:&quot;page&quot;}}]]"
>
Выбрать машину
</div>
<div
id="question"
class="cursor-pointer col-span-4 xl:col-span-4 bg-transparent hover:bg-primary text-primary font-semibold hover:text-white py-1 text-ssm px-4 border border-primary hover:border-transparent rounded-lg text-center transition_up"
phx-hook="InfoModal"
>
<i class="ri-question-mark" />
</div>
</div>
</div>
<div id="packageDisplay" class="relative lg:col-span-8 xl:col-span-9 2xl:col-span-10 col-span-12 bg-light shadow">
<div v-if="!isLoading && curerentTableData.dataSource" id="tableButtons" class="w-full">
<div class="w-full flex justify-end">
<div class="flex gap-3 pt-4 pb-2 pr-4">
<div class="">
<button
type="button"
@click="setOpenColumnSelect"
class="flex items-center transition-all justify-center py-2 px-3 bg-light text-sm font-medium text-slate-700 focus:outline-none rounded-lg border border-dashed border-slate-400 hover:text-blue-800 hover:border-blue-900 hover:shadow focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
>
<i class="ri-settings-3-line" />
</button>
</div>
<div
id="graphic_btn_inactive"
class=""
>
<button
type="button"
@click="toggleDisplayView"
class="flex items-center transition-all justify-center py-2 px-3 bg-light text-sm font-medium text-slate-700 focus:outline-none rounded-lg border border-dashed border-slate-400 hover:text-blue-800 hover:border-blue-900 hover:shadow focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#graphic_btn_inactive&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#graphic_btn_active&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#pack_table_warapper&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#pack_graphic_wrapper&quot;}],[&quot;push&quot;,{&quot;event&quot;:&quot;toggle_display_data&quot;}]]"
>
<i class="ri-bar-chart-grouped-line" />
</button>
</div>
<div
id="graphic_btn_active"
class="hidden"
>
<button
type="button"
class="flex items-center transition-all justify-center py-2 px-3 bg-blue-200 text-sm font-medium text-blue-700 focus:outline-none rounded-lg border border-dashed border-blue-500 hover:text-blue-800 hover:border-blue-900 hover:shadow focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#graphic_btn_inactive&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#graphic_btn_active&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#pack_table_warapper&quot;}],[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#pack_graphic_wrapper&quot;}],[&quot;push&quot;,{&quot;event&quot;:&quot;toggle_display_data&quot;}]]"
>
<i class="ri-bar-chart-grouped-line" />
</button>
</div>
<div
id="excel_btn"
phx-click="download_csv"
style=""
class="flex items-center justify-center transition-all justify-center py-2 px-3 bg-light text-sm font-medium text-slate-700 focus:outline-none rounded-lg border border-dashed border-slate-400 hover:text-blue-800 hover:border-blue-900 hover:shadow focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 cursor-pointer"
>
<i class="ri-file-excel-line" />
</div>
<div
id="excel_loader"
style="display: none"
class="flex items-center justify-center transition-all justify-center py-2 px-3 bg-light text-sm font-medium text-slate-700 focus:outline-none rounded-lg border border-dashed border-slate-400 hover:text-blue-800 hover:border-blue-900 hover:shadow focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 "
>
<svg
aria-hidden="true"
class="w-4 h-4 text-gray-300 animate-spin dark:text-gray-400 fill-slate-700"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</div>
</div>
</div>
</div>
<div v-if="columnSelector" id="colums_select_wrapper" class="w-full">
<VueMultiselect
name="selectedPack"
v-model="selectedColumnsSelect"
label="name"
track-by="name"
:options="columnsSelect"
:placeholder="null"
@select="addedOption"
@remove="removedOption"
:showLabels="false"
:multiple="true"
:group-select="true"
>
</VueMultiselect>
</div>
<div v-if="displayView === 'table'" id="packTableWarapper">
{{ console.log('getters.isLoading', isLoading) }}
<div id="reportPacks" class="tab-pane active text-center min-h-[300px] rounded-md ">
<Tabulator v-if="!isLoading && curerentTableData.dataSource" v-bind="curerentTableData" />
<span v-if="!isLoading && !curerentTableData.dataSource" class="absolute flex w-full justify-center items-center mt-2"> Выберите пакет и нажмите выгрузить</span>
<div v-if="isLoading && !curerentTableData.dataSource" class="absolute flex w-full h-full justify-center items-center bg-slate-50" id="packLoader">
<Spinner />
</div>
</div>
</div>
<PackChart :displayView="displayView" :curerentTableData="curerentTableData" />
</div>
<div>
<button
type="button"
class="hidden"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;}]]"
/>
<div
id="period_table_modal"
class="fixed z-[10000] inset-0 hidden"
phx-remove="[[&quot;transition&quot;,{&quot;time&quot;:200,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;fade-out&quot;],[],[]]}]]"
>
<!-- Modal container -->
<div class="h-screen flex items-center justify-center p-4">
<!-- Overlay -->
<div
class="absolute z-0 inset-0 bg-gray-500 opacity-75"
aria-hidden="true"
/>
<!-- Modal box -->
<div
class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl "
role="dialog"
aria-modal="true"
tabindex="0"
autofocus=""
phx-window-keydown="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#period_table_modal-return&quot;}]]"
phx-key="escape"
>
<button
type="button"
class="absolute top-2 right-2 text-gray-400 flex space-x-1 items-center"
aria_label="close modal"
phx-click="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#period_table_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#period_table_modal-return&quot;}]]"
>
<span class="text-sm">(esc)</span>
<i class="ri ri-close-line text-2xl" />
</button>
<div class="min-w-[350px] min-h-[400px] py">
<div class=" p-4 font-bold" />
<div
id="pack-settings-editor"
phx-hook="PackSettingsEditor"
/>
</div>
</div>
</div>
</div>
</div>
<div
id="ignore:31QzedwP"
phx-update="ignore"
>
<div>
<button
type="button"
class="hidden"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#select_machines_modal&quot;}]]"
/>
<div
id="select_machines_modal"
class="fixed z-[10000] inset-0 hidden"
phx-remove="[[&quot;transition&quot;,{&quot;time&quot;:200,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;fade-out&quot;],[],[]]}]]"
>
<!-- Modal container -->
<div class="h-screen flex items-center justify-center p-4">
<!-- Overlay -->
<div
class="absolute z-0 inset-0 bg-gray-500 opacity-75"
aria-hidden="true"
/>
<!-- Modal box -->
<div
class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl "
role="dialog"
aria-modal="true"
tabindex="0"
autofocus=""
phx-window-keydown="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#select_machines_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#select_machines_modal-return&quot;}]]"
phx-key="escape"
>
<button
type="button"
class="absolute top-2 right-2 text-gray-400 flex space-x-1 items-center"
aria_label="close modal"
phx-click="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#select_machines_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#select_machines_modal-return&quot;}]]"
>
<span class="text-sm">(esc)</span>
<i class="ri ri-close-line text-2xl" />
</button>
<div class="min-w-[350px] min-h-[400px] py">
<div class=" p-4 font-bold" />
<div class="w-[95vw] h-[95vh] flex flex-col gap-8 p-5">
<div id="machines_modal_loader">
<div
style=""
class="h-full absolute inset-0 flex flex-row items-center grow transition items-center justify-center appear_from_opacity-3 z-30 rounded p-2"
>
<div class="flex flex-row items-center grow transition items-center justify-center rounded shadow bg-white h-full">
<div
type-node="loader"
class="flex gap-3 items-center justify-center "
>
<span class="text-slate-600 text-md font-medium">Загрузка</span>
<div role="status">
<svg
aria-hidden="true"
class="mr-2 w-6 h-6 text-gray-300 animate-spin dark:text-gray-400 fill-slate-700"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div
id="select_machines"
no-init="true"
phx-hook="MakeTabulator"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div>
<button
type="button"
class="hidden"
phx-click="[[&quot;toggle&quot;,{&quot;display&quot;:null,&quot;ins&quot;:[[],[],[]],&quot;outs&quot;:[[],[],[]],&quot;time&quot;:200,&quot;to&quot;:&quot;#map_modal&quot;}]]"
/>
<div
id="map_modal"
class="fixed z-[10000] inset-0 hidden"
phx-remove="[[&quot;transition&quot;,{&quot;time&quot;:200,&quot;to&quot;:null,&quot;transition&quot;:[[&quot;fade-out&quot;],[],[]]}]]"
>
<!-- Modal container -->
<div class="h-screen flex items-center justify-center p-4">
<!-- Overlay -->
<div
class="absolute z-0 inset-0 bg-gray-500 opacity-75"
aria-hidden="true"
/>
<!-- Modal box -->
<div
class="relative max-h-full overflow-y-auto bg-white rounded-lg shadow-xl "
role="dialog"
aria-modal="true"
tabindex="0"
autofocus=""
phx-window-keydown="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#map_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#map_modal-return&quot;}]]"
phx-click-away="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#map_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#map_modal-return&quot;}]]"
phx-key="escape"
>
<button
type="button"
class="absolute top-2 right-2 text-gray-400 flex space-x-1 items-center"
aria_label="close modal"
phx-click="[[&quot;hide&quot;,{&quot;time&quot;:200,&quot;to&quot;:&quot;#map_modal&quot;,&quot;transition&quot;:[[&quot;ease-in&quot;,&quot;duration-200&quot;],[&quot;opacity-100&quot;],[&quot;opacity-0&quot;]]}],[&quot;dispatch&quot;,{&quot;event&quot;:&quot;click&quot;,&quot;to&quot;:&quot;#map_modal-return&quot;}]]"
>
<span class="text-sm">(esc)</span>
<i class="ri ri-close-line text-2xl" />
</button>
<div class="min-w-[350px] min-h-[400px] py">
<div class="border-b p-4 font-bold">
Карта
</div>
<div
id="ignore:7"
phx-update="ignore"
class="w-[95vw] h-[85vh] m-auto relative"
>
<div id="map_loader">
<div
style="padding: 0"
class="h-full absolute inset-0 flex flex-row items-center grow transition items-center justify-center appear_from_opacity-3 z-30 rounded p-2"
>
<div class="flex flex-row items-center grow transition items-center justify-center rounded shadow bg-white h-full">
<div
type-node="loader"
class="flex gap-3 items-center justify-center "
>
<span class="text-slate-600 text-md font-medium">Загрузка</span>
<div role="status">
<svg
aria-hidden="true"
class="mr-2 w-6 h-6 text-gray-300 animate-spin dark:text-gray-400 fill-slate-700"
viewBox="0 0 100 101"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z"
fill="currentColor"
/>
<path
d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z"
fill="currentFill"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
<div
id="map_show"
phx-hook="CoordsMap"
style="width: 95vw; height: 82vh;"
/>
<div
id="popup"
class="ol-popup hidden transition-all"
>
<a
id="popup-closer"
href="#"
class="ol-popup-closer hover:no-underline top-2 right-2 "
>
<i class="ri-close-fill" />
</a>
<div id="popup-content" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'App',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
@@ -0,0 +1,105 @@
<!-- eslint-disable -->
<script>
import {mapActions, useStore} from "vuex"
import Spinner from "@molecules/Spinner/index.vue"
import { CopyToClipboard } from "@store/hooks/CopyToClipboard"
export default {
name: 'Service',
components: {
Spinner,
},
props: {
idx: {
type: Number,
default: 0
},
name: {
type: String,
default: ""
},
service: {
type: Object,
default: () => {}
},
copyed: {
type: String,
default: "copyed"
},
},
data () {
return {
isCopyed: false
}
},
setup (props, {slots}) {
const store = useStore()
store.dispatch('logger/uploadData', {status: 'info', text: 'Сервис Info'})
},
methods: {
...mapActions('services', ['uploadServiceData']),
isInitParams: function(service) {
if (service?.init_params && typeof service?.init_params === 'object') {
if (service?.init_params?.service && service?.init_params?.machine_addr) return true
}
},
copyText: function (e) {
const id = e.target.getAttribute('id')
const text = e.target.getAttribute('copyOnClickText')
this.isCopyed = true
return new CopyToClipboard('serviceList', text)
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div :key="idx" style="min-width: 240px" class="h-full overflow-hidden shadow-lg rounded-lg h-auto w-80 my-2 md:w-80 p-4 bg-indigo-50 transition duration-200 ease-in-out transform hover:translate-y-1 hover:shadow-2xl">
<div class="w-full flex flex-col h-full justify-center">
<div class="flex my-2 justify-between">
<span class="font-bold"> {{ service?.title }} </span>
<span v-if="service?.is_online || name === 'глобальное хранилище'" class="font-bold text-green-700">доступен</span>
<span v-else class="font-bold text-red-800">отключен </span>
</div>
<hr>
<div class="text-gray-700 my-3 flex items-center flex-grow">
<div v-if="service?.serviceIp || service?.serviceCode" class="flex flex-col gap-4">
<div class="flex flex-col">
<span class="italic text-xs font-bold">Детали</span>
<span v-if="service?.serviceIp" class="italic text-xs">{{ service?.serviceIp }}</span>
<span v-if="service?.serviceCode" class="italic text-xs">{{ service?.serviceCode }}</span>
</div>
<div class="flex flex-col">
<span class="italic text-xs font-bold">Последний перезапуск(UTC):</span>
<span class="italic text-xs">{{ service?.dateStart }}</span>
</div>
<div class="flex flex-col">
<span class="italic text-xs font-bold">Описание сервиса:</span>
<span class="italic text-xs">{{ service?.description || "... нет описания" }}</span>
</div>
</div>
<div v-else class="flex flex-col">
<span class="italic text-xs font-bold">Описание сервиса:</span>
<span class="italic text-xs">Сервис глобального хранилища применяется для условно постоянного хранения данных в кэше</span>
</div>
</div>
<hr>
<div class="mt-2">
<span v-if="isInitParams(service)" class="flex justify-between" >
<span @click="copyText" :copyOnClickText="`${service?.init_params?.service}:${service?.init_params?.machine_addr}`" :copyed="`${isCopyed ? copyed : ''}`" class="copyText italic text-xs flex cursor-pointer" :id="`service_${service?.init_params?.service}`">
<i class="ri-file-copy-2-line text-slate-600 mr-1"></i>
{{ service?.serviceLink }}
</span>
<a :href="`/admin_panel/auth_service?show_service_cache=${name}`" id="selectService" @click="uploadServiceData(service)" class="flex italic text-xs cursor-pointer underline hover:text-gray-500 linear-loader">
открыть
</a>
</span>
<div v-else class="italic text-xs">нет доступа к глоб. хранилищу</div>
</div>
</div>
</div>
</template>
@@ -0,0 +1,74 @@
import Services from './index.vue'
import { globalServices } from '@store/modules/services/StaticData'
import { shallowMount, mount } from '@vue/test-utils'
import {createStore} from 'vuex'
const store = createStore({
state() {
return {
services: globalServices,
}
},
getters: {
services: (state) => state.services,
},
mutations: {
setServices: (state, updval) => state.services = updval,
},
})
describe("Services", () => {
it("renders Services, with custom button title", () => {
const wrapper = mount(Services, {
global: {
plugins: [store],
},
data () {
return {
titleServicesButton: 'Показать больше сервисов',
}
},
})
expect(wrapper.text()).toContain('Показать больше сервисов')
})
})
describe("Services", () => {
it("renders after click button, open full Services", async () => {
const wrapper = mount(Services, {
global: {
plugins: [store],
},
})
await wrapper.find('button').trigger('click')
expect(wrapper.find('button').text()).toContain('Скрыть')
})
})
describe("Services", () => {
it("renders Services with exists data", async () => {
const wrapper = mount(Services, {
global: {
plugins: [store],
},
})
expect(store.state.services.length).toBeGreaterThan(0)
})
})
describe("Services", () => {
it("renders Services maked empty data", async () => {
const wrapper = mount(Services, {
global: {
plugins: [store],
},
})
store.commit('setServices', [])
expect(store.state.services.length).toBe(0)
})
})
@@ -0,0 +1,67 @@
<!-- eslint-disable -->
<script>
import {mapGetters, useStore} from "vuex"
import Service from './Service.vue'
import Spinner from "@molecules/Spinner/index.vue"
import Tabulator from "@molecules/Tabulator/index.vue"
export default {
name: 'Services',
components: {
Service,
Spinner,
Tabulator,
},
props: {
},
data () {
return {
isOpenServices: false,
titleServicesButton: 'Показать больше'
}
},
setup (props, {slots}) {
const store = useStore()
store.dispatch('services/uploadData')
},
computed: {
...mapGetters('services', ['services']),
},
methods: {
sortingServices: function (services) {
return services?.sort((a, b) => b.is_online - a.is_online)
},
toggleServices: function (isOpenServices) {
this.isOpenServices = !isOpenServices
this.titleServicesButton = this.isOpenServices ? 'Скрыть' : 'Показать больше'
}
}
}
</script>
<!-- eslint-disable -->
<template>
<div class="col-span-12">
<div class="flex flex-col mt-4 shadow-md mb-1 mx-5 bg-white" style="max-width: calc(100vw - 40px); margin-bottom: 30px;">
<div class="flex flex-col flex-wrap justify-between px-4 pb-2 pt-8 w-full">
<div class="text-lg font-bold text-center text-gray-600">Информация о сети (кэш)</div>
<div class="flex flex-col max-w-full">
<div>
Сервисы, записывающие данные в кэш
</div>
<div id="serviceList" :class="`flex flex-wrap h-auto flex-row mt-4 overflow-x-auto w-full gap-4 transition-all pb-4 ${isOpenServices ? 'max-h-full' : 'max-h-[220px]'}`">
<div v-for="(service, idx) in sortingServices(services)">
<Service :idx="idx" :name="service.service || service.name" :service="service" />
</div>
</div>
</div>
</div>
<button @click="toggleServices(isOpenServices)" class="w-full max-h-[90px] py-2.5 px-5 text-sm font-medium text-gray-900 bg-gray-100 border-t border-gray-200 hover:bg-gray-100 hover:text-slate-700 dark:bg-gray-700 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
{{titleServicesButton}}
</button>
</div>
</div>
</template>
@@ -0,0 +1,122 @@
import Service from './Service.vue'
import { globalServices } from '@store/modules/services/StaticData'
import { shallowMount, mount } from '@vue/test-utils'
import {createStore} from 'vuex'
const store = createStore({
state() {
return {
services: globalServices,
}
},
getters: {
services: (state) => state.services,
},
mutations: {
setServices: (state, updval) => state.services = updval,
},
})
describe("Service", () => {
it("renders Service, with set props value", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
idx: 1,
},
})
await wrapper.setProps({ idx: 3 })
expect(wrapper.html()).toContain(3)
})
})
describe("Service", () => {
it("renders Service with condition success", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
name: '',
},
})
await wrapper.setProps({ name: 'глобальное хранилище' })
expect(wrapper.html()).toContain('доступен')
})
})
describe("Service", () => {
it("renders Service with condition not success", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
name: '',
},
})
await wrapper.setProps({ name: 'Test service name' })
expect(wrapper.html()).toContain('отключен')
})
})
describe("Service", () => {
it("renders Service if not service details", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
service: {},
},
})
expect(wrapper.html()).toContain('Сервис глобального хранилища применяется для условно постоянного хранения данных в кэше')
})
})
describe("Service", () => {
it("renders Service with set service details", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
service: {serviceIp: '192.168.1.1', serviceCode: '1234567890', serviceLink: 'http://test.com', is_online: false,
},
},
})
await wrapper.setProps({ service: {serviceIp: '192.168.1.7', serviceCode: '1234567897', is_online: true} }) // 1 раз, далее новый тест
expect(wrapper.html()).toContain('192.168.1.7')
expect(wrapper.html()).toContain('1234567897')
expect(wrapper.html()).toContain('доступен')
expect(wrapper.html()).toContain('... нет описания')
})
})
describe("Service", () => {
it("renders Service with alert copyText success after click", async () => {
const wrapper = mount(Service, {
global: {
plugins: [store],
},
props: {
copyed: '',
service: {init_params: {service: 'Test service', machine_addr: 'Test machine_addr'}},
},
})
await wrapper.get('.copyText').trigger('click')
expect(wrapper.html()).toContain('copyed')
})
})
+761
View File
@@ -0,0 +1,761 @@
export default dom = (() => {
const getNodeList = (arg) => {
if (typeof arg === "string" && arg.trim().slice(0, 1) !== "<") {
return document.querySelectorAll(arg);
} else if (typeof arg === "string" && arg.trim().slice(0, 1) === "<") {
const dom = domParser(arg);
return [dom];
} else if (typeof arg === "object" && arg instanceof NodeList) {
return arg;
} else if (typeof arg === "object" && arg instanceof HTMLElement) {
return [arg];
} else if (typeof arg === "object" && arg instanceof SVGElement) {
return [arg];
} else {
return arg;
}
};
const domParser = (arg) => {
const parser = new DOMParser(),
content = "text/html",
DOM = parser.parseFromString(arg, content);
return DOM.body.childNodes[0];
};
const addEvent = (nodeList, event, callback) => {
nodeList[event] = callback;
};
const uuidv4 = () => {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
let r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
const eventPath = (evt) => {
let path = (evt.composedPath && evt.composedPath()) || evt.path,
target = evt.target;
if (path != null) {
// Safari doesn't include Window, but it should.
return path.indexOf(window) < 0 ? path.concat(window) : path;
}
if (target === window) {
return [window];
}
function getParents(node, memo) {
memo = memo || [];
let parentNode = node.parentNode;
if (!parentNode) {
return memo;
} else {
return getParents(parentNode, memo.concat(parentNode));
}
}
return [target].concat(getParents(target), window);
};
const addEvents = (nodeList) => {
// .on()
addEvent(nodeList, "on", (arg1, arg2, arg3) => {
nodeList.forEach((node) => {
node.addEventListener(
arg1,
(e) => {
const uuid = uuidv4();
if (typeof arg2 === "string") {
eventPath(e).every((parentNode) => {
if (parentNode.matches && parentNode.matches(arg2)) {
parentNode[uuid] = arg3;
parentNode[uuid](parentNode);
delete parentNode[uuid];
return false;
} else {
return true;
}
});
} else {
node[uuid] = arg2;
node[uuid](e);
delete node[uuid];
}
},
false
);
});
return nodeList;
});
// .css()
addEvent(nodeList, "css", (arg1, arg2) => {
// Get values
if (arg2 === undefined && typeof arg1 !== "object") {
return getComputedStyle(nodeList[0])[arg1];
}
// Set values
nodeList.forEach((node) => {
if (typeof arg1 === "object") {
for (const [key, val] of Object.entries(arg1)) {
node.style[key] = val;
}
} else {
node.style[arg1] = arg2;
}
});
addEvents(nodeList);
return nodeList;
});
// .slideUp()
addEvent(nodeList, "slideUp", (arg1 = 300, arg2 = () => {}) => {
nodeList.forEach((node) => {
node.style.transitionProperty = "height, margin, padding";
node.style.transitionDuration = arg1 + "ms";
node.style.height = node.offsetHeight + "px";
node.offsetHeight;
node.style.overflow = "hidden";
node.style.height = 0;
node.style.paddingTop = 0;
node.style.paddingBottom = 0;
node.style.marginTop = 0;
node.style.marginBottom = 0;
window.setTimeout(() => {
node.style.display = "none";
node.style.removeProperty("height");
node.style.removeProperty("padding-top");
node.style.removeProperty("padding-bottom");
node.style.removeProperty("margin-top");
node.style.removeProperty("margin-bottom");
node.style.removeProperty("overflow");
node.style.removeProperty("transition-duration");
node.style.removeProperty("transition-property");
const uuid = uuidv4();
node[uuid] = arg2;
node[uuid](node);
delete node[uuid];
}, arg1);
});
addEvents(nodeList);
return nodeList;
});
// .slideDown()
addEvent(nodeList, "slideDown", (arg1 = 300, arg2 = () => {}) => {
nodeList.forEach((node) => {
node.style.removeProperty("display");
let display = window.getComputedStyle(node).display;
if (display === "none") display = "block";
node.style.display = display;
let height = node.offsetHeight;
node.style.overflow = "hidden";
node.style.height = 0;
node.style.paddingTop = 0;
node.style.paddingBottom = 0;
node.style.marginTop = 0;
node.style.marginBottom = 0;
node.offsetHeight;
node.style.transitionProperty = "height, margin, padding";
node.style.transitionDuration = arg1 + "ms";
node.style.height = height + "px";
node.style.removeProperty("padding-top");
node.style.removeProperty("padding-bottom");
node.style.removeProperty("margin-top");
node.style.removeProperty("margin-bottom");
window.setTimeout(() => {
node.style.removeProperty("height");
node.style.removeProperty("overflow");
node.style.removeProperty("transition-duration");
node.style.removeProperty("transition-property");
const uuid = uuidv4();
node[uuid] = arg2;
node[uuid](node);
delete node[uuid];
}, arg1);
});
addEvents(nodeList);
return nodeList;
});
// .fadeOut()
addEvent(nodeList, "fadeOut", (arg1 = 300, arg2 = () => {}) => {
nodeList.forEach((node) => {
node.style.opacity = 1;
node.style.transitionProperty = "opacity";
node.style.transitionDuration = arg1 + "ms";
node.style.opacity = 0;
window.setTimeout(() => {
node.style.display = "none";
node.style.removeProperty("transition-property");
node.style.removeProperty("transition-duration");
node.style.removeProperty("opacity");
const uuid = uuidv4();
node[uuid] = arg2;
node[uuid](node);
delete node[uuid];
}, arg1);
});
addEvents(nodeList);
return nodeList;
});
// .fadeIn()
addEvent(nodeList, "fadeIn", (arg1 = 300, arg2 = () => {}) => {
nodeList.forEach((node) => {
let display = window.getComputedStyle(node).display;
if (display === "none") display = "block";
node.style.display = display;
node.style.opacity = 0;
node.style.transitionProperty = "opacity";
node.style.transitionDuration = arg1 + "ms";
window.setTimeout(() => {
node.style.opacity = 1;
window.setTimeout(() => {
node.style.removeProperty("transition-property");
node.style.removeProperty("transition-duration");
node.style.removeProperty("opacity");
}, arg1);
const uuid = uuidv4();
node[uuid] = arg2;
node[uuid](node);
delete node[uuid];
}, arg1);
});
addEvents(nodeList);
return nodeList;
});
// .hide()
addEvent(nodeList, "hide", () => {
nodeList.forEach((node) => {
node.style.display = "none";
});
addEvents(nodeList);
return nodeList;
});
// .show()
addEvent(nodeList, "show", () => {
nodeList.forEach((node) => {
if (node.style.display === "none") {
node.style.display = "block";
}
});
addEvents(nodeList);
return nodeList;
});
// .clone()
addEvent(nodeList, "clone", () => {
let clonedNodes = [];
nodeList.forEach((node) => {
clonedNodes.push(node.cloneNode(true));
});
addEvents(clonedNodes);
return clonedNodes;
});
// .each()
addEvent(nodeList, "each", (arg1) => {
nodeList.forEach((node, index) => {
const uuid = uuidv4();
node[uuid] = arg1;
node[uuid](index, node);
delete node[uuid];
});
addEvents(nodeList);
return nodeList;
});
// .find()
addEvent(nodeList, "find", (arg1) => {
let results = [];
nodeList.forEach((node) => {
const matchedNodes = node.querySelectorAll(arg1);
if (matchedNodes.length) {
matchedNodes.forEach((matchedNode) => {
results.push(matchedNode);
});
}
});
addEvents(results);
return results;
});
// .hasClass()
addEvent(nodeList, "hasClass", (arg1) => {
let found = false;
nodeList.forEach((node) => {
if (node.classList.contains(arg1)) found = true;
});
return found;
});
// .removeClass()
addEvent(nodeList, "removeClass", (arg1) => {
if (arg1.length) {
arg1.split(" ").forEach((classname) => {
nodeList.forEach((node) => {
node.classList.remove(classname);
});
});
}
addEvents(nodeList);
return nodeList;
});
// .addClass()
addEvent(nodeList, "addClass", (arg1) => {
if (arg1.length) {
arg1.split(" ").forEach((classname) => {
nodeList.forEach((node) => {
node.classList.add(classname);
});
});
}
addEvents(nodeList);
return nodeList;
});
// .is()
addEvent(nodeList, "is", (arg1) => {
if (typeof arg1 === "string") {
return (
nodeList[0].matches ||
nodeList[0].matchesSelector ||
nodeList[0].msMatchesSelector ||
nodeList[0].mozMatchesSelector ||
nodeList[0].webkitMatchesSelector ||
nodeList[0].oMatchesSelector
).call(nodeList[0], arg1);
} else {
return nodeList[0] === arg1;
}
});
// .attr()
addEvent(nodeList, "attr", (arg1, arg2) => {
if (arg2 === undefined && typeof arg1 !== "object") {
if (nodeList[0] !== undefined) {
const attr = nodeList[0].getAttribute(arg1);
return attr === null ? undefined : attr;
} else {
return undefined;
}
}
nodeList.forEach((node) => {
if (typeof arg1 === "object") {
for (const [key, val] of Object.entries(arg1)) {
node.setAttribute(key, val);
}
} else {
node.setAttribute(arg1, arg2);
}
});
addEvents(nodeList);
return nodeList;
});
// .removeAttr()
addEvent(nodeList, "removeAttr", (arg1) => {
nodeList.forEach((node) => {
node.removeAttribute(arg1);
});
addEvents(nodeList);
return nodeList;
});
// .data()
addEvent(nodeList, "data", (arg1, arg2) => {
if (arg2 === undefined) {
const attr = nodeList[0].getAttribute(`data-${arg1}`);
return attr === null ? undefined : attr;
}
nodeList.forEach((node) => {
node.setAttribute(`data-${arg1}`, arg2);
});
addEvents(nodeList);
return nodeList;
});
// .width()
addEvent(nodeList, "width", (arg1) => {
if (arg1 === undefined) {
if (nodeList === window) {
return parseInt(window.innerWidth);
} else {
return typeof nodeList[0] !== "undefined"
? parseInt(getComputedStyle(nodeList[0])["width"])
: null;
}
}
if (nodeList === window) {
window.resizeTo(arg1, window.innerHeight);
} else {
nodeList.forEach((node) => {
node.style["width"] = arg1;
});
}
addEvents(nodeList);
return nodeList;
});
// .height()
addEvent(nodeList, "height", (arg1) => {
if (arg1 === undefined) {
if (nodeList === window) {
return parseInt(window.innerHeight);
} else {
return typeof nodeList[0] !== "undefined"
? parseInt(getComputedStyle(nodeList[0])["height"])
: null;
}
}
if (nodeList === window) {
window.resizeTo(window.innerWidth, arg1);
} else {
nodeList.forEach((node) => {
node.style["height"] = arg1;
});
}
addEvents(nodeList);
return nodeList;
});
// .css()
addEvent(nodeList, "css", (arg1, arg2) => {
if (arg2 === undefined && typeof arg1 !== "object") {
return getComputedStyle(nodeList[0])[arg1];
}
nodeList.forEach((node) => {
if (typeof arg1 === "object") {
for (const [key, val] of Object.entries(arg1)) {
node.style[key] = val;
}
} else {
node.style[arg1] = arg2;
}
});
addEvents(nodeList);
return nodeList;
});
// .replaceWith()
addEvent(nodeList, "replaceWith", (arg1) => {
const replacerNodes = [];
const argumentTypes = getNodeList(arg1);
nodeList.forEach((node, key) => {
argumentTypes.forEach((replacerNode) => {
let dom = replacerNode;
if (key > 0) {
dom = replacerNode.cloneNode(true);
}
node.parentNode.insertBefore(dom, node.nextSibling);
replacerNodes.push(dom);
});
node.remove();
});
addEvents(replacerNodes);
return replacerNodes;
});
// .insertAfter()
addEvent(nodeList, "insertAfter", (arg1) => {
const insertedNodes = [];
const argumentTypes = getNodeList(arg1);
nodeList.forEach((node) => {
argumentTypes.forEach((targetNode, key) => {
let dom = node;
if (key > 0) {
dom = node.cloneNode(true);
}
targetNode.parentNode.insertBefore(dom, targetNode.nextSibling);
insertedNodes.push(dom);
});
});
addEvents(insertedNodes);
return insertedNodes;
});
// .appendTo()
addEvent(nodeList, "appendTo", (arg1) => {
const appendedNodes = [];
const argumentTypes = getNodeList(arg1);
nodeList.forEach((node) => {
argumentTypes.forEach((targetNode, key) => {
let dom = node;
if (key > 0) {
dom = node.cloneNode(true);
}
targetNode.appendChild(dom);
appendedNodes.push(dom);
});
});
addEvents(appendedNodes);
return appendedNodes;
});
// .append()
addEvent(nodeList, "append", (arg1) => {
const argumentTypes = getNodeList(arg1);
nodeList.forEach((node, key) => {
argumentTypes.forEach((appendNode) => {
let dom = appendNode;
if (key > 0) {
dom = appendNode.cloneNode(true);
}
node.appendChild(dom);
});
});
addEvents(nodeList);
return nodeList;
});
// .remove()
addEvent(nodeList, "remove", () => {
nodeList.forEach((node) => {
if (node.parentNode !== null) {
node.parentNode.removeChild(node);
}
});
addEvents(nodeList);
return nodeList;
});
// .first()
addEvent(nodeList, "first", () => {
const firstNode = nodeList[0] !== undefined ? [nodeList[0]] : [];
addEvents(firstNode);
return firstNode;
});
// .last()
addEvent(nodeList, "last", () => {
const lastNodeList =
nodeList[nodeList.length - 1] !== undefined
? [nodeList[nodeList.length - 1]]
: [];
addEvents(lastNodeList);
return lastNodeList;
});
// .val()
addEvent(nodeList, "val", (arg1) => {
if (arg1 === undefined) {
if (nodeList[0] instanceof HTMLSelectElement && nodeList[0].multiple) {
const selectedOptions = [];
for (const selectedOption of nodeList[0].selectedOptions) {
selectedOptions.push(selectedOption.value);
}
return selectedOptions;
} else {
return nodeList[0].value;
}
}
nodeList.forEach((node) => {
if (node instanceof HTMLSelectElement) {
node.value = "";
if (typeof arg1 !== "object") {
arg1 = [arg1];
}
for (const value of arg1) {
const selectedOption = Array.from(node).find(
(option) => option.value == value
);
if (selectedOption !== undefined) {
selectedOption.selected = true;
}
}
} else {
node.value = arg1;
}
});
addEvents(nodeList);
return nodeList;
});
// .html()
addEvent(nodeList, "html", (arg1) => {
if (arg1 === undefined) {
return nodeList[0].innerHTML;
}
nodeList.forEach((node) => {
node.innerHTML = arg1;
});
addEvents(nodeList);
return nodeList;
});
// .text()
addEvent(nodeList, "text", (arg1) => {
if (arg1 === undefined) {
return nodeList[0].textContent;
}
nodeList.forEach((node) => {
node.textContent = arg1;
});
addEvents(nodeList);
return nodeList;
});
// .filter()
addEvent(nodeList, "filter", (arg1) => {
let results = [];
nodeList.forEach((node, index) => {
const uuid = uuidv4();
node[uuid] = arg1;
const filter = node[uuid](index, node);
delete node[uuid];
if (filter) {
results.push(node);
}
});
addEvents(results);
return results;
});
// .closest()
addEvent(nodeList, "closest", (arg1) => {
let results = [];
nodeList.forEach((node) => {
const matchedNode = node.closest(arg1);
if (
matchedNode !== null &&
!results.filter((resNode) => resNode === matchedNode).length
)
results.push(matchedNode);
});
addEvents(results);
return results;
});
// .children()
addEvent(nodeList, "children", (arg1) => {
let results = [];
nodeList.forEach((node) => {
for (const matchedNode of node.children) {
if (arg1 === undefined) {
results.push(matchedNode);
} else {
for (const childNode of node.querySelectorAll(arg1)) {
if (childNode === matchedNode) results.push(childNode);
}
}
}
});
addEvents(results);
return results;
});
// .parent()
addEvent(nodeList, "parent", () => {
let results = [];
nodeList.forEach((node) => {
const matchedNode = node.parentNode;
if (
matchedNode !== null &&
!results.filter((resNode) => resNode === matchedNode).length
)
results.push(matchedNode);
});
addEvents(results);
return results;
});
// .prev()
addEvent(nodeList, "prev", () => {
let results = [];
nodeList.forEach((node) => {
if (node.previousElementSibling !== null) {
results.push(node.previousElementSibling);
}
});
addEvents(results);
return results;
});
// .next()
addEvent(nodeList, "next", () => {
let results = [];
nodeList.forEach((node) => {
if (node.nextElementSibling !== null) {
results.push(node.nextElementSibling);
}
});
addEvents(results);
return results;
});
};
return (window.dom = (arg) => {
const nodeList = getNodeList(arg);
addEvents(nodeList);
return nodeList;
});
})();
+179
View File
@@ -0,0 +1,179 @@
import * as Popper from "@popperjs/core";
import dom from "./dom";
(function (dom) {
"use strict";
function hide() {
dom(".dropdown-menu").each(async function () {
if (
dom(this).attr("id") !== undefined &&
dom('[data-dropdown-replacer="' + dom(this).attr("id") + '"]').length &&
dom(this).data("dropdown-programmatically") === undefined
) {
let randId = dom(this).attr("id");
let dropdownToggle = dom('[data-dropdown-replacer="' + randId + '"]')
.parent()
.find("[data-tw-toggle='dropdown']");
dom(this).removeClass("show");
const event = new Event("hide.tw.dropdown");
dom(dropdownToggle).parent()[0].dispatchEvent(event);
await setTimeout(() => {
dom('[data-dropdown-replacer="' + randId + '"]').replaceWith(this);
dom(this).removeAttr("style");
dom(this).removeAttr("data-popper-placement");
dom(dropdownToggle).attr("aria-expanded", false);
const event = new Event("hidden.tw.dropdown");
dom(dropdownToggle).parent()[0].dispatchEvent(event);
}, 200);
} else if (
dom(this).attr("id") !== undefined &&
!dom('[data-dropdown-replacer="' + dom(this).attr("id") + '"]')
.length &&
dom(this).hasClass("show") &&
dom(this).data("dropdown-programmatically") === undefined
) {
dom(this).remove();
} else if (dom(this).data("dropdown-programmatically") == "initiate") {
dom(this).attr("data-dropdown-programmatically", "showed");
} else if (dom(this).data("dropdown-programmatically") == "showed") {
dom(this).removeAttr("data-dropdown-programmatically");
hide();
}
});
}
function findVisibleDropdownToggle(dropdownToggle) {
return dropdownToggle.filter((key, dropdownToggle) => {
return dropdownToggle.offsetParent !== null;
});
}
async function show(dropdown) {
let dropdownBox = dom(dropdown).find(".dropdown-menu").first();
let dropdownToggle = findVisibleDropdownToggle(
dom(dropdown).find("[data-tw-toggle='dropdown']")
);
let placement = dom(dropdown).data("tw-placement")
? dom(dropdown).data("tw-placement")
: "bottom-end";
let randId = "_" + Math.random().toString(36).substr(2, 9);
hide();
if (dom(dropdownBox).length) {
dom(dropdownToggle).attr("aria-expanded", true);
dom(dropdown).css("position") == "static"
? dom(dropdown).css("position", "relative")
: "";
dom(dropdownBox).css("width", dom(dropdownBox).css("width").localeCompare("300px") ? "300px": dom(dropdownBox).css("width"));
dom('<div data-dropdown-replacer="' + randId + '"></div>').insertAfter(
dropdownBox
);
dom(dropdownBox).attr("id", randId).appendTo("body");
dom(".modal.show").each(function () {
if (dom(this).find('[data-dropdown-replacer="' + randId + '"]')) {
dom(dropdownBox).css("z-index", dom(this).css("z-index"));
}
});
Popper.createPopper(dropdownToggle[0], dropdownBox[0], {
placement: placement,
});
dom(dropdownBox).addClass("show");
const event = new Event("show.tw.dropdown");
dom(dropdown)[0].dispatchEvent(event);
await setTimeout(() => {
const event = new Event("shown.tw.dropdown");
dom(dropdown)[0].dispatchEvent(event);
}, 200);
}
}
function toggleProgrammatically(dropdown) {
let dropdownBox = dom(dropdown).find(".dropdown-menu").first();
if (dom(dropdownBox).length) {
showProgrammatically(dropdown);
} else {
hide();
}
}
function showProgrammatically(dropdown) {
if (dom(dropdown).find(".dropdown-menu").length) {
dom(dropdown)
.find(".dropdown-menu")
.attr("data-dropdown-programmatically", "initiate");
} else {
let randId = dom("[data-dropdown-replacer]").data("dropdown-replacer");
dom("#" + randId).attr("data-dropdown-programmatically", "initiate");
}
show(dropdown);
}
function createInstance(dropdownToggle) {
const dropdown = dom(dropdownToggle).closest(".dropdown");
return {
show() {
showProgrammatically(dropdown);
},
hide() {
hide();
},
toggle() {
toggleProgrammatically(dropdown);
},
};
}
dom("body").on("click", function (event) {
let dropdown = dom(event.target).closest(".dropdown");
let dropdownToggle = dom(dropdown).find("[data-tw-toggle='dropdown']");
let dropdownBox = dom(dropdown).find(".dropdown-menu").first();
let activeDropdownBox = dom(event.target).closest(".dropdown-menu").first();
let dismissButton = dom(event.target).data("tw-dismiss");
if (
(!dom(dropdown).length && !dom(activeDropdownBox).length) ||
(dom(dropdownToggle).length && !dom(dropdownBox).length) ||
dismissButton == "dropdown"
) {
hide();
} else if (!dom(activeDropdownBox).length) {
show(dropdown);
}
});
document.addEventListener("keydown", function (event) {
if (event.code == "Escape") {
hide();
}
});
(function init() {
dom("[data-tw-toggle='dropdown']").each(function () {
this["__dropdown"] = createInstance(this);
});
if (window.tailwind === undefined) window.tailwind = {};
window.tailwind.Dropdown = {
getInstance(el) {
return el.__dropdown;
},
getOrCreateInstance(el) {
return el.__dropdown === undefined ? createInstance(el) : el.__dropdown;
},
};
})();
})(dom);
+20
View File
@@ -0,0 +1,20 @@
<template>
<div>
123
</div>
</template>
<script>
export default {
name: 'Name',
components: {},
data() {
},
computed: {
},
methods: {
}
}
</script>
+55
View File
@@ -0,0 +1,55 @@
import { type } from 'ramda'
/* eslint-disable */
export const runAfter = (func, wait, immediate) => {
let timeout
return function () {
const context = this
const args = arguments
const later = function () {
timeout = null
if (!immediate) func.apply(context, args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
if (callNow) func.apply(context, args)
}
}
export const pipe = (x) => (...fns) => fns.reduce((r, fn) => fn(r), x)
export const capitalizeFirstLetter = (string) => {
if (string) return string.charAt(0).toUpperCase() + string.slice(1);
else {
const error = `error by func capitalizeFirstLetter, ivalid string. string can't equal ${string}`
console.error(error)
return error;
}
}
export const lowerFirstLetter = (string) => {
if (string && string[0] && type(string) === "String") return string[0].toLowerCase() + string.slice(1);
else {
const error = `error by func lowerFirstLetter, ivalid string. string can't equal ${string}`
console.error(error)
return error;
}
}
export const substringIfNeed = (string, length = 40) => {
if (string && `${string}`.length > length) {
return `${string}`.substring(0, length) + "...";
}
return string;
}
export const copyObj = (mainObj) => {
let objCopy = {};
let key;
for (key in mainObj) {
objCopy[key] = mainObj[key];
}
return objCopy;
}
+15
View File
@@ -0,0 +1,15 @@
import { createApp } from 'vue'
import "./styles/_index.css"
import 'remixicon/fonts/remixicon.css'
import App from './App.vue'
import Router from "@router"
import ramdaVue from "ramda-vue";
import { store } from '@/store'
import "./helpers/dropdown.js"
createApp(App)
.use(Router)
.use(ramdaVue)
.use(store)
.mount('#app')
+152
View File
@@ -0,0 +1,152 @@
import {createRouter, createWebHistory} from "vue-router";
import Main from "@pages/Main/index.vue"
import Auth from "@pages/Auth/index.vue"
import Communication from "@pages/Communication/index.vue"
import LastPacksNum from "@pages/LastPacksNum/index.vue"
import Fuel from "@pages/Fuel/index.vue"
import Cron from "@pages/Cron/index.vue"
import ICCID from "@pages/ICCID/index.vue"
import Devices from "@pages/Devices/index.vue"
import FreePacks from "@pages/FreePacks/index.vue"
import Packs from "@pages/Packs/index.vue"
import FileViewer from "@pages/FileViewer/index.vue"
import NotFound from "@pages/404/index.vue"
import Diap from "@admin_pages/Diap/index.vue"
import News from "@admin_pages/News/index.vue"
import Machines from "@pages/Main/Machines/Machines.vue"
import FinderPacks from "@pages/Main/FinderPacks/FinderPacks.vue"
import AddUsers from "@pages/Main/AddUsers/AddUsers.vue"
import Services from "@admin_pages/Services/index.vue"
import ServiceManage from "@admin_pages/ServiceManage/index.vue"
import {get} from "@store/modules/apiHelpers"
import {cond, T} from 'ramda'
const routes = [
{
path: "/",
name: "main",
component: Main,
children: [
{
path: "/",
name: "machines",
component: Machines
},
{
path: "finder_packs",
name: "finder_packs",
component: FinderPacks
},
{
path: "add_users",
name: "add_users",
component: AddUsers
},
]
},
{
path: "/html/last_packs",
name: "last_packs",
component: import("@pages/LastPacks/index.vue")
},
{
path: "/html/controll_com_service",
name: "communication",
component: import("@pages/Communication/index.vue")
},
{
path: "/html/last_packs_by_pack",
name: "last_packs_by_pack",
component: LastPacksNum
},
{
path: "/html/live_fuel",
name: "fuel",
component: Fuel
},
{
path: "/html/cron",
name: "cron",
component: Cron
},
{
path: "/html/fetch_iccid",
name: "iccid",
component: ICCID
},
{
path: "/html/fetch_askr_devices",
name: "devices",
component: Devices
},
{
path: "/html/packs/:pack",
name: "packs",
component: Packs
},
{
path: "/html/free_packs",
name: "free_packs",
component: FreePacks
},
{
path: "/html/file_viewer/:mode",
name: "file_viewer",
component: FileViewer
},
{
path: "/admin_panel/diap",
name: "diap",
component: Diap
},
{
path: "/admin_panel/services",
name: "services",
component: Services
},
{
path: "/users/log_out",
name: "auth",
component: Auth
},
{
path: '/:pathMatch(.*)*',
name: '404',
component: NotFound
},
];
const routerHistory = createWebHistory();
const router = createRouter({
history: routerHistory,
routes
});
const logout = (from, next) => {
localStorage.removeItem("token")
sessionStorage.setItem("pathname", from.fullPath)
next()
}
const auth_check = async () => {
return await get("/api/auth/loginStatus", ({data}) => {
return data
})
}
// router.beforeEach(async (to, from, next) => {
// const status = await auth_check()
// return cond([
// [() => to.name === "auth" && from.name === "auth", () => next()],
// [() => to.name === "auth", () => logout(from, () => next())],
// [() => !status, () => logout(from, () => next({name: "auth"}))],
// [T, () => next()],
// ])()
// })
export {routes};
export default router;
+54
View File
@@ -0,0 +1,54 @@
export class Alert {
constructor({ id, color, icon, message, delay = 4000, isManualClose, width = 'auto' }) {
const wrapper = document.createElement("div");
const alert = document.createElement("div");
wrapper.appendChild(alert)
const closeIcon = document.createElement("i");
closeIcon.className = `ri-close-circle-fill cursor-pointer text-${color}-800 text-xl`;
closeIcon.onclick = () => {
this.destroy()
}
alert.className = `bg-${color}-100 bg-light shadow-md rounded-lg py-5 px-6 mb-3 text-base text-${color}-700 inline-flex items-center fixed transition_up top-0 translate-y-4 left-0 w-fit slideInFromTop border border-${color}-300 gap-4 flex items-center`;
alert.style.zIndex = "10000";
alert.style.width = width
alert.style.transform = "translateY(-5rem) translateX(calc(50vw - 50%))";
alert.setAttribute("role", "alert");
alert.innerHTML = `
<div class="w-fit gap-1 flex items-center">
${icon}
${typeof message === 'string' ? message : JSON.stringify(message)}
</div>
`;
alert.appendChild(closeIcon);
document.getElementById(id).appendChild(wrapper);
setTimeout(() => {
if (delay > 100 && alert) {
alert.style.transform =
"translateY(1.25rem) translateX(calc(50vw - 50%))";
}
}, 300);
setTimeout(() => {
if(!isManualClose) {
this.destroy()
}
}, delay)
this.alert = alert;
this.wrapper = wrapper;
}
destroy() {
if (this.alert) {
this.alert.style.transform =
"translateY(-5rem) translateX(calc(50vw - 50%))";
setTimeout(() => {
this.wrapper.innerHTML = "";
this.alert = null;
}, 300);
}
}
}
@@ -0,0 +1,53 @@
import { Alert } from "@store/hooks/Alert"
export class CopyToClipboard {
constructor (id, text) {
this.id = id
this.el = document.getElementById(id)
this.text = this.el?.getAttribute('copyOnClickText')
this.clipBoardText = text
if (this.el && this.clipBoardText && !this.text) {
this.copy(this.clipBoardText, this.id)
}
if (this.el && this.text && !this.clipBoardText) {
this.copy(this.text, this.id)
}
}
copy (text, id) {
if (!navigator.clipboard) {
fallbackCopyTextToClipboard(text)
return
}
navigator.clipboard.writeText(text).then(function() {
new Alert({id: id, color: "green", icon: `<i class="ri-book-fill"></i>`, message: "Текст скопирован", delay: 2000})
}, function(err) {
new Alert({id: id, color: "yellow", icon: `<i class="ri-book-fill"></i>`, message: "Ваш браузер не поддерживает копирование", delay: 2000})
console.error('Async: Could not copy text: ', err)
})
}
fallbackCopyTextToClipboard (text, id) {
var textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.top = "-2000px";
textArea.style.left = "-2000px";
textArea.style.position = "fixed";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
new Alert({id: id, color: "green", icon: `<i class="ri-book-fill"></i>`, message: "Текст скопирован", delay: 2000})
} catch (err) {
new Alert({id: id, color: "yellow", icon: `<i class="ri-book-fill"></i>`, message: "Ваш браузер не поддерживает копирование", delay: 2000})
}
document.body.removeChild(textArea);
}
}
@@ -0,0 +1,110 @@
import {groupByKeys} from "./staticData"
import {getTextWidth, isEqualKeyValue} from "./helpers"
const viewGraphicsPieBar = (chartId, period = '', countMachines, title, windowConfig, isOpenMenu = false, type) => {
const elements = []
const elementsMenu = []
const elPeriod = period ? {id: 'period', name: 'Период загрузки данных', type: 'text', right: windowConfig?.pie?.graphic?.period?.right, top: windowConfig?.pie?.graphic?.period?.top, z: 999, style: {text: period, fill: '#777', fontSize: windowConfig?.pie?.graphic?.period?.fontSize}} : ''
elements.push(elPeriod)
const elCount = {id: 'count', type: 'text', left: windowConfig?.pie?.graphic?.count?.left, top: windowConfig?.pie?.graphic?.count?.top, z: 999, style: {text: countMachines, textAlign: 'center', fontSize: windowConfig?.pie?.graphic?.count?.fontSize}}
!chartId.includes('bar') ? elements.push(elCount) : ''
const elMenu = {id: 'menu', info: {typeEl: 'menu', chartId: chartId, type: type}, type: 'image', right: windowConfig?.bar?.graphic?.menu?.right, top: windowConfig?.bar?.graphic?.menu?.top, z: 999, style: {image: '/images/charts-more-vertical.svg', width: 17, height: 17}}
elements.push(elMenu)
const elRect = {id: 'rect', info: {typeEl: 'rect'}, type: 'rect', right: windowConfig?.bar?.graphic?.rect?.right, top: windowConfig?.bar?.graphic?.rect?.top, z: 999, shape: {width: 90, height: 40, r: [5]}, style: {fill: '#eee', shadowBlur: 8, shadowOffsetX: 3, shadowOffsetY: 3, shadowColor: 'rgba(0,0,0,0.3)'}}
const elDownload = {id: 'download', info: {typeEl: 'download', title: title, countCharts: countMachines, chartId: chartId, type: type}, type: 'image', right: windowConfig?.bar?.graphic?.download?.right, top: windowConfig?.bar?.graphic?.download?.top, z: 999, style: {image: '/images/charts-download.svg', width: 17, height: 17}}
const elDownloadTitle = {id: 'downloadTitle', info: {typeEl: 'downloadTitle', title: title, countCharts: countMachines, chartId: chartId, type: type}, type: 'text', right: windowConfig?.bar?.graphic?.downloadTitle?.right, top: windowConfig?.bar?.graphic?.downloadTitle?.top, z: 999, style: {text: 'Скачать', fontSize: 12}}
elementsMenu.push(elRect)
elementsMenu.push(elDownload)
elementsMenu.push(elDownloadTitle)
const menuContainer = {id: 'menuContainer', type: 'group', bounding: 'raw', right: windowConfig?.bar?.graphic?.container?.right, top: windowConfig?.bar?.graphic?.container?.top, z: 9999, children: elementsMenu, style: {}}
isOpenMenu ? elements.push(menuContainer) : ''
return elements
}
const viewGraphicsTypesBar = (chartId, title, groupByKey, prevGroupByData, filterKey, prevGroupByFilters, type, groupByValue, period, countCharts, windowConfig, isOpenMenu = false) => {
const elements = []
const elementsMenu = []
const elPeriod = {id: 'period', name: 'Период загрузки данных', type: 'text', right: windowConfig?.bar?.graphic?.period?.right, top: windowConfig?.bar?.graphic?.period?.top, z: 999, style: {text: period, fill: '#777', fontSize: windowConfig?.pie?.graphic?.period?.fontSize}}
elements.push(elPeriod)
const elMenu = {id: 'menu', info: {typeEl: 'menu', chartId: chartId, groupByKey: groupByKey, filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'image', right: windowConfig?.bar?.graphic?.menu?.right, top: windowConfig?.bar?.graphic?.menu?.top, z: 999, style: {image: '/images/charts-more-vertical.svg', width: 17, height: 17}}
elements.push(elMenu)
const elRect = {id: 'rect', info: {typeEl: 'rect'}, type: 'rect', right: windowConfig?.bar?.graphic?.rect?.right, top: windowConfig?.bar?.graphic?.rect?.top, z: 999, shape: {width: 90, height: 40, r: [5]}, style: {fill: '#eee', shadowBlur: 8, shadowOffsetX: 3, shadowOffsetY: 3, shadowColor: 'rgba(0,0,0,0.3)'}}
const elDownload = {id: 'download', info: {typeEl: 'download', title: title, countCharts: countCharts, chartId: chartId, groupByKey: groupByKey, filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'image', right: windowConfig?.bar?.graphic?.download?.right, top: windowConfig?.bar?.graphic?.download?.top, z: 999, style: {image: '/images/charts-download.svg', width: 17, height: 17}}
const elDownloadTitle = {id: 'downloadTitle', info: {typeEl: 'downloadTitle', title: title, countCharts: countCharts, chartId: chartId, groupByKey: groupByKey, filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', right: windowConfig?.bar?.graphic?.downloadTitle?.right, top: windowConfig?.bar?.graphic?.downloadTitle?.top, z: 999, style: {text: 'Скачать', fontSize: 12}}
elementsMenu.push(elRect)
elementsMenu.push(elDownload)
elementsMenu.push(elDownloadTitle)
const menuContainer = {id: 'menuContainer', type: 'group', bounding: 'raw', right: windowConfig?.bar?.graphic?.container?.right, top: windowConfig?.bar?.graphic?.container?.top, z: 9999, children: elementsMenu, style: {}}
isOpenMenu ? elements.push(menuContainer) : ''
if(groupByKey !== groupByKeys.firstLayout && !isEqualKeyValue(prevGroupByData, groupByKeys.firstLayout, null)) {
const buttonPrevious = {id: `${filterKey}`, info: {typeEl: 'buttonPrevious', prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'image', right: windowConfig?.bar?.graphic?.back?.right, top: windowConfig?.bar?.graphic?.back?.top, z: 999, style: {image: '/images/arrow-go-back-line.svg', width: 17, height: 17}}
elements.push(buttonPrevious)
}
const elMain = {id: '', info: {typeEl: 'link', filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', left: windowConfig?.bar?.graphic?.link?.left, top: windowConfig?.bar?.graphic?.link?.top, z: 999, style: {text: 'Главная', fontSize: 10}}
const notFirstLayout = !Object.keys(prevGroupByFilters).includes(groupByKeys.firstLayout)
for (const groupByFilter in prevGroupByFilters) {
if (groupByFilter === groupByKeys.firstLayout) {
const linkTitle = `/ ${prevGroupByFilters[groupByKeys.firstLayout]}`
const leftPos = getTextWidth('Главная ', 10)
elMain.id = `0:link_${groupByFilter}-${linkTitle}`
elements.push(elMain)
const paramsFirst = {id: `1:link_${groupByFilter}-${linkTitle}`, info: {typeEl: 'link', filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', left: leftPos + windowConfig?.bar?.graphic?.link?.left, top: windowConfig?.bar?.graphic?.link?.top, z: 999, style: {text: linkTitle, fontSize: 10}}
elements.push(paramsFirst)
}
if (groupByFilter === groupByKeys.secondLayout) {
const linkTitle = ` / ${prevGroupByFilters[groupByKeys.secondLayout]}`
const leftPos = getTextWidth(notFirstLayout ? 'Главная ' : `Главная / ${prevGroupByFilters[groupByKeys.firstLayout]}`, 10)
if(notFirstLayout) {
elMain.id = `0:link_${groupByFilter}-${linkTitle}`
elements.push(elMain)
}
const paramsSecond = {id: `2:link_${groupByFilter}-${linkTitle}`, info: {typeEl: 'link', filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', left: leftPos + windowConfig?.bar?.graphic?.link?.left, top: windowConfig?.bar?.graphic?.link?.top, z: 999, style: {text: linkTitle, fontSize: 10}}
elements.push(paramsSecond)
}
if (groupByFilter === groupByKeys.thirdLayout) {
const linkTitle = ` / ${prevGroupByFilters[groupByKeys.thirdLayout]}`
const leftPos = getTextWidth(notFirstLayout ? `Главная / ${prevGroupByFilters[groupByKeys.secondLayout]} ` : `Главная / ${prevGroupByFilters[groupByKeys.firstLayout]} / ${prevGroupByFilters[groupByKeys.secondLayout]}`, 10)
const paramsThird = {id: `3:link_${groupByFilter}-${linkTitle}`, info: {typeEl: 'link', filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', left: leftPos + windowConfig?.bar?.graphic?.link?.left, top: windowConfig?.bar?.graphic?.link?.top, z: 999, style: {text: linkTitle, fontSize: 10}}
elements.push(paramsThird)
}
if (groupByFilter === groupByKeys.fourthLayout) {
const linkTitle = ` / ${prevGroupByFilters[groupByKeys.fourthLayout]}`
const leftPos = getTextWidth(notFirstLayout ? `Главная / ${prevGroupByFilters[groupByKeys.secondLayout]} / ${prevGroupByFilters[groupByKeys.thirdLayout]} ` : `Главная / ${prevGroupByFilters[groupByKeys.firstLayout]} / ${prevGroupByFilters[groupByKeys.secondLayout]} / ${prevGroupByFilters[groupByKeys.thirdLayout]}`, 10)
const paramsFourth = {id: `4:link_${groupByFilter}-${linkTitle}`, info: {typeEl: 'link', filterKey: filterKey, prevGroupByData: prevGroupByData, prevGroupByFilters: prevGroupByFilters, type: type, groupByValue: groupByValue}, type: 'text', left: leftPos + windowConfig?.bar?.graphic?.link?.left, top: windowConfig?.bar?.graphic?.link?.top, z: 999, style: {text: linkTitle, fontSize: 10}}
elements.push(paramsFourth)
}
}
return elements
}
export {viewGraphicsPieBar, viewGraphicsTypesBar}
@@ -0,0 +1,157 @@
import {groupByKeys} from './staticData'
const getTextWidth = (text, font) => {
const canvas = getTextWidth.canvas || (getTextWidth.canvas = document.createElement('canvas'));
const context = canvas.getContext('2d');
context.font = font;
const metrics = context.measureText(text);
return metrics.width;
}
const isEqualKeyValue = (obj, key, value) => {
if(Object.keys(obj).length > 0) {
if(Object.keys(obj).includes(key) && Object.values(obj).includes(value)) return true
else return false
} else return true
}
const chartsFiltersNames = (data) => [...new Set(data.map((el) => el.charts.map((item) => item.name)).flat())]
const chartsFiltersNamesMl = (data) => [...new Set(data.map((el) => el.map((item) => item.charts.name)).flat().filter(el => el !== undefined))]
const updateBarChartsData = (data, key) => data.map((el) => {
return el.filter((item) => {
if(item.name === key) {
return el
}
})
}).flat()
const dataFilterTitle = (data, title) => data.map((el) => {
return el.reduce((acc, item) => {
if(item.name === title || acc !== undefined && acc?.name === title) {
if(acc === 0) {
return {name: item.name, count: Number(item.value)}
}
if(acc?.count >= 0) {
return {name: item.name, count: acc.count + Number(item.value)}
}
}
}, 0)
}).filter((el) => el !== undefined)
const prepareBarTypesData = (data, xAxisData, params) => {
if(data.charts.length < chartsFiltersNames(xAxisData).length) {
// Если нет фильтруемых данных, то добавляем столбец графика с нулевым значением для каждого элемента el
const zeroData = chartsFiltersNames(xAxisData).filter((name) => !data.charts.some(({name: name2}) => name === name2)).map((name) => {
return {filterKey: params.filterKey, name: name, value: 0}
})
const withoutZeroData = data.charts.map((item) => {
item.type = params.type
item.filterKey = params.filterKey
item.groupByKey = params.groupByKey
item.groupByValue = params.groupByValue ? params.groupByValue : params.typeId // Для графика типа "types" params.typeId
item.groupByData = {[params.groupByKey]: data.yAxisData}
item.prevGroupByData = params.prevGroupByFilters
return item})
return [withoutZeroData, zeroData].flat()
} else {
return data.charts.map((item) => {
item.type = params.type
item.filterKey = params.filterKey
item.groupByKey = params.groupByKey
item.groupByValue = params.groupByValue ? params.groupByValue : params.typeId
item.groupByData = {[params.groupByKey]: data.yAxisData}
item.prevGroupByData = params.prevGroupByFilters
return item
})
}
}
const prepareBarTypesMlData = (data, params) => {
return data.map((item) => {
// console.log('item', item)
item.charts.type = params.type
item.charts.groupByKey = params.groupByKey
item.charts.groupByValue = params.groupByValue ? params.groupByValue : params.typeId
item.charts.groupByData = {[params.groupByKey]: item.yAxisData}
item.charts.prevGroupByData = params.prevGroupByFilters
return item.charts
})
}
const groupByBarTypesData = (data, xAxisData, func) => {
const dataGroupBy = []
const dataCustomTitle = []
for(let i = 0; i < (func(xAxisData).length); i++) {
dataGroupBy.push(updateBarChartsData(data, func(xAxisData)[i]))
dataCustomTitle.push(dataFilterTitle(dataGroupBy, func(xAxisData)[i]))
}
return {dataGroupBy: dataGroupBy, dataCustomTitle: dataCustomTitle}
}
const updatedGroupByData = (idx, prevGroupByFilters, groupByKey, groupByValue) => {
switch(idx) {
case '0': return {[groupByKey]: groupByValue}
case '1':
for (const prevGroupByFilter in prevGroupByFilters) {
if(prevGroupByFilter === groupByKeys.secondLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
if(prevGroupByFilter === groupByKeys.thirdLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
if(prevGroupByFilter === groupByKeys.fourthLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
}
return prevGroupByFilters
case '2':
for (const prevGroupByFilter in prevGroupByFilters) {
if(prevGroupByFilter === groupByKeys.thirdLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
if(prevGroupByFilter === groupByKeys.fourthLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
}
return prevGroupByFilters
case '3':
for (const prevGroupByFilter in prevGroupByFilters) {
if(prevGroupByFilter === groupByKeys.fourthLayout) {
delete prevGroupByFilters[prevGroupByFilter]
}
}
return prevGroupByFilters
}
}
const choiceTypeBarTypesCharts = (params) => {
switch(params.type) {
case 'ml':
const yAxisMlData = params.yAxisData.sort().map((el) => {
return `${el.key !== null ? el.key : 'Нет данных'} ${el.val}`
}).sort((a, b) => a - b).reverse()
const xAxisMlData = params.xAxisData.map((item) => item.map((el) => el))
const mlData = xAxisMlData.sort().map((el) => prepareBarTypesMlData(el, params))
return {axisData: yAxisMlData, updatedData: groupByBarTypesData(mlData, xAxisMlData, chartsFiltersNamesMl)}
case 'mount':
const xAxisMountData = params.yAxisData.sort((a, b) => a.key - b.key).map((el) => {
return `${el.key !== null ? el.key : 'Нет данных'} \n ${el.val}`})
const mountData = params.xAxisData.sort().map((el) => prepareBarTypesData(el, params.xAxisData, params))
return {axisData: xAxisMountData, updatedData: groupByBarTypesData(mountData, params.xAxisData, chartsFiltersNames)}
default :
const yAxisDefaultData = params.yAxisData.sort().map((el) => {
return `${el.key !== null ? el.key : 'Нет данных'} ${el.val}`
}).sort((a, b) => a - b).reverse()
const defaultData = params.xAxisData.sort().map((el) => prepareBarTypesData(el, params.xAxisData, params))
return {axisData: yAxisDefaultData, updatedData: groupByBarTypesData(defaultData, params.xAxisData, chartsFiltersNames)}
}
}
export {isEqualKeyValue, getTextWidth, updatedGroupByData, choiceTypeBarTypesCharts}
@@ -0,0 +1,906 @@
import * as echarts from "echarts"
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from 'echarts/components';
import { PieChart } from 'echarts/charts';
import { LabelLayout } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import {colorList, colorListBar, orderCharts, title, openMachine, groupByKeys, typesCharts} from './staticData'
import {viewGraphicsPieBar, viewGraphicsTypesBar} from "./graphicElements";
import {updatedGroupByData, choiceTypeBarTypesCharts} from './helpers'
import {axisBarConfig, windowConfig, sizeOptions} from './windowConfigs'
echarts.use([
TitleComponent,
TooltipComponent,
LegendComponent,
PieChart,
CanvasRenderer,
LabelLayout
])
const lineConfig = (params, id, windowConfig) => {
const times = params.xAxisData
const states = params.yAxisData
const series = [
{
data: states,
type: 'line'
}
]
const axisConfig = axisBarConfig(params.type, times, windowConfig, params.names)
return {
title: params.title,
titleLeft: 'center',
grid: {
left: '5%',
width: '90%',
top: 30,
containLabel: true
},
axisConfig,
series,
windowConfig,
graphic: [{id: 'yAxisTitle', type: 'text', left: 40, top: 20, z: 999, style: {text: '%', fontSize: 12}}]
}
}
const linesConfig = (params, id, windowConfig) => {
// console.log('params', params)
const times = params.xAxisData
const text = params.names.yAxis ? params.names.yAxis : 'Гб'
const namesLegend = params.charts.map((el) => el.name)
const series = params.charts.map((el) => {
return {
name: el.name,
data: el.value.map((item) => item.state).reverse(),
type: 'line'
}
})
const axisConfig = axisBarConfig(params.type, times, windowConfig, params.names)
return {
title: params.title,
titleLeft: 'center',
legend: {
textStyle: {
fontSize: windowConfig?.line?.legend?.fontSize
},
orient: 'horizontal',
// top: 360,
bottom: windowConfig?.line?.legend?.bottom,
// padding: windowConfig?.bar?.legend?.padding,
left: 'center',
itemGap: windowConfig?.line?.legend?.itemGap,
itemWidth: windowConfig?.line?.legend?.itemWidth,
itemHeight: windowConfig?.line?.legend?.itemHeight,
data: namesLegend
},
grid: {
left: '5%',
width: '90%',
top: 30,
containLabel: true
},
axisConfig,
series,
windowConfig,
graphic: [{id: 'yAxisTitle', type: 'text', left: 40, top: 20, z: 999, style: {text: text, fontSize: 12}}]
}
}
const linesPackConfig = (params, id, windowConfig) => {
console.log('params', params)
const times = params.xAxisData
const text = params.names.yAxis ? params.names.yAxis : ' '
const namesLegend = params.charts.map((el) => el.name)
const series = params.charts.map((el) => {
return {
name: el.name,
data: el.value.map((item) => item.state).reverse(),
type: 'line'
}
})
const axisConfig = axisBarConfig(params.type, times, windowConfig, params.names)
return {
title: params.title,
titleLeft: 'center',
legend: {
textStyle: {
fontSize: windowConfig?.line?.legend?.fontSize
},
orient: 'horizontal',
// top: 360,
bottom: windowConfig?.linesPack?.legend?.bottom,
// padding: windowConfig?.bar?.legend?.padding,
left: 'center',
itemGap: windowConfig?.linesPack?.legend?.itemGap,
itemWidth: windowConfig?.linesPack?.legend?.itemWidth,
itemHeight: windowConfig?.linesPack?.legend?.itemHeight,
data: namesLegend
},
grid: {
left: '3%',
width: '95%',
height: '50%',
top: 30,
containLabel: true
},
axisConfig,
series,
windowConfig,
graphic: [{id: 'yAxisTitle', type: 'text', left: 40, top: 20, z: 999, style: {text: text, fontSize: 12}}]
}
}
const pieConfig = (params, id, windowConfig) => {
const data = params.machines.map((el, idx) => {
el.color = colorList(el.name)
el.order = orderCharts(el.name)
return el
}).sort((a, b) => a.order - b.order)
const series = [{
name: '',
type: 'pie',
label: {
position: 'inner',
formatter: (d) => {
// console.log('d', d)
return d.value
},
color: '#FFF',
fontWeight: 'bold'
},
minAngle: 15,
center: ['50%', '50%'],
radius: ['40%', '70%'],
color: data.map((el) => el.color), //26.12.2023
itemStyle: {
borderRadius: 5,
borderColor: '#FFF',
borderWidth: 2
},
data: data,
emphasis: {
itemStyle: {
// color: {
// type: 'radial',
// x: 0.4,
// y: 0.3,
// r: 1,
// // colorStops: [
// // {
// // offset: 0,
// // // color: `rgb(251, 118, 123, 0.5)`,
// // opacity: 0.5
// // },
// // {
// // offset: 1,
// // // color: `rgb(204, 46, 72, 0.5)`
// // opacity: 0.5
// // }
// // ]
// },
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
opacity: 0.7
}
},
}]
return {
title: title(id, params.countMachines),
type: 'pie',
legend: {
formatter: (title) => {
const [elem] = params.machines.filter((el) => el.name == title) //26.12.2023
return title + ' ' + elem.value
},
textStyle: {
fontSize: windowConfig?.pie?.legend?.fontSize
},
icon: 'circle',
orient: 'horizontal',
bottom: 0,
left: 'center',
itemGap: windowConfig?.pie?.legend?.itemGap,
itemWidth: windowConfig?.pie?.legend?.itemWidth,
itemHeight: windowConfig?.pie?.legend?.itemHeight,
},
graphic: [{elements: viewGraphicsPieBar(id, params.period, params.countMachines, title(id, params.countMachines), windowConfig, params.isOpenMenu, params.type)}],
series,
windowConfig
}
}
const barConfig = (params, id, windowConfig) => {
// console.log('params', params)
const countMachinesWithoutMl = params?.machines?.[params?.machines?.length - 1]?.key === 'routes_without_ml' ? params?.machines?.[params?.machines?.length - 1]?.values : 0
const data = params?.machines?.map((el, idx) => {
el.itemStyle = {color: colorListBar(params.type, el.name)}
return el
}).sort((a, b) => a?.value - b?.value).reverse()
const xAxisData = data.map((el) => {
return `${el.name}`
})
const series = data?.map((chart, idx) => {
return {
name: `${chart?.name} ${chart?.value}`,
type: 'bar',
tooltip: {
formatter: value => `${value.marker} ${value.data.name !== null ? value.data.name : 'Нет данных'} ${value.data.value}`
},
label: {
show: true,
position: 'inside',
overflow: 'break',
color: '#FFF',
fontWeight: 'bold',
textborderColor: '#ff0000'
// formatter: '{c}'
},
labelLine: {
show: true,
lineStyle: {
// color: 'source',
color: 'black',
}
},
labelLayout:
(p) => {
if(p.labelRect.width >= p.rect.width) {
return {hideOverlap: true}
}
},
scaleLimit: {
min: 1,
max: 5
},
itemStyle: {
borderColor: chart?.itemStyle?.color,
color: chart?.itemStyle?.color,
// borderRadius: 5,
// borderWidth: 1
},
barMinWidth: 25,
barMinHeight: 20,
barMaxWidth: '90%',
barGap: '10%',
barCategoryGap: '30%',
groupPadding: 2,
data: data.map((el, index) => idx === index ? el : ''),
// data: data, // Для мульти данных, то есть больше чем один тип данных в столбце
stack: 'total',
sampling: 'average',
emphasis: {
itemStyle: {
shadowBlur: 5,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
opacity: 1
}
},
}
})
const axisConfig = axisBarConfig(params.type, xAxisData, windowConfig)
return {
// label: {
// show: true,
// position: 'inside'
// },
title: title(id, params.countMachines, params.countMachines_with_ml, params.countMachines_without_ml ? params.countMachines_without_ml : countMachinesWithoutMl),
backgroundColor: '#FFF',
legend: {
textStyle: {
fontSize: windowConfig?.bar?.legend?.fontSize
},
orient: 'horizontal',
// top: 360,
bottom: windowConfig?.bar?.legend?.bottom,
// padding: windowConfig?.bar?.legend?.padding,
left: 'center',
itemGap: windowConfig?.bar?.legend?.itemGap,
itemWidth: windowConfig?.bar?.legend?.itemWidth,
itemHeight: windowConfig?.bar?.legend?.itemHeight,
},
grid: {
left: '5%',
width: '90%',
top: 50,
containLabel: true
},
axisConfig,
graphic: [{elements: viewGraphicsPieBar(id, params.period, params.countMachines, title(id, params.countMachinesWithMl, countMachinesWithoutMl), windowConfig, params.isOpenMenu, params.type)}],
series,
windowConfig
}
}
const barTypesConfig = (params, id, windowConfig, enableDataZoom) => {
console.log('params', params)
const isMlType = params.type === typesCharts.typesMl
const isMountType = params.type === 'mount'
const dataWithoutZero = choiceTypeBarTypesCharts(params).updatedData.dataGroupBy.map((el) => {
const result = el.map((item) => {
if(item.value === 0) {
item.value = ''
return item
}
if(item.name === null) {
item.name = 'Нет данных'
return item
}
return item
}).sort((a, b) => {
if(a.groupByKey && b.groupByKey) {
(a.groupByData[a.groupByKey] - b.groupByData[b.groupByKey])
}})
return !isMountType ? result.reverse() : result
})
// console.log('dataWithoutZero', dataWithoutZero)
const series = dataWithoutZero.map((charts, idx) => {
const [title] = choiceTypeBarTypesCharts(params).updatedData.dataCustomTitle.flat().filter((el) => el?.name == charts[0]?.name)
return {
name: `${title?.name} ${title?.count} id:${id}`,
type: 'bar',
tooltip: {
formatter: value => `${value.marker} ${value?.data?.name !== null ? value?.data?.name : 'Нет данных'} ${value?.data?.value}`
},
label: {
show: true,
position: 'inside',
overflow: 'break',
color: '#FFF',
fontWeight: 'bold',
textborderColor: '#ff0000'
// formatter: '{c}'
},
labelLine: {
show: true,
lineStyle: {
// color: 'source',
color: 'black',
}
},
labelLayout: (p) => p.labelRect.width <= p.rect.width ? {hideOverlap: true} : {hideOverlap: false},
// return {x: initCharts.getWidth() - 50, moveOverlap: 'shiftY'}
// return {dx: 50, dy: -100}
scaleLimit: {
min: 1,
max: 5
},
itemStyle: {
borderColor: isMlType ? colorListBar(params.type, charts[0]?.name) : colorList(charts[0]?.name),
color: isMlType ? colorListBar(params.type, charts[0]?.name) : colorList(charts[0]?.name),
// borderRadius: 5,
// borderWidth: 1
},
// barWidth: 30,
// barMinWidth: 25,
barMaxWidth: 35,
barMinHeight: 30,
barGap: '20%',
barCategoryGap: '10%',
groupPadding: 2,
// data: data.map((el, index) => idx === index ? el : ''), // Для одного типа данных в столбце
data: charts,
stack: 'total',
sampling: 'average',
large: true,
largeThreshold: 100,
progressiveThreshold: 1000,
emphasis: {
itemStyle: {
shadowBlur: 5,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
opacity: 1
}
},
}
})
const axisConfig = axisBarConfig(params.type, choiceTypeBarTypesCharts(params).axisData, windowConfig)
return {
// label: {
// show: true,
// position: 'inside'
// },
title: isMountType? title(id, params.countMounts) : title(id, params.countMachines, params.countMachinesWithMl, params.countMachinesWithoutMl),
legend: {
// icon: 'path://M224 320c8.823 0 16 7.177 16 16s-7.177 16-16 16-16-7.177-16-16 7.177-16 16-16m0-32c-26.51 0-48 21.49-48 48s21.49 48 48 48 48-21.49 48-48-21.49-48-48-48zM320 0H128C64 0 0 42.981 0 96v256c0 47.169 50.656 86.391 106.9 94.473l-55.285 55.285c-3.78 3.78-1.103 10.243 4.243 10.243h25.798c3.183 0 6.235-1.264 8.485-3.515L150.627 448h146.745l60.486 60.485a12.002 12.002 0 0 0 8.485 3.515h25.798c5.345 0 8.022-6.463 4.243-10.243L341.1 446.472C397.344 438.391 448 399.169 448 352V96c0-53.019-63-96-128-96zM32 128h384v96H32v-96zm96-96h192c58.237 0 96 37.881 96 64H32c0-32.299 47.552-64 96-64zm192 384H128c-48.448 0-96-31.701-96-64v-96h384v96c0 32.299-47.552 64-96 64z',
// backgroundColor: '#ff0000',
orient: 'horizontal',
textStyle: {
fontSize: windowConfig?.bar?.legend?.fontSize
},
zLevel: 1,
// top: 35,
left: 'center',
bottom: windowConfig?.bar?.legend?.bottom,
itemGap: windowConfig?.bar?.legend?.itemGap,
itemWidth: windowConfig?.bar?.legend?.itemWidth,
itemHeight: windowConfig?.bar?.legend?.itemHeight,
formatter: (e) => {
return e.slice(0, e.indexOf('id') - 1)
}
// data: legendData
},
grid: {
left: '1%',
top: windowConfig?.bar?.grid?.top,
width: windowConfig?.bar?.grid?.width,
containLabel: true,
height: isMountType ? '68%' : 'auto'
},
dataZoom: [
{
type: 'slider',
show: true,
width: 14,
top: windowConfig?.bar?.dataZoom?.top,
right: '3%',
yAxisIndex: 0,
realtime: true,
start: windowConfig?.bar?.dataZoom?.start,
end: 100,
rangeMode: ['value', 'percent'],
zoomLock: true,
brushSelect: false,
minSpan: windowConfig?.bar?.dataZoom?.minSpan,
handleSize: 18,
showDetail: false,
handleIcon: "path://M12 0c-9.432 0-12 2.568-12 12s2.551 12 12 12 12-2.551 12-12-2.568-12-12-12z",
handleStyle: {
color: '#CCCCCC',
borderColor: '#CCCCCC'
}
},
{
type: 'inside',
yAxisIndex: 0,
start: 70,
end: 100,
zoomOnMouseWheel: false,
moveOnMouseWheel: true,
handleSize: 18
},
],
graphic: isMountType ? [{elements: viewGraphicsPieBar(id, '', params.countMounts, title(id), windowConfig, params.isOpenMenu, params.type)}] :
[{elements: viewGraphicsTypesBar(id, title(id, isMlType ? params.countMachinesWithMl : params.countMachines, params.countMachinesWithoutMl), params.groupByKey, params.prevGroupByData, params.filterKey, params.prevGroupByFilters, params.type, params.groupByValue ? params.groupByValue : params.typeId ? params.typeId : null, params.period, params.yAxisData.length, windowConfig, params.isOpenMenu)}],
series,
axisConfig,
windowConfig,
enableDataZoom
}
}
const config = (params) => {
// console.log('params', params)
return {
title: {
text: params.title,
subtextStyle: {
fontSize: 11,
align: 'left'
},
top: params.windowConfig?.title?.top,
left: params.titleLeft ? params.titleLeft : params.windowConfig?.title?.left,
textStyle: {
width: params.windowConfig?.title?.width,
fontSize: params.windowConfig?.title?.fontSize,
overflow: 'break',
},
},
backgroundColor: params.windowConfig?.backgroundColor,
tooltip: {trigger: 'item'},
legend: params.legend,
xAxis: params.axisConfig?.xAxis ? params.axisConfig?.xAxis : '',
yAxis: params.axisConfig?.yAxis ? params.axisConfig?.yAxis : '',
dataZoom: params.enableDataZoom && params.dataZoom ? params.dataZoom : '',
toolbox: {
show: true,
feature: {
mark: {show: true},
// dataView: {show: true, readOnly: false, title: ''},
// magicType: {show: true, type: ['line', 'bar'], title: {line: 'Переключить на линейный график', bar: 'Переключить на график гистограмму'} },
// restore: {show: true, title: 'Обновить'},
// saveAsImage: {show: true, title: ''}
},
iconStyle: {
borderColor: '#333',
// borderWidth: 0,
// borderType: 'solid'
},
emphasis: {iconStyle: {
borderColor: 'rgba(1, 97, 253, 0.3)',
}},
top: 16,
right: 20,
},
graphic: params.graphic ? params.graphic : '',
grid: params.grid ? params.grid : '',
series: params.series
}
}
class ECharts {
constructor(id, sizes = {}) {
this.id = id
this.el = document.getElementById(id)
const clientWidth = window.innerWidth
const clientHeight = window.innerHeight
this.isMobile = clientWidth < 768
this.currentWidth = clientWidth - 100
// const currentheight = clientHeight + 70
this.widthChart = sizes.width ? sizes.width : 450
this.heightChart = sizes.height ? sizes.height : 350
this.mountHeightChart = 350
this.charts = []
this.legendFilterData = []
this.initCharts = echarts.init(this.el, null, {renderer: 'canvas'})
this.initCharts.showLoading()
// window.addEventListener('resize', function() {
// this.initCharts.resize()
// })
console.log('sizes', sizes)
this.getRes = this.el.getAttribute("get-res") == "true"
}
getChart(id) {
if (this.getRes) {
return {isGetChart: true, id}
}
}
makeLineCharts(charts) {
charts.chartId = this.id
this.charts = []
this.charts.push(charts)
this.initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, charts.height ? charts.height : this.heightChart))
this.initCharts.setOption(config(lineConfig(charts, this.id, windowConfig(this.isMobile, this.currentWidth))), {
replaceMerge: ["series", "yAxis", "xAxis", "grid"],
})
this.initCharts.hideLoading()
}
makeLinesCharts(charts) {
console.log('charts', charts)
charts.chartId = this.id,
this.charts = []
this.charts.push(charts)
if(this.initCharts) {
this.initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, charts.height ? charts.height : this.heightChart))
this.initCharts.setOption(config(linesConfig(this.charts[0], this.id, windowConfig(this.isMobile, this.currentWidth))), {
replaceMerge: ["series", "yAxis", "xAxis", "grid"],
})
}
this.initCharts.hideLoading()
}
makeLinesPackCharts(charts) {
console.log('charts', charts)
charts.chartId = this.id,
this.charts = []
this.charts.push(charts)
if(this.initCharts) {
// this.initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, charts.height ? charts.height : this.heightChart))
this.initCharts.setOption(config(linesPackConfig(this.charts[0], this.id, windowConfig(this.isMobile, this.currentWidth))), {
replaceMerge: ["series", "yAxis", "xAxis", "grid"],
})
}
this.initCharts.hideLoading()
}
updateLineCharts(charts) {
if(this.charts[0].xAxisData[this.charts[0].xAxisData.length - 1] !== charts.xAxisData[charts.xAxisData.length - 1]) {
this.charts[0].xAxisData.shift()
this.charts[0].xAxisData.push(charts.xAxisData[charts.xAxisData.length - 1])
this.charts[0].yAxisData.shift()
this.charts[0].yAxisData.push(charts.yAxisData[charts.yAxisData.length - 1])
if(this.initCharts) {
this.initCharts.setOption({xAxis: [{data: this.charts[0].xAxisData}], series: [{data: this.charts[0].yAxisData}]}, false)
}
}
}
updateLinesCharts(charts) {
// console.log('charts', charts)
if(this.charts[0].xAxisData[this.charts[0].xAxisData.length - 1] !== charts.xAxisData[charts.xAxisData.length - 1]) {
this.charts[0].charts.forEach((el, idx) => {
el.value.pop()
el.value.unshift(charts.charts[idx].value[0])
})
this.charts[0].xAxisData.shift()
this.charts[0].xAxisData.push(charts.xAxisData[charts.xAxisData.length - 1])
if(this.initCharts) {
this.initCharts.setOption({xAxis: [{data: this.charts[0].xAxisData}],series: [{data: this.charts[0].charts}]}, false)
}
}
}
makePieCharts(charts) {
charts.chartId = this.id
this.charts = []
this.charts.push(charts)
const initCharts = echarts.init(this.el, null, sizeOptions(this.widthChart, this.heightChart), {renderer: 'canvas'})
initCharts.hideLoading()
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, this.heightChart))
initCharts.setOption(config(pieConfig(charts, this.id, windowConfig(this.isMobile, this.currentWidth))), true)
}
makeBarCharts(charts) {
charts.chartId = this.el.id
this.charts = []
this.charts.push(charts)
const initCharts = echarts.init(document.getElementById(`${this.el.id}`), null, {renderer: 'canvas'})
initCharts.hideLoading()
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, this.heightChart))
initCharts.setOption(config(barConfig(charts, this.el.id, windowConfig(this.isMobile, this.currentWidth))), true)
}
makeBarMountCharts(charts) {
// console.log('charts', charts)
charts.chartId = this.el.id
this.charts = []
this.charts.push(charts)
this.mountHeightChart = charts.height
const initCharts = echarts.init(document.getElementById(`${this.el.id}`), null, {renderer: 'canvas'})
initCharts.hideLoading()
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, charts.height))
initCharts.setOption(config(barTypesConfig(charts, this.el.id, windowConfig(this.isMobile, this.currentWidth))), true)
}
makeBarTypesCharts(charts) {
charts.chartId = this.el.id
this.charts = []
this.charts.push(charts)
console.log('charts - makeBarTypesCharts', charts.legendFilterData)
const initCharts = echarts.init(document.getElementById(`${this.el.id}`), null, {renderer: 'canvas'})
// this.heightChart = 270 + (charts?.yAxisData?.length * 27)
const countCharts = charts?.yAxisData?.length
const enableDataZoom = (countCharts * 35) + 200 > this.heightChart
initCharts.hideLoading()
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, this.heightChart))
initCharts.setOption(config(barTypesConfig(charts, this.el.id, windowConfig(this.isMobile, this.currentWidth, countCharts), enableDataZoom)), true)
let currentLegendCount = 0
if(charts.legendFilterData?.length > 0) {
charts.legendFilterData?.map((legend) => {
if(legend.id === this.el.id) {
if(this.el.id !== 'routes_types_bar_ml') {
charts.xAxisData.map((chart) => {
chart.charts.reduce((acc, el) => {
if(el.name === legend.filterData.name) {
currentLegendCount += Number(el.value)
} else {
return acc
}
}, 0)
})
} else {
charts.xAxisData.map((chart) => {
chart.reduce((acc, el) => {
if(el.charts.name === legend.filterData.name) {
currentLegendCount += Number(el.charts.value)
} else {
return acc
}
}, 0)
})
}
initCharts.dispatchAction({
type: !legend.filterData.value ? 'legendUnSelect' : 'legendSelect',
name: `${legend.filterData.name} ${currentLegendCount} id:${legend.id}`
})
}
currentLegendCount = 0
})
}
}
setMenuChart (data) {
if (data.groupByInfo.chartId) {
this.charts = [data.groupByInfo.data]
const [chart] = this.charts
chart.chartId = data.groupByInfo.chartId
chart.type = data.groupByInfo.type
chart.isOpenMenu = data.openMenuChart
const initCharts = echarts.init(document.getElementById(`${data.groupByInfo.chartId}`), null, {renderer: 'canvas'})
const countCharts = chart?.yAxisData?.length
const enableDataZoom = (chart?.yAxisData?.length * 35) + 200 > this.heightChart
initCharts.hideLoading()
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, data.groupByInfo.chartId.includes('mount') ? chart.height : this.heightChart))
if(data.groupByInfo.chartId.includes('pie')) {
initCharts.setOption(config(pieConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
} else if (data.groupByInfo.chartId.includes('types_bar')) {
initCharts.setOption(config(barTypesConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth, countCharts), enableDataZoom)), true)
} else if (data.groupByInfo.chartId.includes('mount')) {
initCharts.setOption(config(barTypesConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
} else {
initCharts.setOption(config(barConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
}
}
}
downloadChart (data, updatedData) {
if (data.groupByInfo.chartId) {
this.charts = [data.groupByInfo.data]
const [chart] = this.charts
const countCharts = chart?.yAxisData?.length
const initCharts = echarts.init(document.getElementById(`${data.groupByInfo.chartId}`), null, {renderer: 'canvas'})
const enableDataZoom = (countCharts * 35) + 200 > data.updatedHeight
initCharts.hideLoading()
if(data.groupByInfo.chartId.includes('pie')) {
initCharts.setOption(config(pieConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
this.downloadImage(data.groupByInfo.title, this.prepareImage(initCharts))
} else if (data.groupByInfo.chartId.includes('types_bar')) {
console.log('this.charts', this.charts)
initCharts.resize(sizeOptions(this.isMobile ? this.currentWidth : this.widthChart, data.groupByInfo.updatedHeight))
initCharts.setOption(config(barTypesConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, data.groupByInfo.updatedHeight, countCharts), enableDataZoom)), true)
initCharts.on('finished', () => {
if(data.groupByInfo.isDownloadChart) {
console.log('data', data)
this.downloadImage(data.groupByInfo.title, this.prepareImage(initCharts))
data.groupByInfo.isDownloadChart = false
updatedData({action: 'downloadChart', groupByInfo: {...data.groupByInfo, updatedHeight: this.heightChart, data: this.charts}})
}
})
} else if (data.groupByInfo.chartId.includes('mount')) {
initCharts.setOption(config(barTypesConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
this.downloadImage(data.groupByInfo.title, this.prepareImage(initCharts))
} else {
initCharts.setOption(config(barConfig(chart, data.groupByInfo.chartId, windowConfig(this.isMobile, this.currentWidth))), true)
console.log('data.groupByInfo.title', data.groupByInfo.title)
this.downloadImage(data.groupByInfo.title, this.prepareImage(initCharts))
}
}
}
prepareImage(initCharts) {
const img = new Image()
img.src = initCharts.getDataURL({type: 'canvas'})
const myHeaders = new Headers()
const myRequest = new Request(img.src, {
method: "GET",
headers: myHeaders,
mode: "cors",
cache: "default",
})
return myRequest
}
async downloadImage(name, myRequest) {
try {
const response = await fetch(myRequest)
if (!response.ok) {
throw new Error("Network response was not OK")
}
const myBlob = await response.blob()
const imageURL = URL.createObjectURL(myBlob)
const link = document.createElement('a')
link.href = imageURL
if (link.href) {
const openedImage = window.open(link.href, '_blank')
console.log('openedImage', openedImage)
const linkImg = openedImage.document.createElement('a')
linkImg.href = link.href
linkImg.download = name
openedImage.document.body.appendChild(linkImg)
linkImg.click()
openedImage.document.body.removeChild(linkImg)
}
}
catch (error) {
console.error("Error:", error)
}
}
updateLegendFiltersData(params) {
const [chart] = this.charts
console.log('params - updateLegendFilterData', params)
console.log('chart - updateLegendFilterData', chart)
if (params.id === chart.chartId) {
if(!params.filterData.value) {
this.legendFilterData.push(params)
return this.legendFilterData
} else {
const updatedFiltersData = this.legendFilterData.filter((legend) => legend.filterData.name !== params.filterData.name)
this.legendFilterData = updatedFiltersData
return updatedFiltersData
}
}
}
onClickedBarCharts(updatedData) {
this.initCharts.on('click', (e) => {
// console.log('e', e)
const filterKey = e.data?.filterKey ? e.data?.filterKey : e.data?.filterKey
if(filterKey && e.componentType === 'series' && e.data?.type !== 'mount') {
// console.log('this.charts - nextLayoutChart', this.charts)
updatedData({id: this.id, action: 'nextLayoutChart', data: this.charts, prevGroupByData: e.data.groupByData, prevGroupByFilters: e.data.prevGroupByData, filterKey: filterKey, groupByKey: e.data.groupByKey, groupByValue: e.data.groupByValue ? e.data.groupByValue : null, type: e.data.type})
}
if(e.event?.target?.id && e.info?.typeEl === 'buttonPrevious' && e.componentType === 'graphic') {
// console.log('beforeLayoutChart')
// console.log('e.info', e.info)
updatedData({id: this.el.id, action: 'beforeLayoutChart', filterKey: e.event.target.id, groupByInfo: e.info})
}
if(e.event?.target?.id && e.info?.typeEl === 'menu' && e.componentType === 'graphic') {
e.info.mountHeightChart = this.mountHeightChart
// console.log('this.charts - menu', this.charts)
updatedData({action: 'openMenuChart', groupByInfo: {...e.info, data: this.charts}})
}
if(e.event?.target?.id && (e.info?.typeEl === 'download' || e.info?.typeEl === 'downloadTitle') && e.componentType === 'graphic') {
const fullHeight = e.info.countCharts * 35 + 200
e.info.isDownloadChart = true
updatedData({action: 'downloadChart', groupByInfo: {...e.info, updatedHeight: fullHeight, data: this.charts}})
}
if(e.event?.target?.id && e.info?.typeEl === 'link' && e.componentType === 'graphic') {
const id = e.event?.target?.parent?.id
const idx = id.slice(0, id.indexOf(':'))
const layoutGroupKey = id.slice((id.indexOf('_') + 1), id.indexOf('-')).trim()
const layoutGroupValue = id.slice(id.indexOf('-') + (id.includes('/') ? 3 : 2)).trim()
const isCurrentLayout = Object.values(e.info?.prevGroupByData)[0] === layoutGroupValue
if (isCurrentLayout && Object.keys(e.info.prevGroupByFilters).length > 1) { // Для выхода с первого вложенного слоя группировки
return false
} else {
// console.log('e.info', e.info)
const updatedPrevGroupByFilters = updatedGroupByData(idx, e.info.prevGroupByFilters, layoutGroupKey, layoutGroupValue)
e.info.prevGroupByFilters = updatedPrevGroupByFilters
e.info.prevGroupByData = {[layoutGroupKey]: layoutGroupValue}
updatedData({id: this.el.id, action: 'beforeLayoutChart', filterKey: e.info.filterKey ? e.info.filterKey : null, groupByInfo: e.info})
}
}
if(e.data?.groupByKey === groupByKeys.fiveLayout) {
openMachine(e.data)
}
})
this.initCharts.on('legendselectchanged', (params) => {
if(params.name.indexOf('id:') > 0) {
// Берём все данные производные из имени, так как это активный элемент легенды
const id = params.name.slice((params.name.indexOf('id:') + 3))
const nameIdxEnd = (params.name.indexOf('id:') - 1)
const namePrepare = params.name.slice(0, nameIdxEnd)
const name = namePrepare.slice(0, namePrepare.lastIndexOf(' '))
const updatedLegendFiltersData = this.updateLegendFiltersData({id: this.el.id, filterData: {name: name, value: params.selected[params.name]}})
updatedData({action: 'legendFiltersParams', data: updatedLegendFiltersData})
}
})
}
}
export default ECharts;
@@ -0,0 +1,118 @@
const chartsSetup = {
main: [
{id: 'commun_pie', type: 'pie', maxHeight: '400px', order: 1},
{id: 'commun_pie_cdim', type: 'pie', maxHeight: '400px', order: 2},
{id: 'commun_pie_cdrp', type: 'pie', maxHeight: '400px', order: 3},
{id: 'commun_pie_mpt_types', type: 'pie', maxHeight: '400px', order: 4},
{id: 'commun_pie_gdsm_types', type: 'pie', maxHeight: '400px', order: 5},
{id: 'commun_pie_sm_pss_types', type: 'pie', maxHeight: '400px', order: 6},
{id: 'routes_pie', type: 'pie', maxHeight: '400px', order: 7},
{id: 'rors_pie', type: 'pie', maxHeight: '400px', order: 8},
{id: 'routes_bar', type: 'mainMl', maxHeight: '400px', order: 9},
{id: 'mount_client_type_bar', type: 'mount', height: '350', maxHeight: '400px', order: 10},
{id: 'mount_mounter_type_bar', type: 'mount', height: '500', maxHeight: '600px', order: 11},
],
types: [
{id: 'commun_types_bar', type: 'default', maxHeight: '400px', order: 1},
{id: 'commun_types_bar_cdim', type: 'without_first_layout', maxHeight: '400px', order: 2},
{id: 'commun_types_bar_cdrp', type: 'without_first_layout', maxHeight: '400px', order: 3},
{id: 'commun_types_bar_mpt_types', type: 'types', maxHeight: '400px', order: 4},
{id: 'commun_types_bar_gdsm_types', type: 'types', maxHeight: '400px', order: 5},
{id: 'commun_types_bar_sm_pss_types', type: 'types', maxHeight: '400px', order: 6},
{id: 'routes_types_bar', type: 'default', maxHeight: '400px', order: 7},
{id: 'rors_types_bar', type: 'default', maxHeight: '400px', order: 8},
{id: 'routes_types_bar_ml', type: 'ml', maxHeight: '380px', order: 9},
]
}
const colorList = (name) => {
switch(name) {
case 'На связи': return 'rgb(134, 212, 131, 1)'
case 'Есть МЛ, выходил на связь': return 'rgb(134, 212, 131, 1)'
case 'РОРС': return 'rgb(134, 212, 131, 1)'
case 'Не на связи': return 'rgb(255, 114, 114, 1)'
case 'Нет МЛ, нет связи': return 'rgb(165, 165, 165, 1)'
case 'Не РОРС': return 'rgb(255, 114, 114, 1)'
case 'Нет данных': return 'rgb(165, 165, 165, 1)'
case 'Есть МЛ, нет связи': return 'rgb(255, 114, 114, 1)'
case 'Нет МЛ, выходил на связь': return 'rgb(243, 161, 30, 1)'
}
}
const colorListBar = (type, name) => {
if(type === typesCharts.mainMl || typesCharts.typesMl) {
switch(name) {
case 'Обычные': return 'rgb(134, 212, 131, 1)'
case 'Аварийные': return 'rgb(255, 114, 114, 1)'
case 'Прочие': return 'rgb(196, 161, 201, 1)'
case 'Ремонтные': return 'rgb(228, 202, 163, 1)'
case 'Без выезда': return 'rgb(243, 161, 30, 1)'
case 'СПС без МЛ': return 'rgb(165, 165, 165, 1)'
}
}
}
const orderCharts = (name) => {
switch(name) {
case 'На связи': return 1
case 'Есть МЛ, выходил на связь': return 1
case 'РОРС': return 1
case 'Не на связи': return 2
case 'Не РОРС': return 2
case 'Есть МЛ, нет связи': return 2
case 'Нет МЛ, выходил на связь': return 3
case 'Нет МЛ, нет связи': return 5
case 'Нет данных': return 5
}
}
const title = (id, count, countWithoutMl = '') => {
switch(id) {
case 'commun_pie': return `Связь с СПС`
case 'commun_pie_cdim': return `Связь с СПС, ЦДИМ`
case 'commun_pie_cdrp': return `Связь с СПС, ЦДРП`
case 'commun_types_bar': return `Связь с СПС`
case 'commun_types_bar_cdim': return `Связь с СПС, ЦДИМ`
case 'commun_types_bar_cdrp': return `Связь с СПС, ЦДРП`
case 'commun_pie_mpt_types': return `Наличие связи с МРТ`
case 'commun_pie_gdsm_types': return `Наличие связи с ЖДСМ`
case 'commun_pie_sm_pss_types': return `Наличие связи с СМ`
case 'commun_types_bar_mpt_types': return `Наличие связи с МРТ`
case 'commun_types_bar_gdsm_types': return `Наличие связи с ЖДСМ`
case 'commun_types_bar_sm_pss_types': return `Наличие связи с СМ`
case 'rors_pie': return `РОРС`
case 'rors_types_bar': return `РОРС`
case 'routes_pie': return `Связь с СПС, МЛ`
case 'routes_types_bar': return `Связь с СПС, МЛ`
case 'routes_bar': return `Выданные МЛ`
case 'routes_types_bar_ml': return `Выданные МЛ`
case 'mount_client_type_bar': return `Кол-во монтажей по заказчикам и КР`
case 'mount_mounter_type_bar': return `Кол-во монтажей по исполнителям и КР`
default: return ''
}
}
const openMachine = (data) => {
const [currentEl] = data?.linkData?.filter((el) => el?.machine_type === data?.groupByData[data?.groupByKey])
window.open(`/html/askr_devices/analyze_device/${currentEl.device_number}`, '_blank')
}
const groupByKeys = {
firstLayout: 'org_type_name',
secondLayout: 'railway_name',
thirdLayout: 'org_name',
fourthLayout: 'type',
fiveLayout: 'machine_type',
}
const typesCharts = {
default: 'default',
without_first_layout: 'without_first_layout',
mpt: 'mpt_types',
gdsm: 'gdsm_types',
sm: 'sm_pss_types',
mainMl: 'mainMl',
typesMl: 'ml',
}
export {colorList, colorListBar, orderCharts, title, openMachine, groupByKeys, typesCharts, chartsSetup}
@@ -0,0 +1,381 @@
const axisBarConfig = (typeChart, axisData, windowConfig, names = {}) => {
return typeChart === 'mount' || typeChart === 'mainMl' || typeChart === 'states' || typeChart === 'packLines' ? {
xAxis: {
type: 'category',
name: names.xAxis ? names.xAxis : '',
nameLocation: 'end',
nameGap: 35,
nameTextStyle: {
verticalAlign: 'top',
align: 'right',
padding: typeChart === 'packLines' ? windowConfig?.linesPack.nameXAxis.padding : [25, 40, 0, 0]
},
splitLine: {show: true},
axisLabel: {
show: true,
color: 'black',
fontSize: windowConfig?.bar?.xAxis?.label?.fontSize,
fontWeight: windowConfig?.bar?.xAxis?.label?.fontWeight,
interval: windowConfig?.bar?.xAxis?.label?.interval,
align: windowConfig?.bar?.xAxis?.label?.align,
hideOverlap: true,
},
axisTick: {
show: true,
interval: windowConfig?.bar?.xAxis?.axisTick?.interval,
lineStyle: {
color: 'black',
}
},
data: axisData
},
yAxis: {
type: 'value',
name: names.xAxis ? names.xAxis : '',
// nameLocation: 'end',
// nameGap: 35,
// nameTextStyle: {
// // verticalAlign: 'top',
// // align: 'right',
// padding: [125, 0, 0, 150]
// },
splitLine: {show: true},
axisLine: {
show: true,
lineStyle: {
// dashOffset: 10,
width: 1,
color: 'white',
shadowColor: 'black',
shadowOffsetX: -2
},
},
axisLabel: {
show: true,
color: 'black',
},
axisTick: {
show: true,
lineStyle: {
color: 'black'
}
},
},
} : {
xAxis: {
type: 'value',
name: names.xAxis ? names.xAxis : '',
splitLine: {show: true},
axisLabel: {
show: true,
color: 'black',
fontSize: windowConfig?.bar?.xAxis?.label?.fontSize,
fontWeight: windowConfig?.bar?.xAxis?.label?.fontWeight,
interval: windowConfig?.bar?.xAxis?.label?.interval,
align: windowConfig?.bar?.xAxis?.label?.align,
hideOverlap: true,
},
axisLine: {
show: true,
lineStyle: {
color: 'black',
}
},
axisTick: {
show: true,
interval: windowConfig?.bar?.xAxis?.axisTick?.interval,
lineStyle: {
color: 'black'
}
},
minInterval: 1,
},
yAxis: {
type: 'category',
name: names.yAxis ? names.yAxis : '',
axisLine: {
show: true,
lineStyle: {
// dashOffset: 10,
width: 1,
color: 'white',
shadowColor: 'black',
shadowOffsetX: -2
},
},
// axisLabel: {
// formatter: (yAxisData) => {
// // return yAxisData.map((el) => {
// // return `${el?.name} ${el?.value}`
// // })
// }
// },
axisLabel: {
show: true,
width: windowConfig?.bar?.yAxis?.label?.width,
color: 'black',
fontSize: windowConfig?.bar?.yAxis?.label?.fontSize,
fontWeight: windowConfig?.bar?.yAxis?.label?.fontWeight,
overflow: 'break',
},
axisTick: {
show: true,
lineStyle: {
color: 'black'
}
},
// fontSize: 10,
// offset: -7,
data: axisData,
splitLine: {
show: true
},
// position: windowConfig?.bar?.yAxis?.label?.position,
// offset: windowConfig?.bar?.yAxis?.label?.offset,
zLevel: 3,
// z: 99
},
}
}
const windowConfig = (isMobile, currentWidth, countCharts) => {
const scrollbarParams = (countCharts) => {
if(15 < countCharts && countCharts < 40) return {
start: 85, minSpan: 5
}
if(40 < countCharts && countCharts < 70) return {
start: 90, minSpan: 5
}
if(70 < countCharts && countCharts < 100) return {
start: 95, minSpan: 5
}
if(100 < countCharts) return {
start: 99, minSpan: 3
}
return {start: 70, minSpan: 5}
}
if(isMobile) return {
title: {
width: currentWidth - 90,
top: 3,
left: 'left',
fontSize: 11,
},
backgroundColor: '#FFF',
pie: {
legend: {
itemWidth: 15,
itemHeight: 15,
itemGap: 5,
fontSize: 10
},
graphic: {
period: {
top: 10,
right: 40,
fontSize: 10
},
count: {
top: 'center',
left: 'center',
fontSize: 20
},
}
},
linesPack: {
legend: {
bottom: 0,
itemWidth: 25,
itemHeight: 14,
itemGap: 7,
fontSize: 11
},
nameXAxis: {
padding: [110, 40, 0, 0],
}
},
bar: {
legend: {
bottom: 0,
itemWidth: 10,
itemHeight: 10,
itemGap: 5,
fontSize: 10
},
grid: {
width: '85%',
top: 40
},
yAxis: {
label: {width: 40, fontWeight: 'bold', fontSize: 9, position: 'left', offset: -20}
},
xAxis: {
label: {fontSize: 10, fontWeight: 'bold', interval: 1, align: 'center'},
axisTick: {interval: 1}
},
dataZoom: {
top: 40,
start: scrollbarParams(countCharts).start,
minSpan: scrollbarParams(countCharts).minSpan,
},
graphic: {
period: {
top: 8,
right: 50,
fontSize: 10
},
menu: {
top: 5,
right: 0
},
container: {
top: 27,
right: 0
},
rect: {
top: 8,
right: 10
},
download: {
top: 18,
right: 70
},
downloadTitle: {
top: 23,
right: 20
},
back: {
top: 5,
right: 20,
},
link: {
top: 23,
left: 7,
}
}
}
}
if(!isMobile) return {
title: {
width: 'auto',
top: 3,
left: 'left',
fontSize: 16,
},
backgroundColor: '#FFF',
line: {
legend: {
bottom: 0,
itemWidth: 25,
itemHeight: 14,
itemGap: 7,
fontSize: 11
},
},
linesPack: {
legend: {
bottom: 0,
itemWidth: 25,
itemHeight: 14,
itemGap: 7,
fontSize: 11
},
nameXAxis: {
padding: [110, 40, 0, 0],
}
},
pie: {
legend: {
itemWidth: 15,
itemHeight: 15,
itemGap: 10,
fontSize: 12
},
graphic: {
period: {
top: 10,
right: 30,
fontSize: 12
},
count: {
top: 'center',
left: 'center',
fontSize: 27
},
}
},
bar: {
legend: {
bottom: 10,
itemWidth: 25,
itemHeight: 14,
itemGap: 10,
fontSize: 12
},
grid: {
width: '90%',
top: 50
},
yAxis: {
label: {fontSize: 13, width: 110, fontWeight: 'normal', position: 'left', offset: 0}
},
xAxis: {
label: {fontSize: 10, fontWeight: 'normal', interval: 1, align: 'center'},
axisTick: {interval: 0}
},
dataZoom: {
top: 50,
start: scrollbarParams(countCharts).start,
minSpan: scrollbarParams(countCharts).minSpan,
},
graphic: {
period: {
top: 10,
right: 60,
fontSize: 12
},
menu: {
top: 7,
right: 5
},
container: {
top: 27,
right: 0
},
rect: {
top: 8,
right: 10
},
download: {
top: 18,
right: 70
},
downloadTitle: {
top: 23,
right: 20
},
back: {
top: 7,
right: 30,
},
link: {
top: 28,
left: 7,
}
}
}
}
}
const sizeOptions = (width = 600, height = 400) => {
return {
width: width,
height: height
}
}
export {axisBarConfig, windowConfig, sizeOptions}
@@ -0,0 +1,63 @@
export class EventStore {
constructor(params) {
this.store = {}
this.params = params
}
storeGetData(data, updateFunc) {
if (data.onlyGet) {
return this.store[data.key] || null
}
return updateFunc({
newData: data.newData,
key: data.key,
data: this.store[data.key] || null
})
}
storeSetData(data) {
return this.store[data.key] = data.value
}
storeRemoveData(data) {
return this.store[data.key] = null
}
storeCreateOrGetData(data, updateFunc) {
if (this.store[data.key]) {
return updateFunc({
newData: data.newData,
key: data.key,
data: this.store[data.key]
})
}
if (!this.store[data.key]) {
this.store[data.key] = data.defaultValue
return updateFunc({
newData: data.newData,
key: data.key,
data: data.defaultValue
})
}
}
localStoreGetData(data, updateFunc) {
if (data.onlyGet) {
return JSON.parse(localStorage.getItem(data.key)) || null
}
return updateFunc({
newData: data.newData,
key: data.key,
data: JSON.parse(localStorage.getItem(data.key)) || null
})
}
localStoreSetData(data) {
return localStorage.setItem(data.key, JSON.stringify(data.value))
}
localStoreRemoveData(data) {
return localStorage.removeItem(data.key)
}
}
@@ -0,0 +1,47 @@
export class PushAfterTimeout {
constructor(id, delay = 1000, iconClass = '') {
this.el = document.getElementById(id)
this.timer = null
this.input = this.el.querySelector('input')
this.iconClass = iconClass
this.makeIconWrapper(this.iconClass)
this.addLoader()
this.timer && clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.removeIcon()
this.input.setAttribute("status", "success")
this.addSuccess(delay)
}, delay)
}
addLoader() {
this.wrapper.innerHTML = `
<div role="status">
<svg aria-hidden="true" class="w-4 h-4 text-gray-100 animate-spin dark:text-gray-600 fill-gray-300" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
</div>
`
}
addSuccess(delay) {
this.wrapper.innerHTML = `<i class="ri-checkbox-circle-line text-green-500 text-lg"></i>`
this.timer = setTimeout(() => {
this.removeIcon();
}, delay);
}
removeIcon() {
this.wrapper.innerHTML = "";
}
makeIconWrapper(iconClass) {
this.wrapper = document.createElement('div');
this.el.appendChild(this.wrapper);
this.wrapper.classList = `absolute flex items-center justify-center right-[10px] top-[50%] ${iconClass}`;
// this.wrapper.style.right = "10px";
// this.wrapper.style.top = "50%";
this.wrapper.style.transform = "translateY(-50%)";
}
}
+39
View File
@@ -0,0 +1,39 @@
import { createStore, createLogger } from 'vuex';
import { store as main } from '@/store/modules/main';
import { store as machines } from '@/store/modules/machines';
import { store as finder_packs } from '@/store/modules/finder_packs';
import { store as add_users } from '@/store/modules/add_users';
import { store as layout } from '@/store/modules/layout';
import { store as auth } from '@/store/modules/auth';
import { store as last_packs } from '@/store/modules/last_packs';
import { store as last_packs_num } from '@/store/modules/last_packs_num';
import { store as packs } from '@/store/modules/packs';
import { store as services } from '@/store/modules/services';
import { store as logger } from '@/store/modules/logger';
let debug = process.env.NODE_ENV !== 'production';
debug = false;
const plugins = debug ? [createLogger({})] : [];
export const store = createStore({
plugins,
modules: {
main,
machines,
finder_packs,
add_users,
layout,
auth,
last_packs,
last_packs_num,
packs,
services,
logger,
},
});
export function useStore() {
return store;
}
@@ -0,0 +1,588 @@
const columns = [
{
title: "ID",
field: "id",
width: 150
},
{
title: "ФИО",
field: "fio",
width: 150
},
{
title: "Логин",
field: "login",
width: 150
},
{
title: "Действия",
field: "actions",
log: true,
render: `
<div class="flex items-center gap-2">
<a target="_blank" href="/html/admin/manage/{{ id }}?prev_page=/\?current_mode=users"><i class="cursor-pointer ri-pencil-line text-slate-700"></i></a>
</div>
`
}
]
const users = {
columns: [
{
download: false,
field: "id",
headerFilter: "input",
headerFilterPlaceholder: "ID",
minWidth: 80,
print: false,
responsive: 0,
sorter: "string",
title: "ID",
vertAlign: "middle",
width: 150
},
{
download: false,
field: "fio",
headerFilter: "input",
headerFilterPlaceholder: "ФИО",
minWidth: 80,
print: false,
responsive: 0,
sorter: "string",
title: "ФИО",
vertAlign: "middle",
width: 150
},
{
download: false,
field: "login",
headerFilter: "input",
headerFilterPlaceholder: "Логин",
minWidth: 80,
print: false,
responsive: 0,
sorter: "string",
title: "Логин",
vertAlign: "middle",
width: 150
},
{
download: false,
field: "actions",
headerFilter: "input",
headerFilterPlaceholder: "Действия",
log: true,
minWidth: 80,
print: false,
render: "\n <div class=\"flex items-center gap-2\">\n <a target=\"_blank\" href=\"/html/admin/manage/{{ id }}?prev_page=/?current_mode=users\"><i class=\"cursor-pointer ri-pencil-line text-slate-700\"></i></a>\n </div>\n ",
responsive: 0,
sorter: "string",
title: "Действия",
vertAlign: "middle"
}
],
data: [
{
adminGroup: {id: 1, name: "ОРИС"},
adminGroupId: 1,
fio: "Шлыков Сергей Николаевич",
firstName: "Сергей",
id: 1,
insertedAt: "2022-03-10 14:51:07",
lastName: "Шлыков",
locale: "ru-RU",
login: "superuser",
notes: "### cHdkIFN1cGVyc2VjcmV0 ###",
passwordHash: "$2b$12$vZkno0sNHC1mlHYqXHyG7Ow74WfTY5BmgyGnN3G4l.QUygY2MDfA6",
secondName: "Николаевич",
updatedAt: "2024-01-15 13:09:35"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "User MNTR ",
firstName: "MNTR",
id: 3,
insertedAt: "2022-03-14 07:12:51",
lastName: "User",
login: "mntr",
notes: "### cHdkIFZuaWlUZXN0 ###",
passwordHash: "$2b$12$fWS34Bkerqo8rakLvOX1b.QmlhCWZviQoSPDEBSX1QOiKxC3hY6Aq",
updatedAt: "2024-01-15 13:09:35"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Романов Александр ",
firstName: "Александр",
id: 17,
insertedAt: "2022-05-24 15:13:00",
lastName: "Романов",
login: "RomanovAV",
notes: "init pwd: 5Nres551### cHdkIDVOcmVzNTUx ###",
passwordHash: "$2b$12$jFDdtPbWQruN0vNmCkEl3uzaWLZxBBYXQC04az6Caj1prQaAz1j8m",
updatedAt: "2024-01-15 13:09:35"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Шмаков Александр Сергеевич",
firstName: "Александр",
id: 18,
insertedAt: "2022-05-24 15:13:00",
lastName: "Шмаков",
login: "smakov",
notes: "init pwd: 9ofVZTC6### cHdkIDlvZlZaVEM2 ###",
passwordHash: "$2b$12$G./jAcqdse6GWEX5x58hvepFAOeBobvnO7EOWjQeZxLaS4Odqicpe",
secondName: "Сергеевич",
updatedAt: "2024-01-15 13:09:36"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Моисеев Сергей Александрович",
firstName: "Сергей",
id: 19,
insertedAt: "2022-05-24 15:13:00",
lastName: "Моисеев",
login: "Moiseyev",
notes: "init pwd: tVWRFS61### cHdkIHRWV1JGUzYx ###",
passwordHash: "$2b$12$KyUjOK8C1a/EqEmnN7gJYeAwG.4kWXuI2OU1UKXtfSojuIZcnBb6e",
secondName: "Александрович",
updatedAt: "2024-01-15 13:09:36"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Карнаухов Александр Иванович",
firstName: "Александр",
id: 20,
insertedAt: "2022-05-24 15:13:00",
lastName: "Карнаухов",
login: "KarnauhovAI",
notes: "init pwd: TwgTmHGH### cHdkIFR3Z1RtSEdI ###",
passwordHash: "$2b$12$33PWjESsfh7gaDYtddv3EutPFi5bOmqSZBDTp4loiAjLpvhWgj.Hm",
secondName: "Иванович",
updatedAt: "2024-01-15 13:09:36"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Бобров Виталий Владимирович",
firstName: "Виталий",
id: 21,
insertedAt: "2022-05-24 15:13:01",
lastName: "Бобров",
login: "Bobrover",
notes: "init pwd: xOUM96vI### cHdkIHhPVU05NnZJ ###",
passwordHash: "$2b$12$hEameXqEy.FfGJYO7YXnKunud0Wn2ba4GyIaq4WevdA/XmR/iOaM.",
secondName: "Владимирович",
updatedAt: "2024-01-15 13:09:36"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Бурак Денис Александрович",
firstName: "Денис",
id: 22,
insertedAt: "2022-05-24 15:13:01",
lastName: "Бурак",
login: "BurakD",
notes: "init pwd: fzqRutIC### cHdkIGZ6cVJ1dElD ###",
passwordHash: "$2b$12$zUISbezh9hVBAW/U32yWjOCxFYS5vPGPGO92h8P/0TLLYymWqBUGW",
secondName: "Александрович",
updatedAt: "2024-01-15 13:09:37"
},
{
adminGroup: {id: 5, name: "TESTERS"},
adminGroupId: 5,
fio: "User Test ",
firstName: "Test",
id: 2,
insertedAt: "2022-03-14 07:12:51",
lastName: "User",
login: "testuser",
notes: "### cHdkIE1lZ2FwYXNzd29yZA== ###",
passwordHash: "$2b$12$zE14rLaUBdVco9RXE9Hepe0mgkbVIYGa4CwiNrZHbHI0SVCNUVQhW",
updatedAt: "2024-01-15 13:50:49"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Ярцев Андрей Васильевич",
firstName: "Андрей",
id: 16,
insertedAt: "2022-05-24 15:12:59",
lastName: "Ярцев",
login: "Yartsev.Andrey",
notes: "init pwd: iWA8qWsv### cHdkIGlXQThxV3N2 ###",
passwordHash: "$2b$12$EAhw4.dUdnSlki0tiFyKI.9HkzfviOYCxqBj/63CNU68rg0idna9i",
secondName: "Васильевич",
updatedAt: "2024-01-15 13:53:07"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Данилов Александр Николаевич",
firstName: "Александр",
id: 23,
insertedAt: "2022-05-24 15:13:01",
lastName: "Данилов",
login: "Danilov.Aleksandr",
notes: "init pwd: hEVonP0H### cHdkIGhFVm9uUDBI ###",
passwordHash: "$2b$12$B1RtRKd7g6fbqYhXfZ9i/e0cVG9CQzI64D37XplU8yUgHz96EX1HO",
secondName: "Николаевич",
updatedAt: "2024-01-15 13:09:37"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Буторин Евгений Львович",
firstName: "Евгений",
id: 24,
insertedAt: "2022-05-24 15:13:01",
lastName: "Буторин",
login: "butorin",
notes: "init pwd: wLsqjX+H### cHdkIHdMc3FqWCtI ###",
passwordHash: "$2b$12$.MMVjOpL1ol4jR6n/b8S9exedOszcsxPbShcWdYOEggtGFaDjnJuG",
secondName: "Львович",
updatedAt: "2024-01-15 13:09:37"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Иванов Дмитрий ",
firstName: "Дмитрий",
id: 25,
insertedAt: "2022-05-24 15:13:01",
lastName: "Иванов",
login: "ivanovd",
notes: "init pwd: OzaruSHc### cHdkIE96YXJ1U0hj ###",
passwordHash: "$2b$12$GBTNgWIRGtlUlfjneY8CCuwkXI5bYnBvjtWRz/u/MQx0fi9VKXfHW",
updatedAt: "2024-01-15 13:09:37"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Ильичев Михаил Сергеевич",
firstName: "Михаил",
id: 33,
insertedAt: "2022-05-24 15:13:03",
lastName: "Ильичев",
login: "ilyichev.mikhail",
notes: "init pwd: NxLW6Xmt### cHdkIE54TFc2WG10 ###",
passwordHash: "$2b$12$P8oiAcMG5m0XvuKLy4Dtve8hw7xnczVDnFGwo0eP7HkEKijQyNOWG",
secondName: "Сергеевич",
updatedAt: "2024-01-15 13:09:37"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Карелин Артём Дмитриевич",
firstName: "Артём",
id: 34,
insertedAt: "2022-05-24 15:13:03",
lastName: "Карелин",
login: "karelin.artem",
notes: "init pwd: +KD43KXa### cHdkICtLRDQzS1hh ###",
passwordHash: "$2b$12$Z6nhsAQLtG5qDndjgHFhzu/AP9oGWEejtWznjua2T/SRLSutLNy.y",
secondName: "Дмитриевич",
updatedAt: "2024-01-15 13:09:38"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Машкова Юлия Сергеевна",
firstName: "Юлия",
id: 35,
insertedAt: "2022-05-24 15:13:04",
lastName: "Машкова",
login: "mashkova",
notes: "init pwd: fCD8a2ub### cHdkIGZDRDhhMnVi ###",
passwordHash: "$2b$12$D61pVAGUHUXaYF761n0vTuHE7XpO.KG.4FcpfHm3y7IffZuR4OPH2",
secondName: "Сергеевна",
updatedAt: "2024-01-15 13:09:38"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Головко Борис Борисович",
firstName: "Борис",
id: 26,
insertedAt: "2022-05-24 15:13:02",
lastName: "Головко",
login: "GolovkoBB",
notes: "init pwd: T8mlO00M### cHdkIFQ4bWxPMDBN ###",
passwordHash: "$2b$12$BSWzgTFpgit9Vz0hKtGqUuBa.bn6SL.qi7ra8LoYo0hp6AR5NfO1m",
secondName: "Борисович",
updatedAt: "2024-01-15 13:09:38"
},
{
adminGroup: {id: 1, name: "ОРИС"},
adminGroupId: 1,
fio: "Перевязкин Александр Александрович",
firstName: "Александр",
id: 36,
insertedAt: "2022-05-24 15:13:04",
lastName: "Перевязкин",
login: "Perevyazkin.Alexandr",
notes: "init pwd: RCZZfFGe### cHdkIFJDWlpmRkdl ###",
passwordHash: "$2b$12$t0VnSxU8Rirbu/50R3JXTeK4fwefglZO8kNC98yDO1FocnUCVTnsW",
secondName: "Александрович",
updatedAt: "2024-01-15 13:09:39"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Гордейчук Алексей Владимирович",
firstName: "Алексей",
id: 27,
insertedAt: "2022-05-24 15:13:02",
lastName: "Гордейчук",
login: "GordeychukAV",
notes: "init pwd: b9fmKp1y### cHdkIGI5Zm1LcDF5 ###",
passwordHash: "$2b$12$mg7xtqxgDgFovnmqfRy5reb0pL1.XWuXxQzcr6L1OCpAtWRXFrsBW",
secondName: "Владимирович",
updatedAt: "2024-01-15 13:09:39"
},
{
adminGroup: {id: 1, name: "ОРИС"},
adminGroupId: 1,
fio: "Котов Кирилл Юрьевич",
firstName: "Кирилл",
id: 28,
insertedAt: "2022-05-24 15:13:02",
lastName: "Котов",
login: "kotov",
notes: "init pwd: 9la1NqNC### cHdkIDlsYTFOcU5D ###",
passwordHash: "$2b$12$yIgJUi47b8NcPLVMaTh2SezTbSdXe7kbMWlNd92p/JxxeyK6PK5vm",
secondName: "Юрьевич",
updatedAt: "2024-01-15 13:09:39"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Шульгин Алексей Викторович",
firstName: "Алексей",
id: 29,
insertedAt: "2022-05-24 15:13:02",
lastName: "Шульгин",
login: "Schulgin.Aleksey",
notes: "init pwd: MULd4uDy### cHdkIE1VTGQ0dUR5 ###",
passwordHash: "$2b$12$rez8rGGiHvHgkxIffxCyp.Ahne1rfgg5khXKLrwmNvwDVrpal0v1a",
secondName: "Викторович",
updatedAt: "2024-01-15 13:09:39"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Егоров Максим Валерьевич",
firstName: "Максим",
id: 30,
insertedAt: "2022-05-24 15:13:03",
lastName: "Егоров",
login: "Egorov.Maxim",
notes: "init pwd: uV4ZwuGv### cHdkIHVWNFp3dUd2 ###",
passwordHash: "$2b$12$vq32dLtEBAOcvME6h5Bb0.sHGa1DryRwoLcCIyzfcJsisZZ.MvyEi",
secondName: "Валерьевич",
updatedAt: "2024-01-15 13:09:39"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Скляров Алексей Николаевич",
firstName: "Алексей",
id: 31,
insertedAt: "2022-05-24 15:13:03",
lastName: "Скляров",
login: "Sklyarov.Aleksey",
notes: "init pwd: RpE5Hy+3### cHdkIFJwRTVIeSsz ###",
passwordHash: "$2b$12$Y35oJ8/99n5sryYOoNTu5eYV77fIvgIA2OFkQ26Nw1b3FlXepUx9i",
secondName: "Николаевич",
updatedAt: "2024-01-15 13:09:40"
},
{
adminGroup: {id: 1, name: "ОРИС"},
adminGroupId: 1,
fio: "Test Admin ",
firstName: "Admin",
id: 47,
insertedAt: "2023-03-02 08:06:05",
lastName: "Test",
login: "ta112",
notes: "### cHdkIFlROEJ1TG5n ###",
passwordHash: "$2b$12$6nuL1xC72rUt30YyGqqr6.5yNQ2vvvQlj2ZJeLrKJ9evqqDE75zAC",
phone: "444",
updatedAt: "2024-01-15 13:09:43"
},
{
adminGroup: {id: 8, name: "ru_watcher"},
adminGroupId: 8,
fio: "Packs Manger ",
firstName: "Manger",
id: 48,
insertedAt: "2023-03-15 11:25:23",
lastName: "Packs",
login: "packs_manger",
notes: "### cHdkIFBhY2sxc3Nz ###",
passwordHash: "$2b$12$rW9uGhrLum.2pTBYgtumAuy8xCqI9iFiUJ4cprGywYLUKbt/XNW4S",
updatedAt: "2024-01-15 13:09:43"
},
{
adminGroup: {id: 1, name: "ОРИС"},
adminGroupId: 1,
fio: "Иванова Александра Сергеевна",
firstName: "Александра",
id: 37,
insertedAt: "2022-05-25 07:16:20",
lastName: "Иванова",
login: "sascha",
notes: "init pwd: Lenovoinwin### cHdkIExlbm92b2lud2lu ###",
passwordHash: "$2b$12$xLWXBi0AYp.QOohHGaURYOp0cuow5eTT24oh1HNXI.ZcOCtzvsVUG",
secondName: "Сергеевна",
updatedAt: "2024-01-15 13:09:40"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "test_admin_del d d",
firstName: "d",
id: 49,
insertedAt: "2023-03-27 07:31:25",
lastName: "test_admin_del",
login: "test_admin_del",
notes: "### cHdkIHpNSjJ0bVhr ###",
passwordHash: "$2b$12$Z0tEFtGnI1iO9kjFXYpIXOwYldhRlNkIcei002qzAy7HVJa7m/I.e",
secondName: "d",
updatedAt: "2024-01-15 13:09:43"
},
{
adminGroup: {id: 9, name: "kudt_watcher"},
adminGroupId: 9,
fio: "KUDT Watcher ",
firstName: "Watcher",
id: 50,
insertedAt: "2023-03-28 07:55:01",
lastName: "KUDT",
login: "KUDT",
notes: "### cHdkIFdhdGNoS3VkdA== ###",
passwordHash: "$2b$12$r47Teqz/KONX3fx2nVEHtup.NRHynjDqobBmNCV7t7fALQwvFXE.a",
updatedAt: "2024-01-15 13:09:43"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Акулов Егор Пользователь",
firstName: "Егор",
id: 52,
insertedAt: "2023-07-31 12:17:56",
lastName: "Акулов",
login: "AkulEgor",
notes: "### cHdkIDEyQWt1bDEz ###",
passwordHash: "$2b$12$37YN2TmjAZjE9dmZXycI4OBKMGVUg.vLcFKwx0sS7mn96hB8V1Iai",
secondName: "Пользователь",
updatedAt: "2024-01-15 13:09:44"
},
{
adminGroup: {id: 12, name: "free_packs_watcher"},
adminGroupId: 12,
fio: "Кузнецов Александр Юрьевич",
firstName: "Александр",
id: 51,
insertedAt: "2023-04-25 09:25:08",
lastName: "Кузнецов",
login: "KuznetsovAY",
notes: "### cHdkIEt1ejU2N2F5 ###",
passwordHash: "$2b$12$WF7iMKiDxKcWShCGTgWnNu4b1HHCxFIP7YPR60LJlwc/BCBwh575q",
secondName: "Юрьевич",
updatedAt: "2024-01-15 13:09:44"
},
{
adminGroup: {id: 8, name: "ru_watcher"},
adminGroupId: 8,
fio: "Ru Watcher ",
firstName: "Watcher",
id: 46,
insertedAt: "2023-03-02 06:31:45",
lastName: "Ru",
login: "RuWatcher",
notes: "### cHdkIEpiWlBqZE05 ###",
passwordHash: "$2b$12$naoRmmTeR6WYy6UZWHxFBuhwO3s52/p/MCkZlP.KgWsXrk8sIUz4W",
phone: "13",
updatedAt: "2024-01-15 13:09:40"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Ананьев Виталий Сергеевич",
firstName: "Виталий",
id: 38,
insertedAt: "2022-05-31 06:34:02",
lastName: "Ананьев",
login: "ananev.vitaly",
notes: "### cHdkIHNKVWl4U0Zh ###",
passwordHash: "$2b$12$JLiIog4a9z2dt8xFP.s/.OBcLHo4cDJgLpCjCxHVQa1rAjmNCIOZG",
secondName: "Сергеевич",
updatedAt: "2024-01-15 13:09:41"
},
{
adminGroup: {id: 15, name: "Монтаж"},
adminGroupId: 15,
fio: "Карпенко Иван Васильевич",
firstName: "Иван",
id: 39,
insertedAt: "2022-06-02 09:28:49",
lastName: "Карпенко",
login: "karpenko.ivan",
notes: "### cHdkIDdaNXM3Tldq ###",
passwordHash: "$2b$12$she6mqurkOULGSd.PzKxeeMppEDjEMhXPdjuH.zncLkVZEKw0iWT2",
secondName: "Васильевич",
updatedAt: "2024-01-15 13:09:41"
},
{
adminGroup: {id: 5, name: "TESTERS"},
adminGroupId: 5,
email: "HasyanovMark@no-mail.xx",
fio: "Хасянов Марк х",
firstName: "Марк",
id: 53,
insertedAt: "2023-09-22 10:27:21",
lastName: "Хасянов",
login: "HasyanovM",
notes: "### cHdkIG5ZdzE4dTk2 ###",
passwordHash: "$2b$12$Pf2T.EKEWXhE2XBpridtOOuKU6NO5evZoGKGMwg4u1wEtCa1hNxUK",
secondName: "х",
updatedAt: "2024-01-15 13:09:44"
},
{
adminGroup: {id: 6, name: "ПКФ"},
adminGroupId: 6,
fio: "Технологии ПКФ ",
firstName: "ПКФ",
id: 40,
insertedAt: "2022-06-02 11:53:48",
lastName: "Технологии",
login: "pkf_teck",
notes: "### cHdkIEFCWDIwSXU3 ###",
passwordHash: "$2b$12$JFef9mZEyWmPENZM2VTTWePVfGogTAgnu4dFYHNMDhY6RPWb7zRXG",
updatedAt: "2024-01-15 13:09:41"
},
{
adminGroup: {id: 7, name: "ВНИКТ"},
adminGroupId: 7,
fio: "Ушанов Андрей ",
firstName: "Андрей",
id: 41,
insertedAt: "2022-06-06 10:02:15",
lastName: "Ушанов",
login: "AUshanov",
notes: "### cHdkIFBPU2t1anox ###",
passwordHash: "$2b$12$StGexN61k2AGoBnCaC2GSeoYrVRGplRoeOSwXXpj3OJiHoLq9zKCi",
updatedAt: "2024-01-15 13:09:41"
},
],
height: "200px"
}
export { columns, users }
Loaded 100 of 255 files, more files were not shown because too many files have changed in this diff. Show more