Browse Source

Много всего

pull/1/head
parent
commit
cc4c09fc11
  1. 12
      .prettierrc
  2. 2418
      package-lock.json
  3. 16
      package.json
  4. 1
      public/images/dew_logo.svg
  5. 171
      src/App.js
  6. 7
      src/components/AboutServer.js
  7. 249
      src/components/Calls.js
  8. 71
      src/components/Categories.js
  9. 5
      src/components/Dashboard.js
  10. 6
      src/components/ErrorPage.js
  11. 20
      src/components/Example.js
  12. 10
      src/components/FavServers.js
  13. 103
      src/components/LeftMenu.js
  14. 240
      src/components/Login.js
  15. 19
      src/components/LongSpan.js
  16. 196
      src/components/Object.js
  17. 7
      src/components/ObjectTypes.js
  18. 224
      src/components/Objects.js
  19. 110
      src/components/PageEdit.js
  20. 56
      src/components/PrivateRoute.js
  21. 24
      src/components/PublicRoute.js
  22. 67
      src/components/ServerInfo.js
  23. 11
      src/components/ServerLog.js
  24. 18
      src/components/ServerLogFancy.js
  25. 10
      src/components/ServerLogs.js
  26. 52
      src/components/TopNavbar.js
  27. 12
      src/components/UnAuthPage.js
  28. 10
      src/components/UserInfo.js
  29. 9
      src/components/Users.js
  30. 4
      src/config/config.dev.json
  31. 6
      src/contexts/storeContext.js
  32. 4
      src/hooks/useStores.js
  33. 15
      src/stores/CallsStore.js
  34. 35
      src/stores/InfoStore.js
  35. 21
      src/stores/LikesStore.js
  36. 6
      src/stores/LocalStore.js
  37. 188
      src/stores/LoginStore.js
  38. 96
      src/stores/ObjectsStore.js
  39. 62
      src/stores/PagesStore.js
  40. 22
      src/stores/RootStore.js
  41. 44
      src/stores/ServersStore.js
  42. 11
      src/stores/UsersStore.js
  43. 3
      src/utils/dateTime.js

12
.prettierrc

@ -0,0 +1,12 @@
{
"printWidth": 120,
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"endOfLine":"lf",
"trailingComma": "es5"
}

2418
package-lock.json generated

File diff suppressed because it is too large Load Diff

16
package.json

