Егор Ермохин
4 years ago
43 changed files with 2516 additions and 2155 deletions
@ -0,0 +1,12 @@
|
||||
{ |
||||
"printWidth": 120, |
||||
"tabWidth": 4, |
||||
"semi": true, |
||||
"singleQuote": true, |
||||
"jsxSingleQuote": false, |
||||
"bracketSpacing": true, |
||||
"jsxBracketSameLine": true, |
||||
"arrowParens": "avoid", |
||||
"endOfLine":"lf", |
||||
"trailingComma": "es5" |
||||
} |
@ -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; |
||||
|
@ -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; |
||||
|
@ -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; |
||||
|
@ -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; |
@ -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} → {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> «{currentObject.NAME}» |
||||
</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; |
||||
|
@ -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; |
@ -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; |
@ -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; |
@ -1,4 +1,4 @@
|
||||
{ |
||||
"api": "http://citadel.tias.pro:48910", |
||||
"api": "https://citadel.tias.pro", |
||||
"allowMultiLogin": true |
||||
} |
||||
} |
||||
|
@ -0,0 +1,6 @@
|
||||
import React from 'react'; |
||||
import RootStore from '../stores/RootStore'; |
||||
|
||||
export const storesContext = React.createContext({ |
||||
rootStore: new RootStore(), |
||||
}); |
@ -0,0 +1,4 @@
|
||||
import React from 'react' |
||||
import { storesContext } from '../contexts/storeContext' |
||||
|
||||
export const useStores = () => React.useContext(storesContext) |
@ -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; |
@ -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; |
@ -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; |
Loading…
Reference in new issue