forked from ddrilling/asb_cloud_front
innit
This commit is contained in:
parent
221faded03
commit
ef8bca20a4
23
.vscode/launch.json
vendored
Normal file
23
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Edge",
|
||||
"request": "launch",
|
||||
"type": "pwa-msedge",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
17
craco.config.js
Normal file
17
craco.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
const CracoLessPlugin = require('craco-less');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
{
|
||||
plugin: CracoLessPlugin,
|
||||
options: {
|
||||
lessLoaderOptions: {
|
||||
lessOptions: {
|
||||
//modifyVars: { '@primary-color': '#E20000' },
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
1177
package-lock.json
generated
1177
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@ -3,20 +3,29 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@craco/craco": "^6.1.1",
|
||||
"@testing-library/jest-dom": "^5.11.10",
|
||||
"@testing-library/react": "^11.2.6",
|
||||
"@testing-library/user-event": "^12.8.3",
|
||||
"antd": "^4.15.0",
|
||||
"craco-less": "^1.17.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.2.3",
|
||||
"web-vitals": "^1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"react_start": "react-scripts start",
|
||||
"react_build": "react-scripts build",
|
||||
"react_test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"proxy": "http://localhost:5000",
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
@ -25,7 +34,7 @@
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
">10%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
@ -34,5 +43,9 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"openapi-typescript": "^3.2.0",
|
||||
"openapi-typescript-codegen": "^0.9.3"
|
||||
}
|
||||
}
|
||||
|
38
src/App.css
38
src/App.css
@ -1,38 +0,0 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
58
src/App.js
58
src/App.js
@ -1,25 +1,43 @@
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import './styles/App.less'
|
||||
import React /*, { useContext, createContext, useState }*/ from "react"
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Switch,
|
||||
Route,
|
||||
Redirect
|
||||
} from "react-router-dom"
|
||||
import Login from './pages/Login'
|
||||
import Main from './pages/Main'
|
||||
import {OpenAPI} from './services/api'
|
||||
|
||||
function App() {
|
||||
OpenAPI.BASE = 'http://localhost:3000'
|
||||
OpenAPI.TOKEN = localStorage['token']
|
||||
|
||||
function PrivateRoute({ children, ...rest }) {
|
||||
let token = localStorage['token']
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
<Route
|
||||
{...rest}
|
||||
render={({ location }) =>
|
||||
token
|
||||
? (children)
|
||||
: (<Redirect to={{pathname: "/login",state: { from: location }}}/>)
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default function App() {
|
||||
return (
|
||||
<Router>
|
||||
<Switch>
|
||||
<Route path="/login">
|
||||
<Login />
|
||||
</Route>
|
||||
<PrivateRoute path="/">
|
||||
<Main />
|
||||
</PrivateRoute>
|
||||
</Switch>
|
||||
</Router>
|
||||
)
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
4
src/components/Loader.jsx
Normal file
4
src/components/Loader.jsx
Normal file
@ -0,0 +1,4 @@
|
||||
/* original from https://loading.io/css/ */
|
||||
export default function Loader(){
|
||||
return(<div className="lds-ripple"><div></div><div></div></div>)
|
||||
}
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
125
src/images/logoSmaill.svg
Normal file
125
src/images/logoSmaill.svg
Normal file
@ -0,0 +1,125 @@
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg736"
|
||||
viewBox="0 0 8006.9226 3574.0189"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;image-rendering:optimizeQuality;shape-rendering:geometricPrecision;text-rendering:geometricPrecision"
|
||||
version="1.1"
|
||||
height="38.606144mm"
|
||||
width="86.490891mm"
|
||||
xml:space="preserve"><metadata
|
||||
id="metadata740"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs674"><style
|
||||
id="style669"
|
||||
type="text/css"><![CDATA[
|
||||
.fil4 {fill:none}
|
||||
.fil1 {fill:#2D2242}
|
||||
.fil3 {fill:#9D9E9E}
|
||||
.fil0 {fill:#E31E24}
|
||||
.fil2 {fill:#FEFEFE}
|
||||
.fil5 {fill:#2D2242;fill-rule:nonzero}
|
||||
]]></style><clipPath
|
||||
id="id0"><path
|
||||
id="path671"
|
||||
d="m 5189,716 c 587,0 1063,476 1063,1063 0,587 -476,1063 -1063,1063 -588,0 -1064,-476 -1064,-1063 0,-587 476,-1063 1064,-1063 z" /></clipPath></defs><g
|
||||
transform="translate(-1018.0595)"
|
||||
id="Слой_x0020_1"><metadata
|
||||
id="CorelCorpID_0Corel-Layer" /><g
|
||||
id="_661845560"><path
|
||||
style="fill:#e31e24"
|
||||
id="path677"
|
||||
d="M 1756,3564 H 1018 L 3236,2 3848,3 4452,0 4400,184 C 4637,66 4905,0 5189,0 5751,0 6253,261 6579,669 l -233,810 C 6213,964 5745,584 5189,584 c -528,0 -975,341 -1134,815 l -30,108 c -20,87 -31,178 -31,272 0,660 535,1195 1195,1195 318,0 607,-125 821,-327 l -220,764 c -185,88 -388,147 -601,147 -636,0 -1194,-334 -1508,-836 l -239,842 h -702 l 187,-595 H 2146 Z M 3082,2443 3703,446 2463,2444 Z"
|
||||
class="fil0" /><path
|
||||
style="fill:#e31e24"
|
||||
id="path679"
|
||||
d="m 7725,3574 c -392,-2 -748,-14 -1152,-2 L 5869,3559 6882,2 l 1790,1 -136,559 -1176,9 -121,462 h 836 c 570,93 953,697 950,1254 -3,656 -585,1291 -1300,1287 z m -995,-606 c 333,0 665,0 998,2 381,2 691,-335 693,-686 1,-291 -206,-632 -510,-673 h -824 z"
|
||||
class="fil0" /><polygon
|
||||
style="fill:#2d2242"
|
||||
id="polygon681"
|
||||
points="5347,1437 5105,1437 5105,1315 5347,1315 "
|
||||
class="fil1" /><polygon
|
||||
style="fill:#2d2242"
|
||||
id="polygon683"
|
||||
points="5455,1555 4992,1555 4992,1469 5455,1469 "
|
||||
class="fil1" /><polygon
|
||||
style="fill:#2d2242"
|
||||
id="polygon685"
|
||||
points="5597,2523 4860,2523 5027,1587 5419,1587 "
|
||||
class="fil1" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon687"
|
||||
points="5166,1737 5061,1830 5089,1676 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon689"
|
||||
points="5288,1737 5393,1830 5365,1676 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon691"
|
||||
points="5224,1696 5285,1654 5172,1654 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon693"
|
||||
points="5143,2007 5019,2062 5039,1952 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon695"
|
||||
points="5310,2007 5435,2062 5415,1952 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon697"
|
||||
points="5091,1894 5229,1962 5365,1894 5229,1783 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon699"
|
||||
points="5052,2132 5232,2251 5412,2130 5229,2043 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon701"
|
||||
points="5163,2297 4949,2445 4996,2184 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon703"
|
||||
points="5292,2297 5505,2445 5458,2184 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#fefefe"
|
||||
id="polygon705"
|
||||
points="5226,2337 5497,2523 4958,2523 "
|
||||
class="fil2" /><polygon
|
||||
style="fill:#2d2242"
|
||||
id="polygon707"
|
||||
points="5246,2523 5200,2523 5200,1735 5246,1735 "
|
||||
class="fil1" /><g
|
||||
id="g709" /><g
|
||||
id="g722"
|
||||
clip-path="url(#id0)"><g
|
||||
id="_661862656"><path
|
||||
style="fill:#9d9e9e"
|
||||
id="path711"
|
||||
d="m 5136,177 c -688,66 -1152,378 -1415,911 l 1475,-196 783,591 z"
|
||||
class="fil3" /><path
|
||||
style="fill:#9d9e9e"
|
||||
id="path713"
|
||||
d="M 6684,1229 C 6401,599 5957,260 5367,182 l 659,1333 -308,931 z"
|
||||
class="fil3" /><path
|
||||
style="fill:#2d2242"
|
||||
id="path715"
|
||||
d="m 6189,3044 c 509,-466 692,-994 581,-1579 l -1059,1044 -981,-1 z"
|
||||
class="fil1" /><path
|
||||
style="fill:#2d2242"
|
||||
id="path717"
|
||||
d="m 4267,3105 c 598,345 1157,360 1681,78 L 4633,2488 4337,1552 Z"
|
||||
class="fil1" /><path
|
||||
style="fill:#2d2242"
|
||||
id="path719"
|
||||
d="m 3626,1346 c -142,676 17,1212 447,1622 l 253,-1466 798,-571 z"
|
||||
class="fil1" /></g></g><path
|
||||
style="fill:none"
|
||||
id="path724"
|
||||
d="m 5189,716 c 587,0 1063,476 1063,1063 0,587 -476,1063 -1063,1063 -588,0 -1064,-476 -1064,-1063 0,-587 476,-1063 1064,-1063 z"
|
||||
class="fil4" /></g></g></svg>
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/images/logo_32.png
Normal file
BIN
src/images/logo_32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import './styles/index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
|
3
src/pages/Files.jsx
Normal file
3
src/pages/Files.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Files(props){
|
||||
return(<h2>Files</h2>)
|
||||
}
|
71
src/pages/Login.jsx
Normal file
71
src/pages/Login.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import { Card, Form, Input, Button, notification } from 'antd';
|
||||
import { UserOutlined, LockOutlined } from '@ant-design/icons'
|
||||
import logo from '../images/logo_32.png'
|
||||
import Loader from '../components/Loader'
|
||||
import { useState } from 'react'
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { AuthService, OpenAPI } from '../services/api/'
|
||||
|
||||
const { login } = AuthService
|
||||
|
||||
const openNotificationError = (message, title) => {
|
||||
notification['error']({
|
||||
message: title||'Ошибка',
|
||||
description: message,
|
||||
});
|
||||
};
|
||||
|
||||
const setUser = (user) =>{
|
||||
OpenAPI.TOKEN = user.token
|
||||
localStorage['token'] = user.token
|
||||
localStorage['login'] = user.login
|
||||
}
|
||||
|
||||
export default function Login() {
|
||||
const [loader, setLoader] = useState(false);
|
||||
const history = useHistory();
|
||||
|
||||
const logoIcon = <img src={logo} alt="АСБ" className="logo"/>
|
||||
|
||||
let handleSubmit = async (formData) =>{
|
||||
setLoader(true)
|
||||
try{
|
||||
let user = await login(formData)
|
||||
setUser(user)
|
||||
history.push('well')
|
||||
}catch(e){
|
||||
if(e.status === 400)
|
||||
openNotificationError(e.message)
|
||||
console.error(`Error ${e}`)
|
||||
}
|
||||
setLoader(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='login_page shadow'>
|
||||
<Card title="Система мониторинга" className='shadow' bordered={true} style={{ width: 350 }} extra={logoIcon}>
|
||||
<Form onFinish={handleSubmit}>
|
||||
<Form.Item
|
||||
name="login"
|
||||
rules={[{ required: true, message: 'Пожалуйста введите имя!' }]}>
|
||||
<Input placeholder="Пользователь" prefix={<UserOutlined />}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[{ required: true, message: 'Пожалуйста введите пароль!' }]}
|
||||
>
|
||||
<Input.Password placeholder='пароль' prefix={<LockOutlined />}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
Вход
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Card>
|
||||
{loader && <Loader/>}
|
||||
</div>
|
||||
)
|
||||
}
|
72
src/pages/Main.jsx
Normal file
72
src/pages/Main.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Layout, Menu, Button, } from 'antd'
|
||||
import { UserOutlined, MenuOutlined, FundViewOutlined, FolderOutlined } from '@ant-design/icons'
|
||||
import logo from '../images/logo_32.png'
|
||||
import { useState } from 'react'
|
||||
import { Switch, Route, Redirect, Link} from "react-router-dom"
|
||||
import Wells from './Wells'
|
||||
import Files from './Files'
|
||||
|
||||
const { Header, Content, Sider } = Layout
|
||||
|
||||
export default function Main(){
|
||||
const [sidebarVisible, setSidebarVisible] = useState(true)
|
||||
const login = localStorage['login']
|
||||
|
||||
let handlerExit = ()=>{
|
||||
localStorage.removeItem('login')
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
|
||||
return(
|
||||
<Layout>
|
||||
<Header className="header">
|
||||
<Button icon={<MenuOutlined />} onClick={()=>setSidebarVisible(!sidebarVisible)}/>
|
||||
<img src={logo} alt="АСБ" className="logo"/>
|
||||
<h1 className="title">Мониторинг</h1>
|
||||
<Link to="/login" onClick={handlerExit}>
|
||||
<Button icon={<UserOutlined/>}>
|
||||
({login}) Выход
|
||||
</Button>
|
||||
</Link>
|
||||
</Header>
|
||||
<Layout>
|
||||
{sidebarVisible &&
|
||||
<Sider width={200} className="site-layout-background">
|
||||
<Menu
|
||||
mode="inline"
|
||||
defaultSelectedKeys={['1']}
|
||||
defaultOpenKeys={['sub1']}
|
||||
style={{ height: '100%', borderRight: 0 }}
|
||||
>
|
||||
<Menu.Item key="1" icon= {<FundViewOutlined />}>
|
||||
<Link to="/well">Мониторинг</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="2" icon= {<FolderOutlined />}>
|
||||
<Link to="/file">файлы</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Sider>
|
||||
}
|
||||
|
||||
<Layout>
|
||||
<Content className="site-layout-background sheet">
|
||||
<Switch>
|
||||
<Route path="/file">
|
||||
<Files />
|
||||
</Route>
|
||||
<Route path="/well/:id">
|
||||
<Wells/>
|
||||
</Route>
|
||||
<Route path="/well">
|
||||
<Wells />
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Redirect to={{pathname: "/well"}}/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
}
|
3
src/pages/Well.jsx
Normal file
3
src/pages/Well.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Well(props){
|
||||
return(<div>Well id: {props.id}</div>)
|
||||
}
|
60
src/pages/Wells.jsx
Normal file
60
src/pages/Wells.jsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { WellService, OpenAPI } from '../services/api'
|
||||
import Loader from '../components/Loader'
|
||||
import { Table } from 'antd';
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Месторождение',
|
||||
dataIndex: 'deposit',
|
||||
key: 'deposit',
|
||||
},
|
||||
{
|
||||
title: 'Куст',
|
||||
dataIndex: 'cluster',
|
||||
key: 'cluster',
|
||||
},
|
||||
{
|
||||
title: 'Скважина',
|
||||
dataIndex: 'caption',
|
||||
key: 'caption',
|
||||
},
|
||||
{
|
||||
title: 'Данные',
|
||||
dataIndex: 'lastData',
|
||||
key: 'lastData',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Wells(props){
|
||||
const [wells, setWells] = useState([])
|
||||
const [loader, setLoader] = useState(false)
|
||||
const history = useHistory()
|
||||
|
||||
let updateWellsList = async () => {
|
||||
setLoader(true)
|
||||
try{
|
||||
setWells( await WellService.get())
|
||||
}
|
||||
catch(e){
|
||||
console.error(`${e.message}`);
|
||||
}
|
||||
setLoader(false)
|
||||
}
|
||||
|
||||
useEffect(()=>{updateWellsList()}, [])
|
||||
|
||||
return(<>
|
||||
<h2>Wells</h2>
|
||||
<Table
|
||||
dataSource={wells}
|
||||
columns={columns}
|
||||
onRow={(record, rowIndex) => {
|
||||
return {
|
||||
onClick: event => {history.push(`wells/${record.id}`)},
|
||||
};
|
||||
}}/>
|
||||
{loader&&<Loader/>}
|
||||
</>)
|
||||
}
|
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
1
src/services/AutoGenerateApi.md
Normal file
1
src/services/AutoGenerateApi.md
Normal file
@ -0,0 +1 @@
|
||||
npx openapi -i http://localhost:5000/swagger/v1/swagger.json -o src/services/openapi
|
20
src/services/api/core/ApiError.ts
Normal file
20
src/services/api/core/ApiError.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { ApiResult } from './ApiResult';
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly url: string;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly body: any;
|
||||
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.body = response.body;
|
||||
}
|
||||
}
|
14
src/services/api/core/ApiRequestOptions.ts
Normal file
14
src/services/api/core/ApiRequestOptions.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ApiRequestOptions = {
|
||||
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
|
||||
readonly path: string;
|
||||
readonly cookies?: Record<string, any>;
|
||||
readonly headers?: Record<string, any>;
|
||||
readonly query?: Record<string, any>;
|
||||
readonly formData?: Record<string, any>;
|
||||
readonly body?: any;
|
||||
readonly responseHeader?: string;
|
||||
readonly errors?: Record<number, string>;
|
||||
}
|
10
src/services/api/core/ApiResult.ts
Normal file
10
src/services/api/core/ApiResult.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type ApiResult = {
|
||||
readonly url: string;
|
||||
readonly ok: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly body: any;
|
||||
}
|
27
src/services/api/core/OpenAPI.ts
Normal file
27
src/services/api/core/OpenAPI.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||
type Headers = Record<string, string>;
|
||||
|
||||
type Config = {
|
||||
BASE: string;
|
||||
VERSION: string;
|
||||
WITH_CREDENTIALS: boolean;
|
||||
TOKEN?: string | Resolver<string>;
|
||||
USERNAME?: string | Resolver<string>;
|
||||
PASSWORD?: string | Resolver<string>;
|
||||
HEADERS?: Headers | Resolver<Headers>;
|
||||
}
|
||||
|
||||
export const OpenAPI: Config = {
|
||||
BASE: '',
|
||||
VERSION: '1',
|
||||
WITH_CREDENTIALS: false,
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
PASSWORD: undefined,
|
||||
HEADERS: undefined,
|
||||
};
|
205
src/services/api/core/request.ts
Normal file
205
src/services/api/core/request.ts
Normal file
@ -0,0 +1,205 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import { ApiError } from './ApiError';
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
|
||||
function isDefined<T>(value: T | null | undefined): value is Exclude<T, null | undefined> {
|
||||
return value !== undefined && value !== null;
|
||||
}
|
||||
|
||||
function isString(value: any): value is string {
|
||||
return typeof value === 'string';
|
||||
}
|
||||
|
||||
function isStringWithValue(value: any): value is string {
|
||||
return isString(value) && value !== '';
|
||||
}
|
||||
|
||||
function isBlob(value: any): value is Blob {
|
||||
return value instanceof Blob;
|
||||
}
|
||||
|
||||
function getQueryString(params: Record<string, any>): string {
|
||||
const qs: string[] = [];
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (isDefined(value)) {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(value => {
|
||||
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
||||
});
|
||||
} else {
|
||||
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (qs.length > 0) {
|
||||
return `?${qs.join('&')}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getUrl(options: ApiRequestOptions): string {
|
||||
const path = options.path.replace(/[:]/g, '_');
|
||||
const url = `${OpenAPI.BASE}${path}`;
|
||||
|
||||
if (options.query) {
|
||||
return `${url}${getQueryString(options.query)}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function getFormData(params: Record<string, any>): FormData {
|
||||
const formData = new FormData();
|
||||
Object.keys(params).forEach(key => {
|
||||
const value = params[key];
|
||||
if (isDefined(value)) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||
|
||||
async function resolve<T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> {
|
||||
if (typeof resolver === 'function') {
|
||||
return (resolver as Resolver<T>)(options);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
|
||||
const token = await resolve(options, OpenAPI.TOKEN);
|
||||
const username = await resolve(options, OpenAPI.USERNAME);
|
||||
const password = await resolve(options, OpenAPI.PASSWORD);
|
||||
const defaultHeaders = await resolve(options, OpenAPI.HEADERS);
|
||||
|
||||
const headers = new Headers({
|
||||
Accept: 'application/json',
|
||||
...defaultHeaders,
|
||||
...options.headers,
|
||||
});
|
||||
|
||||
if (isStringWithValue(token)) {
|
||||
headers.append('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = btoa(`${username}:${password}`);
|
||||
headers.append('Authorization', `Basic ${credentials}`);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (isBlob(options.body)) {
|
||||
headers.append('Content-Type', options.body.type || 'application/octet-stream');
|
||||
} else if (isString(options.body)) {
|
||||
headers.append('Content-Type', 'text/plain');
|
||||
} else {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.formData) {
|
||||
return getFormData(options.formData);
|
||||
}
|
||||
if (options.body) {
|
||||
if (isString(options.body) || isBlob(options.body)) {
|
||||
return options.body;
|
||||
} else {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function sendRequest(options: ApiRequestOptions, url: string): Promise<Response> {
|
||||
const request: RequestInit = {
|
||||
method: options.method,
|
||||
headers: await getHeaders(options),
|
||||
body: getRequestBody(options),
|
||||
};
|
||||
if (OpenAPI.WITH_CREDENTIALS) {
|
||||
request.credentials = 'include';
|
||||
}
|
||||
return await fetch(url, request);
|
||||
}
|
||||
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | null {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getResponseBody(response: Response): Promise<any> {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
if (isJSON) {
|
||||
return await response.json();
|
||||
} else {
|
||||
return await response.text();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
|
||||
const errors: Record<number, string> = {
|
||||
400: 'Bad Request',
|
||||
401: 'Unauthorized',
|
||||
403: 'Forbidden',
|
||||
404: 'Not Found',
|
||||
500: 'Internal Server Error',
|
||||
502: 'Bad Gateway',
|
||||
503: 'Service Unavailable',
|
||||
...options.errors,
|
||||
}
|
||||
|
||||
const error = errors[result.status];
|
||||
if (error) {
|
||||
throw new ApiError(result, error);
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
throw new ApiError(result, 'Generic Error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request using fetch client
|
||||
* @param options The request options from the the service
|
||||
* @returns ApiResult
|
||||
* @throws ApiError
|
||||
*/
|
||||
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
|
||||
const url = getUrl(options);
|
||||
const response = await sendRequest(options, url);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
|
||||
catchErrors(options, result);
|
||||
return result;
|
||||
}
|
12
src/services/api/index.ts
Normal file
12
src/services/api/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export { ApiError } from './core/ApiError';
|
||||
export { OpenAPI } from './core/OpenAPI';
|
||||
|
||||
export type { AuthDto } from './models/AuthDto';
|
||||
export type { UserTokenDto } from './models/UserTokenDto';
|
||||
export type { WellDto } from './models/WellDto';
|
||||
|
||||
export { AuthService } from './services/AuthService';
|
||||
export { WellService } from './services/WellService';
|
8
src/services/api/models/AuthDto.ts
Normal file
8
src/services/api/models/AuthDto.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type AuthDto = {
|
||||
login?: string | null;
|
||||
password?: string | null;
|
||||
}
|
15
src/services/api/models/UserTokenDto.ts
Normal file
15
src/services/api/models/UserTokenDto.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type UserTokenDto = {
|
||||
login?: string | null;
|
||||
level?: number | null;
|
||||
name?: string | null;
|
||||
surname?: string | null;
|
||||
patronymic?: string | null;
|
||||
id?: number;
|
||||
customerName?: string | null;
|
||||
roleName?: string | null;
|
||||
token?: string | null;
|
||||
}
|
11
src/services/api/models/WellDto.ts
Normal file
11
src/services/api/models/WellDto.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type WellDto = {
|
||||
id?: number;
|
||||
caption?: string | null;
|
||||
cluster?: string | null;
|
||||
deposit?: string | null;
|
||||
lastData?: any;
|
||||
}
|
43
src/services/api/services/AuthService.ts
Normal file
43
src/services/api/services/AuthService.ts
Normal file
@ -0,0 +1,43 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { AuthDto } from '../models/AuthDto';
|
||||
import type { UserTokenDto } from '../models/UserTokenDto';
|
||||
import { request as __request } from '../core/request';
|
||||
|
||||
export class AuthService {
|
||||
|
||||
/**
|
||||
* Аутентификация пользователя
|
||||
* @param requestBody
|
||||
* @returns UserTokenDto новый токен
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static async login(
|
||||
requestBody?: AuthDto,
|
||||
): Promise<UserTokenDto> {
|
||||
const result = await __request({
|
||||
method: 'POST',
|
||||
path: `/auth/login`,
|
||||
body: requestBody,
|
||||
errors: {
|
||||
400: `логин и пароль не подходят`,
|
||||
},
|
||||
});
|
||||
return result.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Продление срока действия токена
|
||||
* @returns any Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static async refresh(): Promise<any> {
|
||||
const result = await __request({
|
||||
method: 'GET',
|
||||
path: `/auth/refresh`,
|
||||
});
|
||||
return result.body;
|
||||
}
|
||||
|
||||
}
|
21
src/services/api/services/WellService.ts
Normal file
21
src/services/api/services/WellService.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
import type { WellDto } from '../models/WellDto';
|
||||
import { request as __request } from '../core/request';
|
||||
|
||||
export class WellService {
|
||||
|
||||
/**
|
||||
* @returns WellDto Success
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static async get(): Promise<Array<WellDto>> {
|
||||
const result = await __request({
|
||||
method: 'GET',
|
||||
path: `/api/Well`,
|
||||
});
|
||||
return result.body;
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
76
src/styles/App.less
Normal file
76
src/styles/App.less
Normal file
@ -0,0 +1,76 @@
|
||||
@import '~antd/dist/antd.less';
|
||||
@import './loader.css';
|
||||
// Переменные для темы тут:
|
||||
// https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
|
||||
|
||||
//@primary-color: rgba(124, 124, 124, 0.3);
|
||||
@primary-color: rgb(195, 40,40);
|
||||
//@primary-color:rgb(65, 63, 61);
|
||||
//@layout-header-background: rgb(195, 40,40);
|
||||
@layout-header-background: rgb(65, 63, 61);
|
||||
|
||||
#root, .app{min-height:100%;}
|
||||
|
||||
.login_page{
|
||||
position: absolute;
|
||||
height:100%;
|
||||
width:100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.shadow{
|
||||
box-shadow: 1px 1px 4px #00000033;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 24px;
|
||||
}
|
||||
|
||||
.header .logo {
|
||||
background-color: rgb(230, 230, 230);
|
||||
border-radius: 32px;
|
||||
padding: 8px 24px;
|
||||
margin: 8px 10px;
|
||||
box-shadow: #fff 2px;
|
||||
}
|
||||
|
||||
.header .title{
|
||||
flex-grow: 1;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
justify-content: baseline;
|
||||
}
|
||||
|
||||
.header button{
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.sheet{
|
||||
padding: 24px;
|
||||
margin: 8px;
|
||||
min-height: 280;
|
||||
}
|
||||
|
||||
#components-layout-demo-top-side-2 .logo {
|
||||
float: left;
|
||||
width: 120px;
|
||||
height: 31px;
|
||||
margin: 16px 24px 16px 0;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.ant-row-rtl #components-layout-demo-top-side-2 .logo {
|
||||
float: right;
|
||||
margin: 16px 0 16px 24px;
|
||||
}
|
||||
|
||||
.site-layout-background {
|
||||
background: #fff;
|
||||
}
|
@ -7,7 +7,9 @@ body {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
34
src/styles/loader.css
Normal file
34
src/styles/loader.css
Normal file
@ -0,0 +1,34 @@
|
||||
/* original from https://loading.io/css/ */
|
||||
|
||||
.lds-ripple {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
.lds-ripple div {
|
||||
position: absolute;
|
||||
border: 4px solid rgb(226, 29, 29);
|
||||
opacity: 1;
|
||||
border-radius: 50%;
|
||||
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
|
||||
}
|
||||
.lds-ripple div:nth-child(2) {
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
@keyframes lds-ripple {
|
||||
0% {
|
||||
top: 36px;
|
||||
left: 36px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
26
tsconfig.json
Normal file
26
tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user