@ -3,19 +3,21 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@blueprintjs/core": "^3.24.0",
"@blueprintjs/select": "^3.13.3",
"@blueprintjs/core": "^3.32.1",
"@blueprintjs/select": "^3.14.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"mobx": "^5.15.4",
"mobx-react": "^6.2.1",
"moment": "^2.27.0",
"mobx": "^5.15.7",
"mobx-react": "^6.3.0",
"mobx-utils": "^5.6.1",
"moment": "^2.29.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-hotkeys": "^2.0.0-pre9",
"react-helmet-async": "^1.0.7",
"react-hotkeys": "^2.0.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1"
"react-scripts": "^3.4.3"
},
"scripts": {
"start": "react-scripts start",

1
public/images/dew_logo.svg

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 134.22 51.34"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M134.22,13.9,121.91,50.4H110l-5.4-17.93L99.15,50.4h-12L75,13.9h15l4.53,17.21L99.66,13.9h9.86l5.12,17.21,4.53-17.21Z"/><path class="cls-1" d="M37.29,32.32a23.93,23.93,0,0,1,3.32-12.39V0H26.35V16.56A13.3,13.3,0,0,0,16.92,13C5.47,13,0,22.54,0,32.19S5.47,51.34,16.92,51.34a12.87,12.87,0,0,0,9.43-3.74v2.8H40.61V44.54A23.06,23.06,0,0,1,37.29,32.32Zm-17.2,5.77a5.57,5.57,0,0,1-5.83-5.9,5.57,5.57,0,0,1,5.83-5.91A5.59,5.59,0,0,1,26,32.19,5.59,5.59,0,0,1,20.09,38.09Z"/><path class="cls-1" d="M79.07,35.11,73.25,17.73A20.18,20.18,0,0,0,59.66,13C48.5,13,40.29,21.17,40.29,32.19s8.06,19.15,21.24,19.15c7.63,0,13.39-3.82,16.2-8.14L68.3,37.59c-.94,1.37-3.17,3-6.77,3-3.89,0-6.26-1.8-7.27-4.9H79C79.05,35.51,79.06,35.31,79.07,35.11ZM54.19,28.66c.79-3,2.73-4.9,5.76-4.9,3.38,0,5.18,1.8,5.9,4.9Z"/></g></svg>

After

Width:  |  Height:  |  Size: 968 B

171
src/App.js

@ -1,13 +1,12 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import TopNavbar from './components/TopNavbar';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Login from './components/Login';
import Objects from './components/Objects';
import PrivateRoute from './components/PrivateRoute';
import UnAuthPage from './components/UnAuthPage';
import AboutServer from './components/AboutServer';
import ErrorPage from './components/ErrorPage';
import LoginStore from './stores/LoginStore';
import Dashboard from './components/Dashboard';
import { observer } from 'mobx-react';
import UserInfo from './components/UserInfo';
@ -17,91 +16,117 @@ import ServerLogs from './components/ServerLogs';
import ServerLog from './components/ServerLog';
import FavServers from './components/FavServers';
import PermsTest from './components/PermsTest';
import LocalStore from './stores/LocalStore';
import Categories from './components/Categories';
import ObjectTypes from './components/ObjectTypes';
import Calls from './components/Calls';
import { FocusStyleManager } from "@blueprintjs/core";
import PageEdit from './components/PageEdit'
import { FocusStyleManager } from '@blueprintjs/core';
import { useStores } from './hooks/useStores';
import { HelmetProvider } from 'react-helmet-async';
import ObjectView from './components/Object';
const App = observer(() => {
FocusStyleManager.onlyShowFocusOnTabs();
FocusStyleManager.onlyShowFocusOnTabs();
let { isLoggedIn } = LoginStore;
let { darkTheme } = LocalStore;
const { rootStore } = useStores();
let { isLoggedIn, tryLogin, loading } = rootStore.loginStore;
let { darkTheme } = rootStore.localStore;
// Wrapped in useCallback otherwise it would be recreated each time
// this component rerenders, hence triggering useEffect below
let changeTheme = () => {
if (darkTheme) {
document.body.className = "bp3-dark";
} else {
document.body.className = "bp3-body";
}
}
let [loginChecked, setLoginChecked] = useState(undefined);
// Wrapped in useCallback otherwise it would be recreated each time
// this component rerenders, hence triggering useEffect below
let changeTheme = () => {
if (darkTheme) {
document.body.className = 'bp3-dark';
} else {
document.body.className = 'bp3-body';
}
};
useEffect(() => {
changeTheme();
}, [darkTheme]);
useEffect(() => {
changeTheme();
}, [darkTheme]);
useEffect(() => {
tryLogin(res => {
setLoginChecked(res);
});
}, []);
const containerStyle = {
display: 'flex',
height: '100vh',
flexDirection: 'column',
}
const containerStyle = {
display: 'flex',
height: '100vh',
flexDirection: 'column',
};
const wrapperStyle = {
padding: 20,
width: '100%',
display: 'flex',
flexDirection: 'column'
}
const wrapperStyle = {
padding: 20,
width: '100%',
display: 'flex',
flexDirection: 'column',
};
const menuStyle = {
display: 'flex',
height: '100%'
}
const menuStyle = {
display: 'flex',
height: '100%',
};
const withMenu = Component => (props) => {
return <div style={menuStyle}>
<LeftMenu darkTheme={(localStorage.getItem('theme') === 'dark') ? true : false} />
<div style={wrapperStyle}>
<Component {...props} />
</div>
</div>;
}
const withMenu = Component => props => {
return (
<div style={menuStyle}>
<LeftMenu darkTheme={localStorage.getItem('theme') === 'dark' ? true : false} />
<div style={wrapperStyle}>
<Component {...props} />
</div>
</div>
);
};
return (
<div className="App">
if (loginChecked === undefined) {
return (
<div>
<p>Initialising User...</p>
</div>
);
}
<Router>
<div style={containerStyle}>
<TopNavbar />
<Switch>
<Route exact path="/">
{!!isLoggedIn ? withMenu(Dashboard) : <UnAuthPage />}
</Route>
<Route path="/login" component={Login} />
<Route path="/clogin" component={() => <Login customServer={true} />} />
<Route path="/about"><p>about page</p></Route>
<PrivateRoute exact path="/objects" component={withMenu(Objects)} />
<PrivateRoute path="/objects/:catID" component={withMenu(Objects)} />
<PrivateRoute path="/info" component={withMenu(AboutServer)} />
<PrivateRoute path="/profile" component={withMenu(UserInfo)} />
<PrivateRoute path="/users" component={withMenu(Users)} />
<PrivateRoute exact path="/logs" component={withMenu(ServerLogs)} />
<PrivateRoute path="/servers" component={withMenu(FavServers)} />
<PrivateRoute path="/logs/:filename" component={withMenu(ServerLog)} />
<PrivateRoute path="/permstest" component={withMenu(PermsTest)} permission={'modDynext'} />
<PrivateRoute path="/permstest2" component={withMenu(PermsTest)} permission={'NO_SUCH_PERM'} />
<PrivateRoute path="/categories" component={withMenu(Categories)} />
<PrivateRoute path="/types" component={withMenu(ObjectTypes)} />
<PrivateRoute path="/calls" component={withMenu(Calls)} />
<Route path="*" component={ErrorPage} />
</Switch>
return (
<div className="App">
<HelmetProvider>
<Router>
<div style={containerStyle}>
<TopNavbar />
<Switch>
<Route exact path="/">
{!!isLoggedIn ? withMenu(Dashboard) : loginChecked === false ? <UnAuthPage /> : null}
</Route>
<Route path="/login" component={Login} />
<Route path="/clogin" component={() => <Login customServer={true} />} />
<Route path="/about">
<p>about page</p>
</Route>
<PrivateRoute exact path="/objects" component={withMenu(Objects)} />
<PrivateRoute exact path="/objects/:catID" component={withMenu(Objects)} />
<PrivateRoute exact path="/objects/:catID/:objID" component={withMenu(ObjectView)} />
<PrivateRoute path="/info" component={withMenu(AboutServer)} />
<PrivateRoute path="/profile" component={withMenu(UserInfo)} />
<PrivateRoute path="/users" component={withMenu(Users)} />
<PrivateRoute exact path="/logs" component={withMenu(ServerLogs)} />
<PrivateRoute path="/servers" component={withMenu(FavServers)} />
<PrivateRoute path="/logs/:filename" component={withMenu(ServerLog)} />
<PrivateRoute path="/permstest" component={withMenu(PermsTest)} permission={'modDynext'} />
<PrivateRoute path="/permstest2" component={withMenu(PermsTest)} permission={'NO_SUCH_PERM'} />
<PrivateRoute path="/categories" component={withMenu(Categories)} />
<PrivateRoute path="/types" component={withMenu(ObjectTypes)} />
<PrivateRoute path="/calls" component={withMenu(Calls)} />
<PrivateRoute path="/pages" component={withMenu(PageEdit)} />
<Route path="*" component={ErrorPage} />
</Switch>
</div>
</Router>
</HelmetProvider>
</div>
</Router>
</div>
)
);
});
export default App;

7
src/components/AboutServer.js

@ -1,12 +1,13 @@
import React, { useEffect } from 'react';
import { HTMLTable, H1, Tab, Tabs, Checkbox } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Info from '../stores/ServerInfo';
import { Link } from 'react-router-dom';
import { useStores } from '../hooks/useStores';
const AboutServer = observer(props => {
const { counter, getData, serverInfo, instanceSettings, getInstanceSettings } = Info;
const { rootStore } = useStores();
const { serverInfo, instanceSettings, getInstanceSettings } = rootStore.infoStore;
useEffect(() => {
getInstanceSettings();
@ -104,4 +105,4 @@ const AboutServer = observer(props => {
);
});
export default AboutServer;
export default AboutServer;

249
src/components/Calls.js

@ -1,109 +1,182 @@
import React, { useEffect } from 'react';
import { H1, HTMLTable, Tab, Tabs } from '@blueprintjs/core';
import { H1, HTMLTable, Tab, Tabs, Tag } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import moment from 'moment';
import { Link } from 'react-router-dom';
import CallsStore from '../stores/CallsStore';
import ObjectsStore from '../stores/ObjectsStore';
import { useStores } from '../hooks/useStores';
const Calls = observer(props => {
let { getAllCalls, objects } = CallsStore;
let { getTypes, getTypeByID, types } = ObjectsStore;
const { rootStore } = useStores();
let { getAllCalls, objects } = rootStore.callsStore;
let { getTypeByID, types } = rootStore.objectsStore;
useEffect(() => {
getTypes();
objects.clear();
getAllCalls();
}, [])
}, [getAllCalls, objects]);
const typeImage = (id) => {
const typeImage = id => {
if (types.length) {
let imgString = getTypeByID(id).IMG_NORMAL;
return <img src={`data:image/png;base64,${imgString}`} />
return <img src={`data:image/png;base64,${imgString}`} alt={''}/>;
}
}
const calls = <>
<HTMLTable interactive={true}>
<thead>
<th></th>
<th>Дата</th>
<th>Контактное лицо</th>
<th>Телефон</th>
</thead>
<tbody>
{objects.filter(o => o.ID_TYPE === 1006).map(o => {
return <tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}</td>
<td>{o.Pars['Контактное лицо']}</td>
<td><a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a></td>
};
const calls = (
<>
<HTMLTable>
<thead>
<tr>
<th></th>
<th>Дата</th>
<th>Контактное лицо</th>
<th>Телефон</th>
</tr>
})}
</tbody>
</HTMLTable>
</>
</thead>
<tbody>
{objects
.filter(o => o.ID_TYPE === 1006)
.map(o => {
return (
<tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>
{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}
</td>
<td>{o.Pars['Контактное лицо']}</td>
<td>
<a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a>
</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
const questions = <>
<HTMLTable interactive={true}>
<thead>
<th></th>
<th>Дата</th>
<th>Вопрос</th>
<th>Телефон</th>
<th>Эл. почта</th>
</thead>
<tbody>
{objects.filter(o => o.ID_TYPE === 1005).map(o => {
return <tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}</td>
<td>{o.Pars['Комментарий заказчика']}</td>
<td><a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a></td>
<td><a href={`mailto:${o.Pars['Эл. почта']}`}>{o.Pars['Эл. почта']}</a></td>
const questions = (
<>
<HTMLTable>
<thead>
<tr>
<th></th>
<th>Дата</th>
<th>Вопрос</th>
<th>Телефон</th>
<th>Эл. почта</th>
</tr>
})}
</tbody>
</HTMLTable>
</>
</thead>
<tbody>
{objects
.filter(o => o.ID_TYPE === 1005)
.map(o => {
return (
<tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>
{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}
</td>
<td>{o.Pars['Комментарий заказчика']}</td>
<td>
<a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a>
</td>
<td>
<a href={`mailto:${o.Pars['Эл. почта']}`}>{o.Pars['Эл. почта']}</a>
</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
const requests = <>
<HTMLTable interactive={true}>
<thead>
<th></th>
<th>Дата</th>
<th>Услуга</th>
<th>Компания</th>
<th>Контактное лицо</th>
<th>Телефон</th>
<th>Эл. почта</th>
<th>Комментарий</th>
</thead>
<tbody>
{objects.filter(o => o.ID_TYPE === 1004).map(o => {
return <tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}</td>
<td>{o.Pars['Услуга']}</td>
<td>{o.Pars['Заказчик']}</td>
<td>{o.Pars['Контактное лицо']}</td>
<td><a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a></td>
<td><a href={`mailto:${o.Pars['Эл. почта']}`}>{o.Pars['Эл. почта']}</a></td>
<td>{o.Pars['Комментарий заказчика']}</td>
const requests = (
<>
<HTMLTable>
<thead>
<tr>
<th></th>
<th>Дата</th>
<th>Услуга</th>
<th>Компания</th>
<th>Контактное лицо</th>
<th>Телефон</th>
<th>Эл. почта</th>
<th>Комментарий</th>
</tr>
})}
</tbody>
</HTMLTable>
</>
</thead>
<tbody>
{objects
.filter(o => o.ID_TYPE === 1004)
.map(o => {
return (
<tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td style={{ minWidth: 150 }}>
{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}
</td>
<td>{o.Pars['Услуга']}</td>
<td>{o.Pars['Заказчик']}</td>
<td>{o.Pars['Контактное лицо']}</td>
<td>
<a href={`tel:${o.Pars['Телефон']}`}>{o.Pars['Телефон']}</a>
</td>
<td>
<a href={`mailto:${o.Pars['Эл. почта']}`}>{o.Pars['Эл. почта']}</a>
</td>
<td>{o.Pars['Комментарий заказчика']}</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
return (<>
<H1>Звонки и заявки</H1>
<Tabs id="Tabs">
<Tab id="calls" title="Звонки" panel={calls} />
<Tab id="questions" title="Вопросы" panel={questions} />
<Tab id="requests" title="Заявки" panel={requests} />
</Tabs></>
return (
<>
<H1>Звонки и заявки</H1>
<Tabs id="Tabs">
<Tab
id="calls"
title={
<>
<div style={{ marginBottom: '2px' }}>
<span style={{ marginRight: '4px' }}>Звонки</span>
<Tag minimal>{objects.filter(o => o.ID_TYPE === 1006).length}</Tag>
</div>
</>
}
panel={calls}
/>
<Tab
id="questions"
title={
<>
<div style={{ marginBottom: '2px' }}>
<span style={{ marginRight: '4px' }}>Вопросы</span>
<Tag minimal>{objects.filter(o => o.ID_TYPE === 1005).length}</Tag>
</div>
</>
}
panel={questions}
/>
<Tab
id="requests"
title={
<>
<div style={{ marginBottom: '2px' }}>
<span style={{ marginRight: '4px' }}>Заявки</span>
<Tag minimal>{objects.filter(o => o.ID_TYPE === 1004).length}</Tag>
</div>
</>
}
panel={requests}
/>
</Tabs>
</>
);
});
export default Calls;
export default Calls;

71
src/components/Categories.js

@ -1,44 +1,45 @@
import React, { useEffect } from 'react';
import { H1, Icon, HTMLTable } from '@blueprintjs/core';
import React from 'react';
import { H1, HTMLTable } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import ObjectsStore from '../stores/ObjectsStore';
import { useStores } from '../hooks/useStores';
const Categories = observer(props => {
let { categories, getCategories } = ObjectsStore;
const { rootStore } = useStores();
let { categories } = rootStore.objectsStore;
useEffect(() => {
getCategories();
}, [])
const catsList = <>
<HTMLTable interactive={true}>
<thead>
<th>ID</th>
<th>Имя</th>
<th>Короткое имя</th>
<th>Таблица</th>
<th>Действия</th>
</thead>
<tbody>
{categories.map(c => {
return <tr>
<td>{c.ID_CAT}</td>
<td>{c.NAME}</td>
<td>{c.NAME_SHORT}</td>
<td>{c.DB_TBL_NAME}</td>
<td></td>
const catsList = (
<>
<HTMLTable interactive={true}>
<thead>
<tr>
<th>ID</th>
<th>Имя</th>
<th>Короткое имя</th>
<th>Таблица</th>
</tr>
})}
</tbody>
</HTMLTable>
</>
</thead>
<tbody>
{categories.map(c => {
return (
<tr>
<td>{c.ID_CAT}</td>
<td>{c.NAME}</td>
<td>{c.NAME_SHORT}</td>
<td>{c.DB_TBL_NAME}</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
return (<>
<H1>Категории</H1>
{catsList}
</>
return (
<>
<H1>Категории</H1>
{catsList}
</>
);
});
export default Categories;
export default Categories;

5
src/components/Dashboard.js

@ -1,12 +1,15 @@
import React from 'react';
import { observer } from 'mobx-react';
import config from '../config';
import { useStores } from '../hooks/useStores';
const Dashboard = observer(props => {
const { rootStore } = useStores();
return <>
<div >
<p>{config.api}</p>
<p>Default server: {config.api}</p>
<p>Current server: {rootStore.loginStore.server}</p>
</div>
</>
});

6
src/components/ErrorPage.js

@ -1,8 +1,6 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useHistory, useLocation } from 'react-router-dom';
//import './ErrorPage.css';
import { useHistory } from 'react-router-dom';
function ErrorPage(props) {
@ -28,4 +26,4 @@ function ErrorPage(props) {
)
}
export default ErrorPage;
export default ErrorPage;

20
src/components/Example.js

@ -1,26 +1,16 @@
import React, { useEffect, useState } from 'react';
import { observer } from 'mobx-react';
import { apiRequest } from '../deepApi';
import Login from '../stores/LoginStore';
import { useStores } from '../hooks/useStores';
const Example = observer(props => {
const { session } = Login;
const { rootStore } = useStores();
let [serverName, setServerName] = useState('');
useEffect(() => {
// Пример вызова апи
// apiRequest(req, "api-...", session, res => ...);
apiRequest(null, "sys-status", session, res => setServerName(res.ServerNameFull));
/* Еще пример
apiRequest(req, // тело запроса
"api-...", // имя АПИ
session, // сессия (в следующих версиях будет подставлена автоматически)
res => ..., // результат
err => ..., // сообщение об ошибки
() => ..., // завершение
null,
p => ...); // прогресс */
// apiRequest(req, "api-...", res => ...);
rootStore.loginStore.apiAuthRequest(null, "sys-status", res => setServerName(res.ServerNameFull));
}, [])
return (<p>Example component, Server name: {serverName}</p>);

10
src/components/FavServers.js

@ -1,17 +1,17 @@
import React, { useState, useEffect } from 'react';
import { H1, HTMLTable, Card, InputGroup, Button, ControlGroup, Callout, Intent, FormGroup, ButtonGroup, Icon } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import ServersStore from '../stores/ServersStore';
import { apiUnAuthCustomRequest } from '../deepApi'
import moment from 'moment';
import { useStores } from '../hooks/useStores';
const FavServers = observer(props => {
const { rootStore } = useStores();
let [currentServer, setCurrentServer] = useState('');
let [currentServerOk, setCurrentServerOk] = useState(null);
let [calloutText, setCalloutText] = useState('');
let { servers, getServers, addServer, clearServers, checkServer, checkServers, removeServer } = ServersStore;
let { servers, getServers, addServer, clearServers, checkServer, checkServers, removeServer } = rootStore.serversStore;
function showStatus(server) {
if (server.status) {
@ -73,7 +73,7 @@ const FavServers = observer(props => {
</FormGroup>
function checkCurrentServer(server) {
apiUnAuthCustomRequest(null, "sys-status", server, (res) => {
rootStore.loginStore.apiUnAuthCustomRequest(null, "sys-status", server, (res) => {
setCurrentServerOk(true); setCalloutText(res.ServerNameFull)
}, (err) => { setCurrentServerOk(false); setCalloutText(err) });
}
@ -112,4 +112,4 @@ const FavServers = observer(props => {
);
});
export default FavServers;
export default FavServers;

103
src/components/LeftMenu.js

@ -1,83 +1,96 @@
import React, { useEffect } from 'react';
import { H1, H5, HTMLTable, Tab, Tabs, Tag, NonIdealState } from '@blueprintjs/core';
import React from 'react';
import { H5 } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Users from '../stores/UsersStore';
import { NavLink } from 'react-router-dom';
import LocalStore from '../stores/LocalStore';
import { useStores } from '../hooks/useStores';
const LeftMenu = observer(props => {
const { users, getUsers } = Users;
let { darkTheme } = LocalStore;
useEffect(() => {
getUsers();
}, [])
const { rootStore } = useStores();
let { darkTheme } = rootStore.localStore;
const menuStyle = {
padding: 20,
background: darkTheme ? '#30404D' : '#EBF1F5',
display: 'flex',
maxWidth: 200
}
maxWidth: 200,
};
const listStyle = {
listStyleType: 'none',
padding: 0,
lineHeight: 1.4,
}
};
const liStyle = {
marginBottom: 4
}
marginBottom: 4,
};
const sectionStyle = {
color: '#5C7080',
fontVariant: 'all-small-caps',
}
};
const activeStyle = {
fontWeight: "bold",
color: darkTheme ? '#FFFFFF' : "#30404D",
fontWeight: 'bold',
color: darkTheme ? '#FFFFFF' : '#30404D',
};
const menu = [
{
section: "Администрация",
section: 'Администрация',
links: [
{ link: '/users', caption: 'Пользователи' },
{ link: '/info', caption: 'О сервере' },
{ link: '/logs', caption: 'Логи' },
],
},
{
section: 'Объекты',
links: [
{ link: "/users", caption: "Пользователи" },
{ link: "/info", caption: "О сервере" },
{ link: "/logs", caption: "Логи" },
]
{ link: '/categories', caption: 'Категории' },
{ link: '/types', caption: 'Типы объектов' },
{ link: '/objects', caption: 'Объекты' },
],
},
{
section: "Объекты",
section: 'Всякая фигня',
links: [
{ link: "/categories", caption: "Категории" },
{ link: "/types", caption: "Типы объектов" },
{ link: "/objects", caption: "Объекты" },
]
{ link: '/servers', caption: 'Избранные сервера' },
{ link: '/permstest', caption: 'Тест разрешений (modDynext)' },
{ link: '/permstest2', caption: 'Тест разрешений (NO_SUCH_PERM)' },
],
},
{
section: "Всякая фигня",
section: 'CMS',
links: [
{ link: "/servers", caption: "Избранные сервера" },
{ link: "/permstest", caption: "Тест разрешений (modDynext)" },
{ link: "/permstest2", caption: "Тест разрешений (NO_SUCH_PERM)" },
{ link: "/calls", caption: "Звонки" },
]
{ link: '/calls', caption: 'Звонки' },
{ link: '/pages', caption: 'Редактор страниц' },
],
},
]
];
return (<>
<div style={menuStyle}>
<nav>
{menu.map(m => <><H5 style={sectionStyle}>{m.section}</H5><ul style={listStyle}>
{m.links.map(l => <li style={liStyle}><NavLink activeStyle={activeStyle} to={l.link}>{l.caption}</NavLink></li>)}</ul></>)}
</nav>
</div>
</>
return (
<>
<div style={menuStyle}>
<nav>
{menu.map(m => (
<>
<H5 style={sectionStyle}>{m.section}</H5>
<ul style={listStyle}>
{m.links.map(l => (
<li key={l.link} style={liStyle}>
<NavLink activeStyle={activeStyle} to={l.link}>
{l.caption}
</NavLink>
</li>
))}
</ul>
</>
))}
</nav>
</div>
</>
);
});
export default LeftMenu;
export default LeftMenu;

240
src/components/Login.js

@ -1,18 +1,16 @@
import React, { useState, useEffect } from 'react';
import { Button, Tooltip, InputGroup, FormGroup, Intent, Callout, Card, Switch, ControlGroup, HTMLSelect, MenuItem, H2 } from '@blueprintjs/core';
import { Select } from "@blueprintjs/select";
import { Select } from '@blueprintjs/select';
import { observer } from 'mobx-react';
import LoginStore from '../stores/LoginStore';
import { useHistory, useLocation } from 'react-router-dom';
import { apiUnAuthCustomRequest, changeServer, DEEP_SERVER } from '../deepApi';
import ServersStore from '../stores/ServersStore';
import config from '../config';
import Info from '../stores/ServerInfo';
import { useStores } from '../hooks/useStores';
import { Helmet } from 'react-helmet-async';
const Login = observer(props => {
let [showPassword, setShowPassword] = useState(false);
let [username, setUsername] = useState('');
let [password, setPassword] = useState("");
let [password, setPassword] = useState('');
let [customServer, setCustomServer] = useState(null);
let [useCustomServer, setUseCustomServer] = useState(false);
let [customServerOk, setCustomServerOk] = useState(null);
@ -20,35 +18,36 @@ const Login = observer(props => {
let [error, setError] = useState(null);
let [hasLogins, setHasLogins] = useState(null);
let { servers, getServers, addServer } = ServersStore;
let [calloutText, setCalloutText] = useState(null);
let { login, isLoggedIn, incorrectPass } = LoginStore;
const { rootStore } = useStores();
let { servers, getServers, addServer } = rootStore.serversStore;
let { login, isLoggedIn, incorrectPass, server, apiUnAuthCustomRequest, changeServer } = rootStore.loginStore;
const { serverStatus } = rootStore.infoStore;
let history = useHistory();
let location = useLocation();
let { from } = location.state || { from: { pathname: "/" } };
const { serverStatus, getData } = Info;
let { from } = location.state || { from: { pathname: '/' } };
let { reason } = location.state || { undefined };
let customServerInput = `${customServerProtocol}://${customServer}`.toLowerCase();
const cardStyle = {
width: 500,
alignSelf: 'center',
margin: 'auto'
}
let insecureServerError = config.allowMultiLogin && useCustomServer
&& customServerProtocol === 'http' && window.location.protocol === 'https:';
margin: 'auto',
};
let insecureServerError = config.allowMultiLogin && useCustomServer && customServerProtocol === 'http' && window.location.protocol === 'https:';
useEffect(() => {
if (isLoggedIn) {
addServer(DEEP_SERVER, undefined, undefined);
addServer(server, undefined, undefined);
history.replace(from);
}
if (serverStatus === false) {
setError('Сервер не отвечает')
setError('Сервер не отвечает');
}
if (incorrectPass && hasLogins) {
setError('Неправильный логин или пароль');
@ -57,111 +56,151 @@ const Login = observer(props => {
useEffect(() => {
if (from.pathname !== '/') {
setError('Чтобы просматривать эту страницу, войдите на сайт');
if (location.state.reason) {
setError(reason);
} else {
setError('Чтобы просматривать эту страницу, войдите на сайт');
}
}
getServers();
}, []);
const lockButton = (
<Tooltip content={`${showPassword ? "Скрыть" : "Показать"} пароль`}>
<Button
icon={showPassword ? "unlock" : "lock"}
intent={Intent.WARNING}
minimal={true}
onClick={() => setShowPassword(!showPassword)}
/>
<Tooltip content={`${showPassword ? 'Скрыть' : 'Показать'} пароль`}>
<Button icon={showPassword ? 'unlock' : 'lock'} intent={Intent.WARNING} minimal={true} onClick={() => setShowPassword(!showPassword)} />
</Tooltip>
);
let alert = (text) => {
return <FormGroup>
<Callout intent={Intent.WARNING}>{text}</Callout>
</FormGroup>
}
let alert = text => {
return (
<FormGroup>
<Callout intent={Intent.WARNING}>{text}</Callout>
</FormGroup>
);
};
let doLogin = () => {
setHasLogins(true);
if (useCustomServer) {
apiUnAuthCustomRequest(null, "sys-status", `${customServerProtocol}://${customServer}`, (res) => {
changeServer(`${customServerProtocol}://${customServer}`);
login(username, password);
}, (err) => {
setError(err);
handleCustomServerState(false);
});
apiUnAuthCustomRequest(
null,
'sys-status',
customServerInput,
res => {
changeServer(customServerInput);
login(username, password);
},
err => {
setError(err);
handleCustomServerState(false);
}
);
} else {
login(username, password);
}
}
};
function handleSwitch() {
setUseCustomServer(!useCustomServer);
handleCustomServerState();
}
let options = ["HTTP", "HTTPS"];
let customServerForm = <>
<FormGroup>
<Switch label="Другой сервер" defaultChecked={useCustomServer}
onChange={handleSwitch} />
<ControlGroup>
<HTMLSelect options={options} disabled={!useCustomServer} value={customServerProtocol.toUpperCase()} onChange={(e) => { setCustomServerProtocol(e.target.value.toLowerCase()); handleCustomServerState(null); }} />
<InputGroup disabled={!useCustomServer}
placeholder="server:port"
value={customServer}
onChange={(e) => { setCustomServer(e.target.value); handleCustomServerState(null); }}
/>
<Button text="Тест" disabled={!useCustomServer || !(!!customServer) && !insecureServerError}
onClick={() => { customServerTest(); handleCustomServerState(null); }} />
</ControlGroup>
</FormGroup>
<FormGroup>
<Select
items={servers}
filterable={false}
disabled={!useCustomServer}
noResults={<MenuItem disabled={true} text="Нет сохраненных адресов" />}
itemRenderer={itemRenderer}
onItemSelect={handleClick}>
<Button text={'Сохраненные адреса'} rightIcon="caret-down" />
</Select>
</FormGroup>
</>
let options = ['HTTP', 'HTTPS'];
let customServerForm = (
<>
<FormGroup>
<Switch label="Другой сервер" defaultChecked={useCustomServer} onChange={handleSwitch} />
<ControlGroup>
<HTMLSelect
options={options}
disabled={!useCustomServer}
value={customServerProtocol.toUpperCase()}
onChange={e => {
setCustomServerProtocol(e.target.value.toLowerCase());
handleCustomServerState(null);
}}
/>
<InputGroup
disabled={!useCustomServer}
placeholder="server:port"
value={customServer}
onChange={e => {
setCustomServer(e.target.value);
handleCustomServerState(null);
}}
/>
<Button
text="Тест"
disabled={!useCustomServer || (!!!customServer && !insecureServerError)}
onClick={() => {
customServerTest();
handleCustomServerState(null);
}}
/>
</ControlGroup>
</FormGroup>
<FormGroup>
<Select
items={servers}
filterable={false}
disabled={!useCustomServer}
noResults={<MenuItem disabled={true} text="Нет сохраненных адресов" />}
itemRenderer={itemRenderer}
onItemSelect={handleClick}>
<Button text={'Сохраненные адреса'} rightIcon="caret-down" />
</Select>
</FormGroup>
</>
);
function itemRenderer(item, { handleClick }) {
return (
<MenuItem
key={item.server}
text={item.server}
onClick={handleClick}
shouldDismissPopover={true}
/>
)
return <MenuItem key={item.server} text={item.server} onClick={handleClick} shouldDismissPopover={true} />;
}
function handleClick(item) {
var url = new URL(item.server);
setCustomServerProtocol(url.protocol.replace(":", "").toUpperCase());
setCustomServerProtocol(url.protocol.replace(':', '').toUpperCase());
setCustomServer(url.host);
setCustomServerOk(null);
}
let title = <span>Сервер <a href={`${customServerProtocol}://${customServer}`}>{`${customServerProtocol}://${customServer}`}</a> недоступен</span>
let serverFailed = <FormGroup>
<Callout intent={Intent.WARNING} title={title}>{calloutText}</Callout>
</FormGroup>
let title = (
<span>
Сервер <a href={customServerInput}>{customServerInput}</a> недоступен
</span>
);
let serverFailed = (
<FormGroup>
<Callout intent={Intent.WARNING} title={title}>
{calloutText}
</Callout>
</FormGroup>
);
let serverOk = <FormGroup>
<Callout intent={Intent.SUCCESS}>{calloutText}</Callout>
</FormGroup>
let serverOk = (
<FormGroup>
<Callout intent={Intent.SUCCESS}>{calloutText}</Callout>
</FormGroup>
);
let insecureServer = <FormGroup>
<Callout intent={Intent.DANGER}>Доступ к серверу по HTTP отсюда невозможен</Callout>
</FormGroup>
let insecureServer = (
<FormGroup>
<Callout intent={Intent.DANGER}>Доступ к серверу по HTTP отсюда невозможен</Callout>
</FormGroup>
);
function customServerTest() {
apiUnAuthCustomRequest(null, "sys-status", `${customServerProtocol}://${customServer}`, (res) => { handleResponse(res) }, (err) => { handleError(err) });
apiUnAuthCustomRequest(
null,
'sys-status',
customServerInput,
res => {
handleResponse(res);
},
err => {
handleError(err);
}
);
}
function handleResponse(response) {
@ -187,41 +226,34 @@ const Login = observer(props => {
}
function loginButtonText() {
if (useCustomServer && DEEP_SERVER !== `${customServerProtocol}://${customServer}`) {
return "Сменить сервер и войти";
if (useCustomServer && server !== customServerInput) {
return 'Сменить сервер и войти';
} else {
return "Войти";
return 'Войти';
}
}
return (
<>
<Helmet>
<title>Dew -- Логин</title>
</Helmet>
<Card style={cardStyle}>
<H2>Вход в систему</H2>
{!!error && alert(error)}
<FormGroup>
<InputGroup
placeholder="Логин"
onChange={(e) => setUsername(e.target.value)}
/>
<InputGroup placeholder="Логин" onChange={e => setUsername(e.target.value)} />
</FormGroup>
<FormGroup>
<InputGroup
placeholder="Пароль"
type={showPassword ? "text" : "password"}
rightElement={lockButton}
onChange={(e) => setPassword(e.target.value)}
/>
<InputGroup placeholder="Пароль" type={showPassword ? 'text' : 'password'} rightElement={lockButton} onChange={e => setPassword(e.target.value)} />
</FormGroup>
{config.allowMultiLogin && props.customServer && customServerForm}
{insecureServerError && insecureServer}
{useCustomServer && customServerOk && serverOk}
{useCustomServer && customServerOk === false && serverFailed}
<FormGroup>
<Button text={loginButtonText()} intent="primary" onClick={doLogin}
disabled={loginButtonState()} />
<Button text={loginButtonText()} intent="primary" onClick={doLogin} disabled={loginButtonState()} />
</FormGroup>
</Card>
</>
);

19
src/components/LongSpan.js

@ -0,0 +1,19 @@
import React, { useState } from 'react';
import { observer } from 'mobx-react';
const LongSpan = observer(props => {
let [long, setLong] = useState(false);
let short = props.text.slice(0, 30);
if (props.text.length > 50) {
return (
<span style={{ cursor: 'pointer' }} onClick={() => setLong(!long)}>
{long ? props.text : short + '...'}
</span>
);
} else {
return <span>{props.text}</span>;
}
});
export default LongSpan;

196
src/components/Object.js

@ -1,86 +1,138 @@
import React, { useEffect, useState } from 'react';
import { H1, Spinner, HTMLTable, ControlGroup, InputGroup, Button, FormGroup, NonIdealState } from '@blueprintjs/core';
import React, { useEffect } from 'react';
import { HTMLTable, Icon, Intent, Tabs, Tab, Checkbox } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import ObjectsStore from '../stores/ObjectsStore';
import moment from 'moment';
import { dateTime } from '../utils/dateTime';
import { Link, useParams } from 'react-router-dom';
import { useStores } from '../hooks/useStores';
import LongSpan from './LongSpan';
const Object = observer(props => {
let { getTypes, getAllObjects, objects, getObjects, getTypeByID, types, fetching, getObject } = ObjectsStore;
let [searchMask, setSearchMask] = useState('');
let [waitingForFetch, setWaitingForFetch] = useState(false)
const ObjectView = observer(props => {
const { rootStore } = useStores();
useEffect(() => {
getTypes();
}, [])
let { currentObject, getTypeByID, getObject, getPars, categories, types } = rootStore.objectsStore;
useEffect(() => {
if (searchMask.length > 2) {
setWaitingForFetch(true);
getObjects(searchMask);
setWaitingForFetch(fetching);
}
}, [searchMask])
let { catID, objID } = useParams();
useEffect(() => {
getObject(objID, catID);
getPars(objID, catID);
}, []);
const typeImage = (id) => {
if (types.length) {
let imgString = getTypeByID(id).IMG_NORMAL;
return <img src={`data:image/png;base64,${imgString}`} />
const typeImg = () => {
let type = getTypeByID(currentObject.ID_TYPE);
if (type) {
return <img src={`data:image/png;base64,${type.IMG_NORMAL}`} />;
} else {
return <Icon icon="tick" intent={Intent.SUCCESS} />;
}
}
const objsList = <>
<HTMLTable interactive={true}>
<thead>
<th></th>
<th>ID</th>
<th>ID_CAT</th>
<th>ID_TYPE</th>
<th>Обозначение</th>
<th>Наименование</th>
<th>Дата создания</th>
<th>Дата изменения</th>
</thead>
<tbody>
{objects.map(o => {
return <tr>
<td>{typeImage(o.ID_TYPE)}</td>
<td>{o.ID}</td>
<td>{o.ID_CAT}</td>
<td>{o.ID_TYPE}</td>
<td><code>{o.SPEC}</code></td>
<td>{o.NAME}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfModification).format('DD.MM.YY HH:mm:ss')}</td>
</tr>
})}
</tbody>
</HTMLTable>
</>
};
let clearButton = <Button icon="cross" disabled={!searchMask} minimal={true} onClick={() => setSearchMask('')} />
const cat = categories.filter(c => c.ID_CAT === currentObject.ID_CAT)[0];
const type = types.filter(c => c.ID_TYPE === currentObject.ID_TYPE)[0];
let noResults = <NonIdealState
icon={'search'}
title={searchMask.length > 2 ? `Нет результатов по запросу "${searchMask}"` : 'Введите запрос'}
/>
const info = !!cat && !!type && (
<>
<HTMLTable>
<thead>
<tr>
<th>Параметр</th>
<th>Значение</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>{currentObject.ID}</td>
</tr>
<tr>
<td>Тип</td>
<td>
{typeImg()} {currentObject.ID_TYPE} ({type.GROUP_NAME} &#8594; {type.NAME})
</td>
</tr>
<tr>
<td>Категория</td>
<td>
{currentObject.ID_CAT} ({cat.NAME_SHORT})
</td>
</tr>
<tr>
<td>Наименование</td>
<td>{currentObject.NAME}</td>
</tr>
<tr>
<td>Обозначение</td>
<td>
<code>{currentObject.SPEC}</code>
</td>
</tr>
<tr>
<td>Дата создания</td>
<td>{dateTime(currentObject.DateOfCreation)}</td>
</tr>
<tr>
<td>Дата изменения</td>
<td>{dateTime(currentObject.DateOfModification)}</td>
</tr>
<tr>
<td>Заблокирован</td>
<td><Checkbox disabled={true} checked={currentObject.BLOCKED}/></td>
</tr>
<tr>
<td>Удален</td>
<td><Checkbox disabled={true} checked={currentObject.DELETED}/></td>
</tr>
</tbody>
</HTMLTable>
</>
);
return (<>
<H1>Объекты</H1>
<FormGroup label={'Фильтр'} labelInfo="(минимум 3 символа)">
<ControlGroup>
<InputGroup style={{ width: 500 }}
value={searchMask}
placeholder="Обозначение или наименование"
rightElement={clearButton}
onChange={(e) => setSearchMask(e.target.value)} />
{fetching && <div style={{ marginLeft: 10, display: 'flex', alignItems: 'center' }} ><Spinner size={Spinner.SIZE_SMALL} /></div>}
const pars = !!currentObject.Pars && (
<>
<HTMLTable>
<thead>
<tr>
<th>Параметр</th>
<th>Значение</th>
</tr>
</thead>
<tbody>
{Object.entries(currentObject.Pars).map(([key, value]) => {
return (
<tr>
<td style={{ whiteSpace: 'nowrap' }}>{key}</td>
<td>
<LongSpan text={value.toString()}></LongSpan>
</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
</ControlGroup>
</FormGroup>
{(objects.length === 0 || searchMask === '' && !fetching) ? noResults : objsList}
</>
return (
<>
<ul className="bp3-breadcrumbs">
<li>
<Link className="bp3-breadcrumb" to="/objects">
Объекты
</Link>
</li>
<li>
<span className="bp3-breadcrumb bp3-breadcrumb-current">
{typeImg()}
<code>{currentObject.SPEC}</code>&nbsp;&laquo;{currentObject.NAME}&raquo;
</span>
</li>
</ul>
<Tabs id="tabs">
<Tab id="info" title="Информация" panel={info} />
<Tab id="pars" title="Параметры" panel={pars} />
</Tabs>
</>
);
});
export default Object;
export default ObjectView;

7
src/components/ObjectTypes.js

@ -1,10 +1,11 @@
import React, { useEffect } from 'react';
import { H1, HTMLTable, Tooltip, Classes } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import ObjectsStore from '../stores/ObjectsStore';
import { useStores } from '../hooks/useStores';
const ObjectTypes = observer(props => {
let { types, getTypes } = ObjectsStore;
const { rootStore } = useStores();
let { types, getTypes } = rootStore.objectsStore;
useEffect(() => {
getTypes();
@ -51,4 +52,4 @@ const ObjectTypes = observer(props => {
);
});
export default ObjectTypes;
export default ObjectTypes;

224
src/components/Objects.js

@ -1,37 +1,34 @@
import React, { useEffect, useState } from 'react';
import { H1, Spinner, HTMLTable, ControlGroup, InputGroup, Button, FormGroup, NonIdealState, MenuItem, Callout, Intent } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import ObjectsStore from '../stores/ObjectsStore';
import moment from 'moment';
import { useHistory, useParams } from 'react-router-dom';
import { MultiSelect } from "@blueprintjs/select";
import { Link, useHistory, useParams } from 'react-router-dom';
import { MultiSelect } from '@blueprintjs/select';
import { observable, toJS } from 'mobx';
import { useStores } from '../hooks/useStores';
import { dateTime } from '../utils/dateTime';
const Objects = observer(props => {
let { catID } = useParams();
let { getTypes, objects, getObjects, getTypeByID, types, fetching, getCategories, categories } = ObjectsStore;
let [searchMask, setSearchMask] = useState('');
const { rootStore } = useStores();
let { objects, getObjects, getTypeByID, types, fetching, categories, searchMask, setSearchMask } = rootStore.objectsStore;
let history = useHistory();
let selectedCats = observable([]);
useEffect(() => {
getTypes();
getCategories();
}, [])
useEffect(() => {
if (searchMask.length > 2) {
console.log("rerender");
selectedCats.map(c => console.log(c.ID_CAT))
console.log((selectedCats))
console.log(JSON.stringify(selectedCats))
getObjects(searchMask, selectedCats.map(c => console.log(c.ID_CAT)));
console.log('rerender');
selectedCats.map(c => console.log(c.ID_CAT));
console.log(selectedCats);
console.log(JSON.stringify(selectedCats));
getObjects(
searchMask,
categories.map(c => c.ID_CAT)
);
}
}, [searchMask])
}, [searchMask]);
useEffect(() => {
if (catID) {
@ -45,51 +42,55 @@ const Objects = observer(props => {
} else {
categories.map(c => selectedCats.push(c));
}
}, [selectedCats, searchMask])
}, [selectedCats, searchMask]);
const typeImage = (id) => {
const typeImage = id => {
if (types.length) {
let imgString = getTypeByID(id).IMG_NORMAL;
return <img src={`data:image/png;base64,${imgString}`} />
return <img src={`data:image/png;base64,${imgString}`} alt="" />;
}
}
};
const objsList = <>
<HTMLTable interactive={true}>
<thead>
<th></th>
<th>ID</th>
<th>ID_CAT</th>
<th>ID_TYPE</th>
<th>Обозначение</th>
<th>Наименование</th>
<th>Дата создания</th>
<th>Дата изменения</th>
</thead>
<tbody>
{objects.map(o => {
return <tr onClick={(e) => history.push(`/logs/${o.ID_CAT}/${o.ID}`)}>
<td>{typeImage(o.ID_TYPE)}</td>
<td>{o.ID}</td>
<td>{o.ID_CAT}</td>
<td>{o.ID_TYPE}</td>
<td><code>{o.SPEC}</code></td>
<td>{o.NAME}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfCreation).format('DD.MM.YY HH:mm:ss')}</td>
<td style={{ minWidth: 150 }}>{moment(o.DateOfModification).format('DD.MM.YY HH:mm:ss')}</td>
const objsList = (
<>
<HTMLTable interactive={true}>
<thead>
<tr>
<th></th>
<th>ID</th>
<th>ID_CAT</th>
<th>ID_TYPE</th>
<th>Обозначение</th>
<th>Наименование</th>
<th>Дата создания</th>
<th>Дата изменения</th>
</tr>
})}
</tbody>
</HTMLTable>
</>
</thead>
<tbody>
{objects.map(o => {
return (
<tr onClick={e => history.push(`/objects/${o.ID_CAT}/${o.ID}`)}>
<td>{typeImage(o.ID_TYPE)}</td>
<td>{o.ID}</td>
<td>{o.ID_CAT}</td>
<td>{o.ID_TYPE}</td>
<td>
<code><Link to={`/objects/${o.ID_CAT}/${o.ID}`}>{o.SPEC}</Link></code>
</td>
<td>{o.NAME}</td>
<td style={{ minWidth: 150 }}>{dateTime(o.DateOfCreation)}</td>
<td style={{ minWidth: 150 }}>{dateTime(o.DateOfModification)}</td>
</tr>
);
})}
</tbody>
</HTMLTable>
</>
);
let clearButton = <Button icon="cross" disabled={!searchMask} minimal={true} onClick={() => setSearchMask('')} />
let clearButton = <Button icon="cross" disabled={!searchMask} minimal={true} onClick={() => (searchMask = '')} />;
let noResults = <NonIdealState
icon={'search'}
title={searchMask.length > 2 ? `Нет результатов по запросу "${searchMask}"` : 'Введите запрос'}
/>
let noResults = <NonIdealState icon={'search'} title={searchMask.length > 2 ? `Нет результатов по запросу "${searchMask}"` : 'Введите запрос'} />;
function highlightText(text, query) {
let lastIndex = 0;
@ -100,7 +101,7 @@ const Objects = observer(props => {
if (words.length === 0) {
return [text];
}
const regexp = new RegExp(words.join("|"), "gi");
const regexp = new RegExp(words.join('|'), 'gi');
const tokens = [];
while (true) {
const match = regexp.exec(text);
@ -123,14 +124,14 @@ const Objects = observer(props => {
}
function escapeRegExpChars(text) {
return text.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
return text.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
}
function itemRenderer(item, { modifiers, handleClick, query }) {
return (
<MenuItem
active={modifiers.active}
icon={selectedCats.includes(item) ? "tick" : "blank"}
icon={selectedCats.includes(item) ? 'tick' : 'blank'}
key={item.NAME}
onClick={handleClick}
text={item.NAME}
@ -139,7 +140,7 @@ const Objects = observer(props => {
key={item.ID_CAT}
text={highlightText(item.NAME, query)}
/>
)
);
}
function handleClick(item) {
@ -149,15 +150,15 @@ const Objects = observer(props => {
setCustomServerOk(null);*/
}
const renderTag = (item) => item.NAME;
const renderTag = item => item.NAME;
const handleCatSelect = (item) => {
const handleCatSelect = item => {
if (selectedCats.includes(item)) {
selectedCats.remove(item);
} else {
selectedCats.push(item);
}
}
};
const handleTagRemove = (tag, index) => {
selectedCats.remove(selectedCats.find(c => c.NAME === tag));
@ -165,52 +166,59 @@ const Objects = observer(props => {
const handleItemListPredicate = (query, items) => {
return items.filter(c => c.NAME.toLowerCase().includes(query.toLowerCase()));
}
};
return (<>
<H1>Объекты</H1>
<FormGroup label={'Фильтр'} labelInfo="(минимум 3 символа)">
<ControlGroup>
<InputGroup style={{ width: 500 }}
value={searchMask}
placeholder="Обозначение или наименование"
rightElement={clearButton}
onChange={(e) => setSearchMask(e.target.value)} />
{fetching && <div style={{ marginLeft: 10, display: 'flex', alignItems: 'center' }} ><Spinner size={Spinner.SIZE_SMALL} /></div>}
</ControlGroup>
</FormGroup>
<FormGroup label={'Категории (работает так себе)'}>
<MultiSelect
placeholder={'Выберите категории'}
itemRenderer={itemRenderer}
items={categories}
selectedItems={selectedCats}
noResults={<MenuItem disabled={true} text="Нет категорий" />}
onItemSelect={handleCatSelect}
onItemsPaste={() => { }}
tagRenderer={renderTag}
resetOnSelect={true}
// itemPredicate={handleItemPredicate}
// onActiveItemChange={()=>{}}
itemListPredicate={handleItemListPredicate}
fill={true}
openOnKeyDown={false}
popoverProps={{ minimal: true }}
tagInputProps={{
onRemove: handleTagRemove,
tagProps: {
minimal: true
},
}}
/>
</FormGroup>
<FormGroup>
{true && <Callout intent={Intent.DANGER}>Нет категории {catID}</Callout>}
</FormGroup>
<Button onClick={() => { selectedCats.clear() }}>test</Button>
{(objects.length === 0 || searchMask === '' && !fetching) ? noResults : objsList}
</>
return (
<>
<ul class="bp3-breadcrumbs">
<li>
<span className="bp3-breadcrumb bp3-breadcrumb-current">Объекты</span>
</li>
</ul>{' '}
<FormGroup label={'Фильтр'} labelInfo="(минимум 3 символа)">
<ControlGroup>
<InputGroup style={{ width: 500 }} value={searchMask} placeholder="Обозначение или наименование" rightElement={clearButton} onChange={e => setSearchMask(e.target.value)} />
{fetching && (
<div style={{ marginLeft: 10, display: 'flex', alignItems: 'center' }}>
<Spinner size={Spinner.SIZE_SMALL} />
</div>
)}
</ControlGroup>
</FormGroup>
<FormGroup label={'Категории (работает так себе)'}>
<MultiSelect
placeholder={'Выберите категории'}
itemRenderer={itemRenderer}
items={categories}
selectedItems={selectedCats}
noResults={<MenuItem disabled={true} text="Нет категорий" />}
onItemSelect={handleCatSelect}
onItemsPaste={() => {}}
tagRenderer={renderTag}
resetOnSelect={true}
// itemPredicate={handleItemPredicate}
// onActiveItemChange={()=>{}}
itemListPredicate={handleItemListPredicate}
fill={true}
openOnKeyDown={false}
popoverProps={{ minimal: true }}
tagInputProps={{
onRemove: handleTagRemove,
tagProps: {
minimal: true,
},
}}
/>
</FormGroup>
<FormGroup>{true && <Callout intent={Intent.DANGER}>Нет категории {catID}</Callout>}</FormGroup>
<Button
onClick={() => {
selectedCats.clear();
}}>
test
</Button>
{objects.length === 0 || (searchMask === '' && !fetching) ? noResults : objsList}
</>
);
});

110
src/components/PageEdit.js

@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react';
import { TextArea, Button, MenuItem, FormGroup, InputGroup, H1, ButtonGroup, Intent } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import { Select } from '@blueprintjs/select';
import { useStores } from '../hooks/useStores';
import { createViewModel } from 'mobx-utils';
const PageEdit = observer(props => {
const { rootStore } = useStores();
let { getAllPages, pages, setCurrentPage, currentPage, isPageDirty } = rootStore.pagesStore;
useEffect(() => {
getAllPages();
}, [getAllPages]);
function itemRenderer(item, { modifiers, handleClick, query }) {
return (
<MenuItem
active={modifiers.active}
icon={'document'}
key={item.ID}
onClick={handleClick}
text={isPageDirty(item) ? item.NAME + '*' : item.NAME}
label={item.ID}
shouldDismissPopover
//text={highlightText(item.NAME, query)}
/>
);
}
const handleItemListPredicate = (query, items) => {
return pages.filter(c => c.NAME.toLowerCase().includes(query.toLowerCase()));
};
const handleElementSelect = page => {
setCurrentPage(page);
};
const pageName = () => {
if (currentPage) {
return isPageDirty(currentPage) ? currentPage.NAME + '*' : currentPage.NAME;
} else {
return 'Выберите страницу...';
}
};
return (
<>
<H1>Редактор страниц</H1>
{pages.length && (
<>
<FormGroup>
<Select
items={pages}
itemRenderer={itemRenderer}
onItemSelect={handleElementSelect}
itemPredicate={handleItemListPredicate}
noResults={<MenuItem disabled={true} text="No results." />}>
<Button rightIcon="caret-down" text={pageName()} />
</Select>
</FormGroup>
{currentPage && (
<>
<FormGroup label="Заголовок">
<InputGroup
label="Заголовок"
onChange={e => (currentPage['Заголовок'] = e.target.value)}
value={currentPage['Заголовок']}
/>
</FormGroup>
<FormGroup label="Заголовок" labelInfo={'англ.'}>
<InputGroup
onChange={e => (currentPage['Заголовок en'] = e.target.value)}
value={currentPage['Заголовок en']}
/>
</FormGroup>
<FormGroup label="Услуга">
<InputGroup
onChange={e => (currentPage['Услуга'] = e.target.value)}
value={currentPage['Услуга']}
/>
</FormGroup>
<TextArea
fill={true}
onChange={e => (currentPage['Тело страницы'] = e.target.value)}
value={currentPage['Тело страницы']}
/>
<FormGroup>
<ButtonGroup>
<Button disabled={!currentPage.isDirty} intent={Intent.PRIMARY} onClick={null}>
Сохранить
</Button>
<Button disabled={!currentPage.isDirty} onClick={() => currentPage.reset()}>
Отменить
</Button>
</ButtonGroup>
</FormGroup>
</>
)}
</>
)}
</>
);
});
export default PageEdit;

56
src/components/PrivateRoute.js

@ -1,51 +1,69 @@
import React, { useEffect } from 'react';
import { Route, Redirect, useHistory, } from 'react-router-dom'
import { Route, useHistory } from 'react-router-dom';
import { observer } from 'mobx-react';
import LoginStore from '../stores/LoginStore';
import { Button, Callout, Intent } from '@blueprintjs/core';
import { useStores } from '../hooks/useStores';
const PrivateRoute = observer(({ component: Component, permission, ...rest }) => {
let { isLoggedIn, hasPermission, getUserInfo, isAdmin } = LoginStore;
let history = useHistory();
const { rootStore } = useStores();
let { isLoggedIn, hasPermission, getUserInfo, isAdmin, tryLogin, loading } = rootStore.loginStore;
useEffect(() => {
getUserInfo();
}, [])
}, [getUserInfo]);
const cardStyle = {
width: 400,
alignSelf: 'center',
margin: 'auto'
}
margin: 'auto',
};
let hasPerm = hasPermission(permission);
// TODO сделать спиннер
let alert = <Callout style={cardStyle} intent={Intent.WARNING} >
<p>У вас нет разрешения на доступ к этой странице</p>
{isAdmin && <p>Требуется разрешение <code>{permission}</code></p>}
{<Button onClick={() => history.goBack()}>Назад</Button>}
</Callout >
let alert = (
<Callout style={cardStyle} intent={Intent.WARNING}>
<p>У вас нет разрешения на доступ к этой странице</p>
{isAdmin && (
<p>
Требуется разрешение <code>{permission}</code>
</p>
)}
{<Button onClick={() => history.goBack()}>Назад</Button>}
</Callout>
);
function f(props) {
if (isLoggedIn) {
if (!permission) {
return <Component permission={permission} {...props} />;
} else if (hasPerm) {
return <Component permission={permission} {...props} />
return <Component permission={permission} {...props} />;
} else {
return alert;
}
} else {
return <Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
tryLogin(result => {
if (!result)
return history.push({
pathname: '/login',
state: { from: props.location, reason: 'Сессия истекла' },
});
if (result) {
if (!permission) {
return <Component permission={permission} {...props} />;
} else if (hasPerm) {
return <Component permission={permission} {...props} />;
} else {
return alert;
}
}
});
}
}
return <Route {...rest} render={(props) => f(props)} />
return <Route {...rest} render={props => f(props)} />;
});
export default PrivateRoute;

24
src/components/PublicRoute.js

@ -0,0 +1,24 @@
import React, { useEffect, useState } from 'react';
import { Route } from 'react-router-dom';
import { observer } from 'mobx-react';
import { useStores } from '../hooks/useStores';
const PublicRoute = observer(({ component: Component, permission, ...rest }) => {
const { rootStore } = useStores();
let { tryLogin } = rootStore.loginStore;
const [isLogged, setIsLogged] = useState(undefined);
useEffect(() => {
tryLogin(result => {
setIsLogged(result);
});
}, []);
function f(props) {
return <Component permission={permission} {...props} />;
}
return <Route {...rest} render={props => (isLogged !== undefined) ? f(props) : null} />;
});
export default PublicRoute;

67
src/components/ServerInfo.js

@ -1,67 +0,0 @@
import React, { useEffect } from 'react';
import logo from '../logo.svg';
//import '../App.css';
import { Button, Navbar, NavbarGroup, NavbarHeading, NavbarDivider, Classes, Alignment, Menu, MenuItem, Popover, Position, HTMLTable, H1, H2 } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Info from '../stores/ServerInfo';
const ServerInfo = observer(props => {
const { counter, getData, serverInfo, instanceSettings, getInstanceSettings } = Info;
useEffect(() => {
getInstanceSettings()
});
return (<>
<H1>О сервере</H1>
<H2>Статус</H2>
<HTMLTable>
<thead>
<tr>
<th>Параметр</th>
<th>Значение</th>
</tr>
</thead>
<tbody>
<tr>
<td>Blueprint</td>
<td>{serverInfo.ServerNameFull}</td>
</tr>
<tr>
<td>TSLint</td>
<td>{serverInfo.ServerNameFull}</td>
</tr>
</tbody>
</HTMLTable>
<H2>Настройки</H2>
<HTMLTable>
<thead>
<tr>
<th>Параметр</th>
<th>Значение</th>
</tr>
</thead>
<tbody>
<tr>
<td>Blueprint</td>
<td>{instanceSettings.ServerPrefix}</td>
</tr>
<tr>
<td>TSLint</td>
<td>{serverInfo.ServerNameFull}</td>
</tr>
</tbody>
</HTMLTable>
<button onClick={getData}>inc</button>
<p>{serverInfo.ServerNameFull}</p>
<p>{counter}</p>
<p></p>
</>
);
});
export default ServerInfo;

11
src/components/ServerLog.js

@ -1,10 +1,9 @@
import React, { useEffect, useState } from 'react';
import { TextArea, ControlGroup, FormGroup, Switch, ProgressBar, Breadcrumbs, Breadcrumb, Icon, Button } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Login from '../stores/LoginStore';
import { apiRequest } from '../deepApi';
import { useParams } from 'react-router-dom';
import ServerLogFancy from './ServerLogFancy';
import { useStores } from '../hooks/useStores';
const ServerLog = observer(props => {
@ -13,7 +12,7 @@ const ServerLog = observer(props => {
const [loading, setLoading] = useState(false);
let [percent, setPercent] = useState(0);
const { session } = Login;
const { rootStore } = useStores();
let { filename } = useParams();
@ -29,8 +28,8 @@ const ServerLog = observer(props => {
function getLog() {
setLoading(true);
const req = { FileName: filename };
apiRequest(req, "api-log-getfilecontent", session, (res) => { setLog(res.FileContent); setLoading(false); },
null, null, null, (e) => { setPercent(e / 100) });
rootStore.loginStore.apiAuthRequest(req, "api-log-getfilecontent", (res) => { setLog(res.FileContent); setLoading(false); },
null, null, (e) => { setPercent(e / 100) });
}
const renderCurrentBreadcrumb = ({ text, ...restProps }) => {
@ -63,4 +62,4 @@ const ServerLog = observer(props => {
);
});
export default ServerLog;
export default ServerLog;

18
src/components/ServerLogFancy.js

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { H1, TextArea, HTMLTable, Switch, Icon, FormGroup, ControlGroup, Intent, H6 } from '@blueprintjs/core';
import { HTMLTable, Switch, Icon, Intent, H6 } from '@blueprintjs/core';
import { observer } from 'mobx-react';
const ServerLogFancy = observer(props => {
@ -28,16 +28,16 @@ const ServerLogFancy = observer(props => {
});
if (!showTrace) {
result = result.filter(str => str.level != 'TRACE');
result = result.filter(str => str.level !== 'TRACE');
}
if (!showError) {
result = result.filter(str => str.level != 'ERROR');
result = result.filter(str => str.level !== 'ERROR');
}
if (!showWarn) {
result = result.filter(str => str.level != 'WARN');
result = result.filter(str => str.level !== 'WARN');
}
if (!showInfo) {
result = result.filter(str => str.level != 'INFO');
result = result.filter(str => str.level !== 'INFO');
}
if (revert) {
@ -74,10 +74,10 @@ const ServerLogFancy = observer(props => {
<tbody>
{(fancyLog.map(str => {
return <tr>
<td>{str.level === 'TRACE' && trace}{str.level === 'ERROR' && error}
<td style={{whiteSpace: "nowrap"}}>{str.level === 'TRACE' && trace}{str.level === 'ERROR' && error}
{str.level === 'WARN' && warn}{str.level === 'INFO' && info}</td>
<td>{str.date}</td>
<td>{str.module}</td>
<td style={{whiteSpace: "nowrap"}}>{str.date}</td>
<td style={{whiteSpace: "nowrap"}}>{str.module}</td>
<td>{str.message}</td>
</tr>
}))}
@ -93,4 +93,4 @@ const ServerLogFancy = observer(props => {
);
});
export default ServerLogFancy;
export default ServerLogFancy;

10
src/components/ServerLogs.js

@ -1,18 +1,16 @@
import React, { useEffect, useState } from 'react';
import { HTMLTable, Breadcrumbs, Breadcrumb, Tag } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Login from '../stores/LoginStore';
import { apiRequest } from '../deepApi';
import { useHistory } from 'react-router-dom';
import { useStores } from '../hooks/useStores';
const ServerLogs = observer(props => {
let history = useHistory();
const { rootStore } = useStores();
const [logs, setLogs] = useState([]);
const { session } = Login;
useEffect(() => {
getLogs();
console.log(logs)
@ -24,7 +22,7 @@ const ServerLogs = observer(props => {
function getLogs() {
const req = { AllFiles: true };
apiRequest(req, "api-log-getfileslist", session, (res) => { setLogs(res.FilesList) });
rootStore.loginStore.apiAuthRequest(req, "api-log-getfileslist", (res) => { setLogs(res.FilesList) });
}
function formatBytes(bytes, decimals = 2) {
@ -67,4 +65,4 @@ const ServerLogs = observer(props => {
);
});
export default ServerLogs;
export default ServerLogs;

52
src/components/TopNavbar.js

@ -1,51 +1,46 @@
import React from 'react';
import { Button, Navbar, NavbarGroup, NavbarHeading, NavbarDivider, Classes, Alignment, Menu, MenuItem, Popover, Position } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import LoginStore from '../stores/LoginStore';
import { useHistory, useLocation } from 'react-router-dom';
import { DEEP_SERVER, changeServerToDefault } from '../deepApi';
import Info from '../stores/ServerInfo';
import config from '../config';
import LocalStore from '../stores/LocalStore';
import { useStores } from '../hooks/useStores';
const TopNavbar = observer(props => {
let { logout, isLoggedIn, username } = LoginStore;
let { switchTheme, darkTheme } = LocalStore;
let history = useHistory();
let location = useLocation();
const { getData, serverInfo, reset } = Info;
const { rootStore } = useStores();
const { server, logout, isLoggedIn, username, logoutToDefault } = rootStore.loginStore;
const { serverInfo } = rootStore.infoStore;
let { switchTheme, darkTheme } = rootStore.localStore;
const profileMenu = (
<Menu>
<MenuItem icon="user" text="Профиль" onClick={() => history.push("/profile")} />
{DEEP_SERVER !== config.api && <MenuItem icon="log-out" text={`Вернуться на ${getHostname(config.api)}`} onClick={handleLogoutToDefault} />}
<MenuItem icon="user" text="Профиль" onClick={() => history.push('/profile')} />
{server !== config.api && <MenuItem icon="log-out" text={`Вернуться на ${getHostname(config.api)}`} onClick={handleLogoutToDefault} />}
<MenuItem icon="log-out" text="Выйти" onClick={handleLogout} />
</Menu>);
</Menu>
);
const authMenu = (
<Popover content={profileMenu} position={Position.BOTTOM}>
<Button className={Classes.MINIMAL} icon="user" text={username} rightIcon="caret-down" />
</Popover>
)
);
const unAuth = <>
{(location.pathname === "/login" || location.pathname === "/clogin") ? null :
<Button className={Classes.MINIMAL} icon="user" text="Войти" onClick={() => history.push("/login")} />}
</>
const unAuth = (
<>{location.pathname === '/login' || location.pathname === '/clogin' ? null : <Button className={Classes.MINIMAL} icon="user" text="Войти" onClick={() => history.push('/login')} />}</>
);
function handleLogoutToDefault() {
changeServerToDefault();
reset();
getData();
handleLogout();
logoutToDefault();
history.push('/');
}
function handleLogout() {
logout();
history.push("/");
history.push('/');
}
function getHostname(url) {
@ -54,16 +49,21 @@ const TopNavbar = observer(props => {
}
return (
<Navbar className={"bp3-dark"}>
<Navbar className={'bp3-dark'}>
<NavbarGroup align={Alignment.LEFT}>
<NavbarHeading><div>👀 ASIP</div><div className={"bp3-text-muted bp3-text-small"}>{getHostname(DEEP_SERVER)}</div></NavbarHeading>
<NavbarHeading>
<div>
<img src="../images/dew_logo.svg" alt="" height="18px" style={{ marginBottom: '-3px' }}></img>
</div>
<div className={'bp3-text-muted bp3-text-small'}>{getHostname(server)}</div>
</NavbarHeading>
<NavbarDivider />
<Button className={Classes.MINIMAL} icon="home" text="Главная" onClick={() => history.push("/")} />
<Button className={Classes.MINIMAL} icon="home" text="Главная" onClick={() => history.push('/')} />
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Button className={Classes.MINIMAL} icon={darkTheme ? 'flash' : 'moon'} onClick={() => switchTheme()} />
{isLoggedIn && <NavbarDivider />}
{isLoggedIn ? authMenu : (!!serverInfo.ServerNameFull ? unAuth : null)}
{isLoggedIn ? authMenu : !!serverInfo.ServerNameFull ? unAuth : null}
</NavbarGroup>
</Navbar>
);

12
src/components/UnAuthPage.js

@ -1,16 +1,16 @@
import React, { useEffect, useState } from 'react';
import logo from '../logo.svg';
//import '../App.css';
import React from 'react';
import { Button, Card, H5, Intent, Callout, Spinner } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Info from '../stores/ServerInfo';
import { useHistory, useLocation, Link } from 'react-router-dom';
import { useHistory } from 'react-router-dom';
import config from '../config';
import { useStores } from '../hooks/useStores';
const UnAuthPage = observer(props => {
let history = useHistory();
const { counter, serverStatus, serverInfo, fetchingData } = Info;
const { rootStore } = useStores();
const { serverInfo, fetchingData, serverStatus } = rootStore.infoStore;
const cardStyle = {
width: 400,

10
src/components/UserInfo.js

@ -1,13 +1,13 @@
import React, { useEffect } from 'react';
import { H1, HTMLTable, Tab, Tabs, Tag, Switch } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Login from '../stores/LoginStore';
import LocalStore from '../stores/LocalStore';
import { useStores } from '../hooks/useStores';
const UserInfo = observer(props => {
const { userInfo, getUserInfo, isAdmin, session } = Login;
let { changeThemeOnTime, setChangeThemeOnTime } = LocalStore;
const { rootStore } = useStores();
const { userInfo, getUserInfo, isAdmin, session } = rootStore.loginStore;
let { changeThemeOnTime, setChangeThemeOnTime } = rootStore.localStore;
useEffect(() => {
getUserInfo();
@ -85,4 +85,4 @@ const UserInfo = observer(props => {
);
});
export default UserInfo;
export default UserInfo;

9
src/components/Users.js

@ -1,11 +1,12 @@
import React, { useEffect } from 'react';
import { H1, H2, HTMLTable, Tab, Tabs, Tag } from '@blueprintjs/core';
import { H1, HTMLTable, Tag } from '@blueprintjs/core';
import { observer } from 'mobx-react';
import Users from '../stores/UsersStore';
import { useStores } from '../hooks/useStores';
const UserInfo = observer(props => {
const { users, getUsers } = Users;
const { rootStore } = useStores();
const { users, getUsers } = rootStore.usersStore;
useEffect(() => {
getUsers();
@ -52,4 +53,4 @@ const UserInfo = observer(props => {
);
});
export default UserInfo;
export default UserInfo;

4
src/config/config.dev.json

@ -1,4 +1,4 @@
{
"api": "http://citadel.tias.pro:48910",
"api": "https://citadel.tias.pro",
"allowMultiLogin": true
}
}

6
src/contexts/storeContext.js

@ -0,0 +1,6 @@
import React from 'react';
import RootStore from '../stores/RootStore';
export const storesContext = React.createContext({
rootStore: new RootStore(),
});

4
src/hooks/useStores.js

@ -0,0 +1,4 @@
import React from 'react'
import { storesContext } from '../contexts/storeContext'
export const useStores = () => React.useContext(storesContext)

15
src/stores/CallsStore.js

@ -1,17 +1,19 @@
import { observable, action, decorate } from 'mobx';
import { apiRequest } from '../deepApi'
import LoginStore from './LoginStore';
class CallsStore {
objects = [];
constructor(rootStore) {
this.rootStore = rootStore;
}
getAllCalls = () => {
let { session } = LoginStore;
this.objects.clear();
const req = { GetAll: true, CategoryID: 10004 };
apiRequest(req, "api-objects-get", session, (res) => {
this.rootStore.loginStore.apiAuthRequest(req, "api-objects-get", (res) => {
res.Objects.filter(o => [1004, 1005, 1006].includes(o.ID_TYPE)).forEach(o => {
const req = { CategoryID: o.ID_CAT, ObjectID: o.ID };
apiRequest(req, "api-objects-getpars", session, (res) => {
this.rootStore.loginStore.apiAuthRequest(req, "api-objects-getpars", (res) => {
o.Pars = res.Pars;
this.objects.push(o);
console.log(o.ID);
@ -24,6 +26,7 @@ class CallsStore {
decorate(CallsStore, {
objects: observable,
getAllObjectsByCatID: action,
getAllCalls: action,
});
export default new CallsStore();
export default CallsStore;

35
src/stores/ServerInfo.js → src/stores/InfoStore.js

@ -1,22 +1,21 @@
import { observable, action, decorate } from 'mobx';
import { apiUnAuthRequest, apiRequest } from '../deepApi'
import LoginStore from './LoginStore';
class Info {
class InfoStore {
serverInfo = {};
instanceSettings = {}
instanceSettings = {};
counter = 10;
serverStatus;
fetchingData = false;
constructor() {
this.getData();
constructor(rootStore) {
this.rootStore = rootStore;
this.getServerInfo();
}
getData = () => {
getServerInfo = () => {
this.fetchingData = true;
this.counter++;
apiUnAuthRequest(null, "sys-status", (res) => {
this.rootStore.loginStore.apiUnAuthRequest(null, 'sys-status', res => {
this.serverInfo = res;
if (!!this.serverInfo.ServerNameFull) {
@ -24,29 +23,31 @@ class Info {
}
this.fetchingData = false;
});
}
};
getInstanceSettings = () => {
let { session } = LoginStore;
let { session } = this.rootStore.loginStore;
this.counter++;
apiRequest(null, "api-settings-instance-get", session, (res) => { this.instanceSettings = res });
}
this.rootStore.loginStore.apiRequest(null, 'api-settings-instance-get', session, res => {
this.instanceSettings = res;
});
};
reset = () => {
this.serverInfo = {};
this.instanceSettings = {}
this.instanceSettings = {};
this.counter = 10;
this.serverStatus = undefined;
}
};
}
decorate(Info, {
decorate(InfoStore, {
serverInfo: observable,
counter: observable,
serverStatus: observable,
fetchingData: observable,
getData: action,
getServerInfo: action,
reset: action,
});
export default new Info();
export default InfoStore;

21
src/stores/LikesStore.js

@ -1,21 +0,0 @@
import { observable, action, decorate } from 'mobx';
class ValueStore {
count = 0;
decrease = () => {
this.counter = --this.count;
}
increase = () => {
this.counter = ++this.count;
}
}
decorate(ValueStore, {
count: observable,
decrease: action,
increase: action,
});
const Store = new ValueStore();
export default Store;

6
src/stores/LocalStore.js

@ -7,7 +7,9 @@ class LocalStore {
time = moment();
constructor(period = 1000 * 60) {
constructor(rootStore, period = 1000 * 60) {
this.rootStore = rootStore
this.interval = setInterval(() => this.clockTick(), period);
if (localStorage.getItem('theme') === 'dark') {
this.darkTheme = true;
@ -69,4 +71,4 @@ decorate(LocalStore, {
changeThemeOnTime: observable
});
export default new LocalStore();
export default LocalStore;

188
src/stores/LoginStore.js

@ -1,12 +1,27 @@
import { observable, action, decorate, computed } from 'mobx';
import { apiUnAuthRequest, apiRequest } from '../deepApi'
import config from '../config';
class LoginStore {
username;
password;
session;
server;
userInfo = {};
incorrectPass;
lastError;
loading = false;
constructor(rootStore) {
this.rootStore = rootStore;
this.server = config.api;
const server = localStorage.getItem('server');
if (!!server && server !== config.api) {
this.server = server;
}
}
login = (username, password) => {
this.username = username;
@ -14,46 +29,188 @@ class LoginStore {
const req = {
UserName: this.username,
PassWord: this.password
PassWord: this.password,
};
apiUnAuthRequest(req, "user-login", (res) => {
this.apiUnAuthRequest(req, 'user-login', res => {
if (res.ResultCode === 1) {
this.session = res.SessionID;
localStorage.setItem('username', this.username);
localStorage.setItem('session', this.session);
this.rootStore.infoStore.getServerInfo();
return true;
}
if (res.ResultCode === 6001) {
if (res.ResultCode === 6001 || res.ResultCode === 6000 ) {
this.incorrectPass = true;
return true;
}
return false;
});
}
};
logout = () => {
this.username = undefined;
this.password = undefined;
this.session = undefined;
this.incorrectPass = undefined;
localStorage.removeItem('username');
localStorage.removeItem('session');
};
logoutToDefault = () => {
this.changeServerToDefault();
this.rootStore.infoStore.reset();
this.rootStore.infoStore.getServerInfo();
this.logout();
}
getUserInfo = () => {
apiRequest(null, "user-info", this.session, (res) => { this.userInfo = res });
sleep = (time) => {
return new Promise((resolve) => setTimeout(resolve, time));
}
tryLogin = (callback) => {
this.loading = true;
let session = localStorage.getItem('session');
let username = localStorage.getItem('username');
if (session && username) {
this.apiRequest(null, 'user-info', session, res => {
if (res.ID > 0) {
this.userInfo = res;
this.username = res.U_NAME;
this.session = session;
this.loading = false;
return callback(true);
} else {
this.loading = false;
return callback(false);
}
}, () => {
this.loading = false;
return callback(false);
});
} else {
this.loading = false;
return callback(false);
}
}
getUserInfo = () => {
this.apiRequest(null, 'user-info', this.session, res => {
this.userInfo = res;
});
};
get isLoggedIn() {
return !!this.session;
}
hasPermission = (permission) => {
hasPermission = permission => {
if (!this.userInfo || !this.userInfo.PermissionNames) return false;
return this.userInfo.PermissionNames.includes(permission);
}
};
get isAdmin() {
if (!this.userInfo || !this.userInfo.PermissionNames) return false;
return this.userInfo.PermissionNames.includes('canAdminUsers');
}
changeServerToDefault = () => {
this.server = config.api;
localStorage.removeItem('server');
};
changeServer = server => {
this.server = server;
localStorage.setItem('server', server);
};
apiUnAuthRequest = (reqBody, apiName, callback) => {
return this.apiRequest(reqBody, apiName, null, callback, null, null, this.server);
};
apiUnAuthCustomRequest = (reqBody, apiName, customServer, callback, onError, onDone) => {
return this.apiRequest(reqBody, apiName, null, callback, onError, onDone, customServer);
};
apiAuthRequest = (reqBody, apiName, callback, onError, onDone, onProgress) => {
return this.apiRequest(reqBody, apiName, this.session, callback, onError, onDone, this.server, onProgress);
}
apiRequest = (reqBody, apiName, session, callback, onError, onDone, server, onProgress) => {
let api = !!server ? server : this.server;
console.log(`Call to: ${api}/${apiName}`);
var xhr = new XMLHttpRequest();
try {
xhr.open('POST', `${api}/${apiName}`);
} catch (error) {
if (process.env.NODE_ENV === 'development') {
console.log(error);
}
return;
}
xhr.setRequestHeader('Content-Type', 'application/json');
if (session) {
xhr.setRequestHeader('X-deep-session', session);
}
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var res = JSON.parse(xhr.responseText);
if (process.env.NODE_ENV === 'development') {
console.log(res);
}
if (res === 'OK') {
onError('Сервер вернул "OK"');
} else {
callback(res);
}
} else {
if (process.env.NODE_ENV === 'development') {
console.log(res);
}
if (onError != null) {
if (xhr.status === 0) {
onError('Ошибка соединения');
} else {
onError(xhr.status + ': ' + xhr.statusText);
}
}
}
if (onDone && typeof onDone === 'function') onDone();
}
};
xhr.onerror = () => {
if (onError && typeof onError === 'function') onError('Ошибка соединения');
};
xhr.onprogress = e => {
if (onProgress && typeof onProgress === 'function' && e.lengthComputable) {
var percentComplete = (e.loaded / e.total) * 100;
onProgress(percentComplete);
}
};
if (process.env.NODE_ENV === 'development') {
console.log(reqBody);
}
xhr.send(JSON.stringify(reqBody));
};
}
decorate(LoginStore, {
@ -61,12 +218,23 @@ decorate(LoginStore, {
password: observable,
session: observable,
userInfo: observable,
server: observable,
incorrectPass: observable,
lastError: observable,
login: action,
logout: action,
logoutToDefault: action,
isLoggedIn: computed,
hasPermission: action,
getUserInfo: action,
apiRequest: action,
apiUnAuthRequest: action,
apiAuthRequest: action,
apiUnAuthCustomRequest: action,
changeServerToDefault: action,
changeServer: action,
tryLogin: action,
loading: observable,
});
export default new LoginStore();
export default LoginStore;

96
src/stores/ObjectsStore.js

@ -1,104 +1,110 @@
import { observable, action, decorate, extendObservable, computed } from 'mobx';
import { apiRequest } from '../deepApi'
import LoginStore from './LoginStore';
import { observable, action, decorate, extendObservable, computed, reaction } from 'mobx';
class ObjectsStore {
categories = [];
objects = [];
types = [];
fetching = false;
currentObject;
currentObject = {};
searchMask = '';
constructor() {
this.getCategories();
this.getTypes();
constructor(rootStore) {
this.rootStore = rootStore;
const disposer = reaction(
() => this.rootStore.loginStore.session,
() => {
this.getCategories();
this.getTypes();
}
);
}
getCategories = () => {
let { session } = LoginStore;
const req = { GetAll: true };
apiRequest(req, "api-categories-get", session, (res) => { this.categories = res.Categories });
}
this.rootStore.loginStore.apiAuthRequest(req, 'api-categories-get', res => {
this.categories = res.Categories;
});
};
get getFilteredCategories() {
return this.categories;
}
getTypes = () => {
let { session } = LoginStore;
const req = { GetAll: true };
apiRequest(req, "api-types-get", session, (res) => { this.types = res.ObjectTypes });
}
this.rootStore.loginStore.apiAuthRequest(req, 'api-types-get', res => {
this.types = res.ObjectTypes;
});
};
getAllObjects = () => {
let { session } = LoginStore;
const req = { GetAll: true, CategoryID: 10001 };
this.fetching = true;
apiRequest(req, "api-objects-get", session, (res) => { this.objects = res.Objects; this.fetching = false; });
}
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-get', res => {
this.objects = res.Objects;
this.fetching = false;
});
};
getAllObjectsByCatID = (catID) => {
let { session } = LoginStore;
getAllObjectsByCatID = catID => {
const req = { GetAll: true, CategoryID: catID };
this.fetching = true;
apiRequest(req, "api-objects-get", session, (res) => {
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-get', res => {
res.Objects.forEach(o => {
const req = { CategoryID: o.ID_CAT, ObjectID: o.ID };
apiRequest(req, "api-objects-getpars", session, (res) => {
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-getpars', res => {
o.Pars = res.Pars;
this.objects.push(o);
});
this.fetching = false;
})
});
});
}
};
getObjects = (searchMask, catIDs) => {
let { session } = LoginStore;
this.objects.clear();
this.fetching = true;
const req = { SearchMask: `%${searchMask}%`, CategoryIDs: catIDs };
apiRequest(req, "api-objects-search", session, (res) => {
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-search', res => {
if (res.FoundObjectKeys.length === 0) {
this.fetching = false;
} else {
res.FoundObjectKeys.map(k => {
const req = { GetAll: false, ObjectIDs: [k.ID], CategoryID: k.ID_CAT };
apiRequest(req, "api-objects-get", session, (resObj) => {
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-get', resObj => {
this.objects.push(resObj.Objects[0]);
if (k === res.FoundObjectKeys[res.FoundObjectKeys.length - 1]) {
this.fetching = false;
}
});
})
});
}
});
}
};
getObject = (id, idCat) => {
let { session } = LoginStore;
this.fetching = true;
const req = { GetAll: false, ObjectIDs: id, CategoryID: idCat };
apiRequest(req, "api-objects-get", session, (resObj) => {
const req = { GetAll: false, ObjectIDs: [id], CategoryID: idCat };
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-get', resObj => {
this.currentObject = resObj.Objects[0];
this.fetching = false;
});
}
};
getPars = (object) => {
let { session } = LoginStore;
const req = { CategoryID: object.ID_CAT, ObjectID: object.ID };
apiRequest(req, "api-objects-getpars", session, (res) => {
let o = this.objects.find(o => o === object);
if (o) {
extendObservable(o, res);
}
getPars = (id, idCat) => {
const req = { CategoryID: idCat, ObjectID: id };
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-getpars', res => {
extendObservable(this.currentObject, res);
});
}
};
getTypeByID = (id) => {
getTypeByID = id => {
return this.types.filter(t => t.ID_TYPE === id)[0];
};
setSearchMask = input => {
this.searchMask = input;
}
}
@ -116,7 +122,9 @@ decorate(ObjectsStore, {
getAllObjectsByCatID: action,
getObject: action,
currentObject: observable,
getFilteredCategories: computed
getFilteredCategories: computed,
searchMask: observable,
setSearchMask: action,
});
export default new ObjectsStore();
export default ObjectsStore;

62
src/stores/PagesStore.js

@ -0,0 +1,62 @@
import { observable, action, decorate } from 'mobx';
import { createViewModel } from 'mobx-utils';
class PagesStore {
pages = [];
pagesVMs = [];
currentPage = null;
constructor(rootStore) {
this.rootStore = rootStore;
}
getAllPages = () => {
this.pages.clear();
//this.pagesVMs.clear();
const req = { GetAll: true, CategoryID: 10001 };
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-get', res => {
res.Objects.filter(o => [1002].includes(o.ID_TYPE)).forEach(o => {
this.pages = [...this.pages, o];
});
});
};
getPars = (page, callback) => {
const req = { CategoryID: page.ID_CAT, ObjectID: page.ID };
this.rootStore.loginStore.apiAuthRequest(req, 'api-objects-getpars', res => {
callback(res.Pars);
});
};
setCurrentPage = page => {
window.c = this.pagesVMs;
if (this.pagesVMs.filter(p => p.ID === page.ID).length > 0) {
this.currentPage = this.pagesVMs.filter(p => p.ID === page.ID)[0];
} else {
this.getPars(page, pars => {
Object.assign(page, pars);
let vm = createViewModel(page);
this.pagesVMs = [...this.pagesVMs, vm];
this.currentPage = vm;
});
}
window.cc = this.currentPage;
};
isPageDirty = page => {
if (this.pagesVMs.filter(p => p.ID === page.ID).length > 0) {
return this.pagesVMs.filter(p => p.ID === page.ID)[0].isDirty;
}
return false;
};
}
decorate(PagesStore, {
pages: observable,
currentPage: observable,
setCurrentPage: action,
getAllPages: action,
isPageDirty: action,
});
export default PagesStore;

22
src/stores/RootStore.js

@ -0,0 +1,22 @@
import LoginStore from './LoginStore';
import ServersStore from './ServersStore';
import InfoStore from './InfoStore';
import LocalStore from './LocalStore';
import UsersStore from './UsersStore';
import CallsStore from './CallsStore';
import ObjectsStore from './ObjectsStore';
import PagesStore from './PagesStore';
class RootStore {
constructor() {
this.loginStore = new LoginStore(this);
this.serversStore = new ServersStore(this);
this.infoStore = new InfoStore(this);
this.localStore = new LocalStore(this);
this.usersStore = new UsersStore(this);
this.callsStore = new CallsStore(this);
this.objectsStore = new ObjectsStore(this);
this.pagesStore = new PagesStore(this);
}
}
export default RootStore;

44
src/stores/ServersStore.js

@ -1,14 +1,18 @@
import { observable, action, decorate } from 'mobx';
import { apiUnAuthCustomRequest } from '../deepApi'
import { apiUnAuthCustomRequest } from '../deepApi.js';
class ServerStore {
servers = [];
constructor(rootStore) {
this.rootStore = rootStore;
}
getServers = () => {
let s = JSON.parse(localStorage.getItem('servers'));
if (!Array.isArray(s)) return;
this.servers = s;
}
};
addServer = (server, status, message) => {
//toto pамена
@ -23,20 +27,20 @@ class ServerStore {
});
this.checkServer(server);
localStorage.setItem('servers', JSON.stringify(this.servers));
}
};
clearServers = () => {
this.servers = [];
localStorage.setItem('servers', JSON.stringify(this.servers));
}
};
removeServer = (server) => {
removeServer = server => {
let foundIndex = this.servers.findIndex(x => x.server === server.server);
this.servers.splice(foundIndex, 1)
this.servers.splice(foundIndex, 1);
localStorage.setItem('servers', JSON.stringify(this.servers));
}
};
checkServer = (server) => {
checkServer = server => {
let checked = {
server: server,
date: new Date().toUTCString(),
@ -44,21 +48,31 @@ class ServerStore {
let msg;
apiUnAuthCustomRequest(null, "sys-status", checked.server,
(res) => { checked.status = true; msg = res.ServerNameShort },
(err) => { checked.status = false; msg = err },
apiUnAuthCustomRequest(
null,
'sys-status',
checked.server,
res => {
checked.status = true;
msg = res.ServerNameShort;
},
err => {
checked.status = false;
msg = err;
},
() => {
checked.result = msg;
let foundIndex = this.servers.findIndex(x => x.server == checked.server);
this.servers[foundIndex] = checked;
localStorage.setItem('servers', JSON.stringify(this.servers));
});
}
}
);
};
checkServers = () => {
let checked = this.servers.map(s => this.checkServer(s.server));
localStorage.setItem('servers', JSON.stringify(checked));
}
};
}
decorate(ServerStore, {
@ -71,4 +85,4 @@ decorate(ServerStore, {
checkServers: action,
});
export default new ServerStore();
export default ServerStore;

11
src/stores/UsersStore.js

@ -1,16 +1,17 @@
import { observable, action, decorate } from 'mobx';
import { apiUnAuthRequest, apiRequest } from '../deepApi'
import LoginStore from './LoginStore';
class UsersStore {
users = {
Users: []
};
constructor(rootStore) {
this.rootStore = rootStore;
}
getUsers = () => {
let { session } = LoginStore;
const req = { GetAll: true };
apiRequest(req, "api-users-get", session, (res) => { this.users = res });
this.rootStore.loginStore.apiAuthRequest(req, "api-users-get", (res) => { this.users = res });
}
}
@ -19,4 +20,4 @@ decorate(UsersStore, {
getUsers: action
});
export default new UsersStore();
export default UsersStore;

3
src/utils/dateTime.js

@ -0,0 +1,3 @@
import moment from 'moment';
export const dateTime = str => moment(str).format('DD.MM.YY HH:mm:ss')
Loading…
Cancel
Save