Merge branch 'main' of https://github.com/perm-hack/perm-hack
commit
469b618c41
@ -1,16 +1,21 @@
|
||||
import { StrictMode } from 'react';
|
||||
import {StrictMode} from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
|
||||
import App from './app/app';
|
||||
import {BrowserRouter} from "react-router-dom";
|
||||
import {Provider} from "react-redux";
|
||||
import store from "./store";
|
||||
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<Provider store={store}>
|
||||
<App/>
|
||||
</Provider>
|
||||
</BrowserRouter>
|
||||
</StrictMode>
|
||||
);
|
||||
|
@ -0,0 +1,32 @@
|
||||
import {createSlice} from "@reduxjs/toolkit";
|
||||
import {timeFormatEnum} from "@perm-hack/ui";
|
||||
|
||||
|
||||
export interface IStatFilters {
|
||||
data: {
|
||||
start_date: string
|
||||
} | {
|
||||
start_data: string,
|
||||
end_date: string
|
||||
} | {}
|
||||
type: "date" | "period"
|
||||
}
|
||||
|
||||
const initialState: IStatFilters = {
|
||||
data: {},
|
||||
type: "date"
|
||||
}
|
||||
const statisticsFilterSlice = createSlice({
|
||||
name: "statFilter",
|
||||
initialState,
|
||||
reducers: {
|
||||
setData: (state, action) => {
|
||||
state.data = action.payload;
|
||||
},
|
||||
setType: (state, action) => {
|
||||
state.type = state.type === "date" ? "period" : "date"
|
||||
}
|
||||
}
|
||||
})
|
||||
export const {setData, setType} = statisticsFilterSlice.actions;
|
||||
export default statisticsFilterSlice.reducer
|
@ -0,0 +1,20 @@
|
||||
import {configureStore} from "@reduxjs/toolkit"
|
||||
import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux";
|
||||
import statisticsFilterSlice from "./features/statisticsFilterSlice";
|
||||
import {statisticsFilterApi} from "./services/statisticsFilterApi";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
statFilter: statisticsFilterSlice,
|
||||
[statisticsFilterApi.reducerPath]: statisticsFilterApi.reducer
|
||||
},
|
||||
middleware: (getDefaultMiddleware) =>
|
||||
getDefaultMiddleware().concat(statisticsFilterApi.middleware),
|
||||
})
|
||||
export default store;
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export const useAppDispatch: (data: any) => AppDispatch = useDispatch // Export a hook that can be reused to resolve types
|
||||
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
@ -0,0 +1,16 @@
|
||||
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||
|
||||
export const statisticsFilterApi = createApi({
|
||||
reducerPath: "statFilterApi",
|
||||
baseQuery: fetchBaseQuery({baseUrl: "https://ayin.k-lab.su/api/analytics"}),
|
||||
endpoints: (build) => ({
|
||||
getDate: build.mutation({
|
||||
query: (data) => ({
|
||||
url: '/all',
|
||||
method: 'POST',
|
||||
body: data,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
export const {useGetDateMutation} = statisticsFilterApi
|
@ -0,0 +1,18 @@
|
||||
import asyncio
|
||||
from websockets.server import serve
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
async def handler(websocket):
|
||||
async for message in websocket:
|
||||
print('recv msg', message)
|
||||
|
||||
|
||||
|
||||
async def main():
|
||||
async with serve(handler, '0.0.0.0', 8765):
|
||||
await asyncio.Future() # run forever
|
||||
|
||||
asyncio.run(main())
|
@ -0,0 +1 @@
|
||||
websockets
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,3 @@
|
||||
.container {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -1,15 +1,19 @@
|
||||
import cls from './statistics.module.scss';
|
||||
import {Filters} from "@perm-hack/ui";
|
||||
// eslint-disable-next-line @nx/enforce-module-boundaries
|
||||
import {Filters, Sidebar, StatisticsCharts} from "@perm-hack/ui";
|
||||
import {useState} from "react";
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface StatisticsProps {}
|
||||
export interface StatisticsProps {
|
||||
}
|
||||
|
||||
export function Statistics(props: StatisticsProps) {
|
||||
return (
|
||||
<div className={cls.container}>
|
||||
<Filters/>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className={cls.container}>
|
||||
<Filters/>
|
||||
<StatisticsCharts/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Statistics;
|
||||
|
@ -1,23 +1,23 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@perm-hack/page": ["page/src/index.ts"],
|
||||
"@perm-hack/ui": ["ui/src/index.ts"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "tmp"]
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"module": "esnext",
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@perm-hack/page": ["page/src/index.ts"],
|
||||
"@perm-hack/ui": ["ui/src/index.ts"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "tmp"]
|
||||
}
|
||||
|
@ -1,42 +1,60 @@
|
||||
import cls from './camera.module.scss';
|
||||
import {useEffect, useState} from "react";
|
||||
|
||||
/* eslint-disable-next-line */
|
||||
export interface CameraProps {
|
||||
}
|
||||
|
||||
export function Camera(props: CameraProps) {
|
||||
return (
|
||||
<div className={cls.container}>
|
||||
<div className={cls.select}>
|
||||
<div>
|
||||
<p className={cls.date}>
|
||||
2020-23-21
|
||||
</p>
|
||||
<p className={cls.time}>
|
||||
14:32
|
||||
</p>
|
||||
</div>
|
||||
<span className={cls.line}></span>
|
||||
<div>
|
||||
<p className={cls.name}>
|
||||
Cum
|
||||
</p>
|
||||
<p className={cls.number}>
|
||||
№69
|
||||
</p>
|
||||
<svg width="24" height="22" viewBox="0 0 24 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 9.16699H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 5.5H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 12.833H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 16.5H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
const [randomNumber, setRandomNumber] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Создаем WebSocket-соединение
|
||||
const ws = new WebSocket('ws://localhost:8765/');
|
||||
|
||||
// Обработка сообщений от сервера
|
||||
ws.onmessage = (event) => {
|
||||
const newRandomNumber = event.data;
|
||||
setRandomNumber(newRandomNumber);
|
||||
};
|
||||
|
||||
// Закрытие соединения при размонтировании компонента
|
||||
return () => {
|
||||
ws.close();
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<div className={cls.container}>
|
||||
<div className={cls.select}>
|
||||
<div>
|
||||
<p className={cls.date}>
|
||||
2020-23-21
|
||||
</p>
|
||||
<p className={cls.time}>
|
||||
14:32
|
||||
</p>
|
||||
</div>
|
||||
<span className={cls.line}></span>
|
||||
<div>
|
||||
<p className={cls.name}>
|
||||
Cum
|
||||
</p>
|
||||
<p className={cls.number}>
|
||||
№69
|
||||
</p>
|
||||
<svg width="24" height="22" viewBox="0 0 24 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 9.16699H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 5.5H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 12.833H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
<path d="M21 16.5H3" stroke="black" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{randomNumber}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
here camera
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
export default Camera;
|
||||
|
@ -1,12 +1,18 @@
|
||||
.container {
|
||||
grid-area: 1 / 1 / 2 / 6;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-size: 14px;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
color: var(--white);
|
||||
background-color: #94BF5E;
|
||||
|
||||
&:disabled {
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,64 @@
|
||||
.item {
|
||||
background: var(--white);
|
||||
border-radius: var(--border-medium);
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
gap: 0 8px;
|
||||
justify-content: space-between;
|
||||
background: var(--white);
|
||||
border-radius: var(--border-medium);
|
||||
padding: 10px 20px;
|
||||
display: flex;
|
||||
gap: 0 8px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 1px;
|
||||
background-color: var(--black);
|
||||
width: 1px;
|
||||
background-color: var(--black);
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0 10px;
|
||||
justify-content: center;
|
||||
.text {
|
||||
text-align: end;
|
||||
color: var(--gray);
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0 10px;
|
||||
justify-content: center;
|
||||
.text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--gray);
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
& svg {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.percent {
|
||||
font-size: 54px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
}
|
||||
.percent {
|
||||
font-size: 54px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
& p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 36px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
color: var(--black
|
||||
);
|
||||
}
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
|
||||
& span {
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
}
|
||||
& p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 36px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
color: var(--black
|
||||
);
|
||||
}
|
||||
|
||||
& span {
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue