This commit is contained in:
Фролов 2021-04-02 17:22:34 +05:00
parent 221faded03
commit ef8bca20a4
36 changed files with 2138 additions and 76 deletions

23
.vscode/launch.json vendored Normal file
View 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}"
}
]
}

1
TODO.md Normal file
View File

@ -0,0 +1 @@
env params prod/dev

17
craco.config.js Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@ -3,20 +3,29 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@craco/craco": "^6.1.1",
"@testing-library/jest-dom": "^5.11.10", "@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6", "@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3", "@testing-library/user-event": "^12.8.3",
"antd": "^4.15.0",
"craco-less": "^1.17.1",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"typescript": "^4.2.3",
"web-vitals": "^1.1.1" "web-vitals": "^1.1.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "craco start",
"build": "react-scripts build", "build": "craco build",
"test": "react-scripts test", "test": "craco test",
"react_start": "react-scripts start",
"react_build": "react-scripts build",
"react_test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"proxy": "http://localhost:5000",
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
"react-app", "react-app",
@ -25,7 +34,7 @@
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
">0.2%", ">10%",
"not dead", "not dead",
"not op_mini all" "not op_mini all"
], ],
@ -34,5 +43,9 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
},
"devDependencies": {
"openapi-typescript": "^3.2.0",
"openapi-typescript-codegen": "^0.9.3"
} }
} }

View File

@ -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);
}
}

View File

@ -1,25 +1,43 @@
import logo from './logo.svg'; import './styles/App.less'
import './App.css'; 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 ( return (
<div className="App"> <Route
<header className="App-header"> {...rest}
<img src={logo} className="App-logo" alt="logo" /> render={({ location }) =>
<p> token
Edit <code>src/App.js</code> and save to reload. ? (children)
</p> : (<Redirect to={{pathname: "/login",state: { from: location }}}/>)
<a }
className="App-link" />
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
); );
} }
export default App; export default function App() {
return (
<Router>
<Switch>
<Route path="/login">
<Login />
</Route>
<PrivateRoute path="/">
<Main />
</PrivateRoute>
</Switch>
</Router>
)
}

View File

@ -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();
});

View 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>)
}

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

125
src/images/logoSmaill.svg Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import './index.css'; import './styles/index.css';
import App from './App'; import App from './App';
import reportWebVitals from './reportWebVitals'; import reportWebVitals from './reportWebVitals';

3
src/pages/Files.jsx Normal file
View File

@ -0,0 +1,3 @@
export default function Files(props){
return(<h2>Files</h2>)
}

71
src/pages/Login.jsx Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
export default function Well(props){
return(<div>Well id: {props.id}</div>)
}

60
src/pages/Wells.jsx Normal file
View 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
View File

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View File

@ -0,0 +1 @@
npx openapi -i http://localhost:5000/swagger/v1/swagger.json -o src/services/openapi

View 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;
}
}

View 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>;
}

View 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;
}

View 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,
};

View 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
View 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';

View File

@ -0,0 +1,8 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type AuthDto = {
login?: string | null;
password?: string | null;
}

View 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;
}

View 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;
}

View 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;
}
}

View 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;
}
}

View File

@ -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
View 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;
}

View File

@ -7,7 +7,9 @@ body {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }

34
src/styles/loader.css Normal file
View 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
View 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"
]
}