Kurmansky
3 years ago
45 changed files with 16683 additions and 15701 deletions
@ -0,0 +1,11 @@ |
|||||||
|
export interface ICreateFactory { |
||||||
|
parentId?: number; |
||||||
|
description?: string; |
||||||
|
directorId: number; |
||||||
|
name: string; |
||||||
|
shortName: string; |
||||||
|
} |
||||||
|
|
||||||
|
export interface IUpdateFactory extends ICreateFactory { |
||||||
|
id: number; |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import { ICreateFactory, IUpdateFactory } from "./requests.interface"; |
||||||
|
import { ApiResponse } from "../http.types"; |
||||||
|
import http from "../http.service"; |
||||||
|
import { IFactory } from "@/shared"; |
||||||
|
|
||||||
|
const fetchFactories = (): ApiResponse<IFactory[]> => { |
||||||
|
return http.get<IFactory[]>("factories", null); |
||||||
|
}; |
||||||
|
|
||||||
|
const createFactory = (payload: ICreateFactory): ApiResponse<IFactory> => { |
||||||
|
return http.post<IFactory>("factories", payload); |
||||||
|
}; |
||||||
|
const updateFactory = (payload: IUpdateFactory): ApiResponse<IFactory> => { |
||||||
|
return http.put<IFactory>("factories", payload); |
||||||
|
}; |
||||||
|
|
||||||
|
const deleteFactory = (id: number): ApiResponse<void> => { |
||||||
|
return http.delete<void>(`factories/${id}`); |
||||||
|
}; |
||||||
|
|
||||||
|
export const factoriesApi = { |
||||||
|
fetchFactories, |
||||||
|
createFactory, |
||||||
|
updateFactory, |
||||||
|
deleteFactory, |
||||||
|
}; |
@ -1,3 +1,4 @@ |
|||||||
export * from './auth/requests' |
export * from "./auth/requests"; |
||||||
export * from './account/requests' |
export * from "./account/requests"; |
||||||
export * from './taxonomies/requests' |
export * from "./taxonomies/requests"; |
||||||
|
export * from "./factories/requests"; |
||||||
|
@ -1,8 +1,6 @@ |
|||||||
|
|
||||||
|
|
||||||
export const config = { |
export const config = { |
||||||
apiUrl: 'http://localhost:3000/admin', |
apiUrl: "http://185.69.154.136:5000/admin/", |
||||||
socketUrl: 'http://localhost:3000', |
socketUrl: "http://localhost:3000", |
||||||
} |
}; |
||||||
|
|
||||||
export default config |
export default config; |
||||||
|
@ -1,64 +1,75 @@ |
|||||||
import config from './index'; |
import config from "./index"; |
||||||
import moment from 'moment'; |
import moment from "moment"; |
||||||
var restClient = require('../lib/rest-client'); |
var restClient = require("../lib/rest-client"); |
||||||
|
|
||||||
function checkHttpStatus(response) { |
function checkHttpStatus(response) { |
||||||
if (response.status >= 200 && response.status < 300) { |
if (response.status >= 200 && response.status < 300) { |
||||||
return response |
return response; |
||||||
} else { |
} else { |
||||||
// let error = new Error(response ? (response.statusText || 'Error') : 'Error');
|
// let error = new Error(response ? (response.statusText || 'Error') : 'Error');
|
||||||
// error.response = response;
|
// error.response = response;
|
||||||
return Promise.resolve().then(() => { |
return Promise.resolve() |
||||||
return response.json(); |
.then(() => { |
||||||
}).then(res => { |
return response.json(); |
||||||
res.status = response.status; |
}) |
||||||
return Promise.reject(res) |
.then((res) => { |
||||||
}); |
res.status = response.status; |
||||||
|
return Promise.reject(res); |
||||||
|
}); |
||||||
|
|
||||||
return Promise.reject(response) |
return Promise.reject(response); |
||||||
// throw error;
|
// throw error;
|
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
function makeRequest({ url, method, headers, bodyJSObject }) { |
function makeRequest({ url, method, headers, bodyJSObject }) { |
||||||
let header_tmp = Object.assign({}, this.headers, headers); |
let header_tmp = Object.assign({}, this.headers, headers); |
||||||
|
|
||||||
let body = bodyJSObject ? JSON.stringify(bodyJSObject) : undefined; |
let body = bodyJSObject ? JSON.stringify(bodyJSObject) : undefined; |
||||||
if (header_tmp.hasOwnProperty('Content-Type') && header_tmp['Content-Type'] == 'multipart/form-data') { |
if ( |
||||||
body = bodyJSObject; |
header_tmp.hasOwnProperty("Content-Type") && |
||||||
delete header_tmp['Accept']; |
header_tmp["Content-Type"] == "multipart/form-data" |
||||||
delete header_tmp['Content-Type']; |
) { |
||||||
} |
body = bodyJSObject; |
||||||
return fetch(url, |
delete header_tmp["Accept"]; |
||||||
{ |
delete header_tmp["Content-Type"]; |
||||||
method: method || "GET", |
} |
||||||
headers: header_tmp, |
return fetch(url, { |
||||||
body: body, |
method: method || "GET", |
||||||
mode: 'cors' |
headers: header_tmp, |
||||||
}).then(checkHttpStatus).then(response => { |
body: body, |
||||||
return response.json(); |
mode: "cors", |
||||||
}); |
}) |
||||||
|
.then(checkHttpStatus) |
||||||
|
.then((response) => { |
||||||
|
return response.json(); |
||||||
|
}); |
||||||
} |
} |
||||||
|
|
||||||
let token = ''; |
let token = ""; |
||||||
|
|
||||||
if (localStorage.getItem('remember_me') && localStorage.getItem('remember_me') == 'yes') { |
if ( |
||||||
token = localStorage.getItem('token') |
localStorage.getItem("remember_me") && |
||||||
} else if (localStorage.getItem('expiret_date') && moment(localStorage.getItem('expiret_date')) > moment()) { |
localStorage.getItem("remember_me") == "yes" |
||||||
token = localStorage.getItem('token'); |
) { |
||||||
|
token = localStorage.getItem("token"); |
||||||
|
} else if ( |
||||||
|
localStorage.getItem("expiret_date") && |
||||||
|
moment(localStorage.getItem("expiret_date")) > moment() |
||||||
|
) { |
||||||
|
token = localStorage.getItem("token"); |
||||||
} else { |
} else { |
||||||
token = '' |
token = ""; |
||||||
} |
} |
||||||
|
|
||||||
|
|
||||||
restClient.config({ |
restClient.config({ |
||||||
baseUrl: config.apiUrl + '/api', |
baseUrl: config.apiUrl + "/api", |
||||||
headers: { |
headers: { |
||||||
'Accept': 'application/json', |
Accept: "application/json", |
||||||
'Content-Type': 'application/json', |
"Content-Type": "application/json", |
||||||
'Authorization': 'Bearer ' + token |
Authorization: "Bearer " + token, |
||||||
}, |
}, |
||||||
requestMethod: makeRequest |
requestMethod: makeRequest, |
||||||
}); |
}); |
||||||
|
|
||||||
export default restClient; |
export default restClient; |
||||||
|
@ -1,129 +0,0 @@ |
|||||||
import React, { Component } from 'react' |
|
||||||
import PropTypes from 'prop-types' |
|
||||||
import { connect } from 'react-redux' |
|
||||||
import { Table, Popconfirm } from 'antd'; |
|
||||||
import './style.scss'; |
|
||||||
import { setItemFactory, showModal, deleteFactory } from '../../actions'; |
|
||||||
import { getPermissionCheck } from '../../../../lib/helper'; |
|
||||||
import TableGrid from '../../../../components/TableGrid/TableGrig'; |
|
||||||
|
|
||||||
class DataFactory extends Component { |
|
||||||
constructor(props) { |
|
||||||
super(props) |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
render() { |
|
||||||
let {data, active_factory, profile} = this.props; |
|
||||||
let permissions = profile.permissions || {}; |
|
||||||
const columns = [ |
|
||||||
{ |
|
||||||
name: 'Назва', |
|
||||||
dataIndex: 'name', |
|
||||||
key: 'name', |
|
||||||
resizable: true, |
|
||||||
sortable: true, |
|
||||||
filter: true, |
|
||||||
}, |
|
||||||
{ |
|
||||||
name: 'Дії', |
|
||||||
key: 'action', |
|
||||||
width: 100, |
|
||||||
formatter: ({row}) => ( |
|
||||||
<span> |
|
||||||
{getPermissionCheck('factory', 'update', profile) ? <a className="lnr lnr-pencil" href='javascript:;' onClick={() => { |
|
||||||
this.props.setItemFactory(row); |
|
||||||
this.props.showModal(true); |
|
||||||
}}></a> : null} |
|
||||||
<span style={{padding: '10px'}}> </span> |
|
||||||
{getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm |
|
||||||
title="Ви справді хочете видалити?" |
|
||||||
onConfirm={() => {this.props.deleteFactory(row.id)}} |
|
||||||
> |
|
||||||
<a className="lnr lnr-trash" href="javascript:;"></a> |
|
||||||
</Popconfirm> : null} |
|
||||||
</span> |
|
||||||
), |
|
||||||
} |
|
||||||
]; |
|
||||||
|
|
||||||
let clientHeight = document.body.clientHeight - 128 - 52 - 90; |
|
||||||
|
|
||||||
let collumsList = [ |
|
||||||
{title: 'Назва', key: 'name'}, |
|
||||||
{title: 'Дії', key: 'action'}, |
|
||||||
|
|
||||||
]; |
|
||||||
|
|
||||||
let defaultCollumsActive = ['name', 'action']; |
|
||||||
|
|
||||||
|
|
||||||
return <div><TableGrid |
|
||||||
data_table={data.filter(item => item.parent_factory == active_factory)} |
|
||||||
columns={columns} |
|
||||||
tableName={'log_users'} |
|
||||||
tableHeight={clientHeight} |
|
||||||
collumsList={collumsList} |
|
||||||
defaultCollumsActive={defaultCollumsActive} |
|
||||||
onRow={(record, rowIndex) => { |
|
||||||
return { |
|
||||||
onDoubleClick: (event) => { |
|
||||||
// history.push('/profile/' + record.id) |
|
||||||
if(getPermissionCheck('factory', 'update', profile)){ |
|
||||||
this.props.setItemFactory(record); |
|
||||||
this.props.showModal(true); |
|
||||||
} |
|
||||||
}, |
|
||||||
|
|
||||||
}; |
|
||||||
}} |
|
||||||
/></div> |
|
||||||
|
|
||||||
return ( |
|
||||||
<div> |
|
||||||
<Table |
|
||||||
dataSource={data.filter(item => item.parent_factory == active_factory)} |
|
||||||
bordered |
|
||||||
pagination={false} |
|
||||||
onRow={(record, rowIndex) => { |
|
||||||
return { |
|
||||||
onClick: (event) => {}, // click row |
|
||||||
onDoubleClick: (event) => { |
|
||||||
// history.push('/profile/' + record.id) |
|
||||||
if(getPermissionCheck('factory', 'update', profile)){ |
|
||||||
this.props.setItemFactory(record); |
|
||||||
this.props.showModal(true); |
|
||||||
} |
|
||||||
}, // double click row |
|
||||||
onContextMenu: (event) => {}, // right button click row |
|
||||||
onMouseEnter: (event) => {}, // mouse enter row |
|
||||||
onMouseLeave: (event) => {}, // mouse leave row |
|
||||||
}; |
|
||||||
}} |
|
||||||
columns={columns} |
|
||||||
rowKey={(record) => record.id} |
|
||||||
/> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
DataFactory.propTypes = { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({ |
|
||||||
is_load: state.factory.is_load, |
|
||||||
data: state.factory.data, |
|
||||||
active_factory: state.factory.active_factory, |
|
||||||
profile: state.auth.profile, |
|
||||||
}) |
|
||||||
|
|
||||||
const mapDispatchToProps = { |
|
||||||
setItemFactory, |
|
||||||
deleteFactory, |
|
||||||
showModal |
|
||||||
} |
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DataFactory) |
|
@ -1,9 +0,0 @@ |
|||||||
.factory { |
|
||||||
.ant-table-wrapper{ |
|
||||||
.ant-empty-image{ |
|
||||||
img{ |
|
||||||
width: auto; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,119 +0,0 @@ |
|||||||
import React, { Component } from 'react' |
|
||||||
import PropTypes from 'prop-types' |
|
||||||
import { connect } from 'react-redux' |
|
||||||
import validationFields from '../../../../lib/validate'; |
|
||||||
import { Field, reduxForm } from 'redux-form'; |
|
||||||
import InputField from '../../../../components/Fields/InputField'; |
|
||||||
import {ButtonToolbar, Button} from 'reactstrap' |
|
||||||
import TreeSelectField from '../../../../components/Fields/TreeSelectField'; |
|
||||||
import { genTree } from '../../../../lib/helper'; |
|
||||||
import _ from 'lodash' |
|
||||||
import SelectField from '../../../../components/Fields/SelectField'; |
|
||||||
import { createFactory, updateFactory, showModal } from '../../actions'; |
|
||||||
import SelectMultyField from '../../../../components/Fields/SelectMultyField'; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FormFactory extends Component { |
|
||||||
constructor(props) { |
|
||||||
super(props) |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
submit = (values) => { |
|
||||||
if(values.id){ |
|
||||||
this.props.updateFactory(values.id, values); |
|
||||||
} else { |
|
||||||
this.props.createFactory(values); |
|
||||||
} |
|
||||||
this.props.showModal(false); |
|
||||||
} |
|
||||||
|
|
||||||
render() { |
|
||||||
let {handleSubmit, factory, users} = this.props; |
|
||||||
return ( |
|
||||||
<form className="form" onSubmit={handleSubmit(this.submit)}> |
|
||||||
<Field |
|
||||||
name='name' |
|
||||||
component={InputField} |
|
||||||
type="text" |
|
||||||
placeholder="Назва" |
|
||||||
label="Назва" |
|
||||||
/> |
|
||||||
<Field |
|
||||||
name='short_name' |
|
||||||
component={InputField} |
|
||||||
type="text" |
|
||||||
placeholder="Коротка назва" |
|
||||||
label="Коротка назва" |
|
||||||
/> |
|
||||||
<Field |
|
||||||
name='parent_factory' |
|
||||||
component={TreeSelectField} |
|
||||||
placeholder="Батьківська категорія" |
|
||||||
label="Батьківська категорія" |
|
||||||
tree={genTree(_.cloneDeep(factory), undefined, undefined, 'parent_factory')} |
|
||||||
/> |
|
||||||
<Field |
|
||||||
name='director' |
|
||||||
component={SelectField} |
|
||||||
placeholder="Директор" |
|
||||||
label="Директор" |
|
||||||
options={users.map(item => ({title: item.name, value: item.id}))} |
|
||||||
/> |
|
||||||
<Field |
|
||||||
name='director_permissions' |
|
||||||
component={SelectMultyField} |
|
||||||
placeholder="Права доступу на підприємство" |
|
||||||
label="Права доступу на підприємство" |
|
||||||
options={[ |
|
||||||
{title: 'Перегляд задач', value: 'find'}, |
|
||||||
{title: 'Створення задач', value: 'create'}, |
|
||||||
{title: 'Редагування задач', value: 'update'}, |
|
||||||
{title: 'Видалення задач', value: 'destroy'}, |
|
||||||
]} |
|
||||||
/> |
|
||||||
<ButtonToolbar className="form__button-toolbar"> |
|
||||||
<Button color="primary" type="submit">Зберегти</Button> |
|
||||||
</ButtonToolbar> |
|
||||||
</form> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
FormFactory.propTypes = { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
FormFactory = reduxForm({ |
|
||||||
form: 'form_factory', |
|
||||||
validate: (values) => { |
|
||||||
let fields = [ |
|
||||||
{name: 'name', rules: [{name: 'required', message: 'Заповнити обовязково'}]}, |
|
||||||
// {name: 'password', rules: [{name: 'required', message: 'Заповнити обовязково'}]}, |
|
||||||
] |
|
||||||
|
|
||||||
let error = validationFields(fields, values); |
|
||||||
|
|
||||||
if(values.id == values.parent_factory && values.parent_factory){ |
|
||||||
error.parent_factory = 'Невірна батьківська категорія' |
|
||||||
} |
|
||||||
|
|
||||||
return error; |
|
||||||
|
|
||||||
} |
|
||||||
})(FormFactory); |
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({ |
|
||||||
factory: state.factory.data, |
|
||||||
initialValues: Object.assign({}, state.factory.item_factory) , |
|
||||||
users: state.factory.users, |
|
||||||
}) |
|
||||||
|
|
||||||
const mapDispatchToProps = { |
|
||||||
createFactory, |
|
||||||
updateFactory, |
|
||||||
showModal, |
|
||||||
} |
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(FormFactory) |
|
@ -0,0 +1,167 @@ |
|||||||
|
import React, { FC } from "react"; |
||||||
|
import { ButtonToolbar, Button } from "reactstrap"; |
||||||
|
import _ from "lodash"; |
||||||
|
import { |
||||||
|
IFactory, |
||||||
|
InputField, |
||||||
|
IUser, |
||||||
|
SelectField, |
||||||
|
SelectMultiField, |
||||||
|
} from "@/shared"; |
||||||
|
import { Controller, useForm } from "react-hook-form"; |
||||||
|
import { TreeSelectField } from "@/components/Fields"; |
||||||
|
import { genTree } from "@/lib/helper"; |
||||||
|
import { |
||||||
|
ICreateFactory, |
||||||
|
IUpdateFactory, |
||||||
|
} from "@/api/factories/requests.interface"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
selectedFactory: IFactory; |
||||||
|
factories: IFactory[]; |
||||||
|
users: IUser[]; |
||||||
|
updateFactory: (factory: IUpdateFactory) => void; |
||||||
|
createFactory: (factory: ICreateFactory) => void; |
||||||
|
closeModal: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const FormFactory: FC<IProps> = ({ |
||||||
|
selectedFactory, |
||||||
|
factories, |
||||||
|
users, |
||||||
|
updateFactory, |
||||||
|
createFactory, |
||||||
|
closeModal, |
||||||
|
}) => { |
||||||
|
const { control, handleSubmit } = useForm<Omit<IFactory, "children">>({ |
||||||
|
defaultValues: { ...selectedFactory }, |
||||||
|
}); |
||||||
|
|
||||||
|
const filterParentsTree = ( |
||||||
|
factories: IFactory[], |
||||||
|
selectedFactory: IFactory |
||||||
|
) => { |
||||||
|
const filteredFactories = factories.filter((it) => { |
||||||
|
return it.id !== selectedFactory.id && it.id !== selectedFactory.parentId; |
||||||
|
}); |
||||||
|
|
||||||
|
for (let i; i < filteredFactories.length; i++) { |
||||||
|
if (filteredFactories[i].children.length) { |
||||||
|
filterParentsTree(filteredFactories[i].children, selectedFactory); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return filteredFactories; |
||||||
|
}; |
||||||
|
|
||||||
|
const getParentsTree = () => { |
||||||
|
let parentsTree: any; |
||||||
|
|
||||||
|
if (selectedFactory) { |
||||||
|
const filteredFactories = filterParentsTree(factories, selectedFactory); |
||||||
|
|
||||||
|
parentsTree = genTree( |
||||||
|
filteredFactories, |
||||||
|
undefined, |
||||||
|
undefined, |
||||||
|
"parentId" |
||||||
|
); |
||||||
|
} else { |
||||||
|
parentsTree = genTree(factories, undefined, undefined, "parentId"); |
||||||
|
} |
||||||
|
|
||||||
|
return parentsTree; |
||||||
|
}; |
||||||
|
|
||||||
|
const submit = (values) => { |
||||||
|
if (values.id) { |
||||||
|
updateFactory(values); |
||||||
|
} else { |
||||||
|
createFactory(values); |
||||||
|
} |
||||||
|
closeModal(); |
||||||
|
}; |
||||||
|
|
||||||
|
const parentCategoryTree = getParentsTree(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<form className="form" onSubmit={handleSubmit(submit)}> |
||||||
|
<Controller |
||||||
|
name="name" |
||||||
|
control={control} |
||||||
|
render={({ field: { value, onChange } }) => ( |
||||||
|
<InputField |
||||||
|
placeholder={"Назва"} |
||||||
|
label={"Назва"} |
||||||
|
onChange={onChange} |
||||||
|
value={value} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
|
||||||
|
<Controller |
||||||
|
name="shortName" |
||||||
|
control={control} |
||||||
|
render={({ field: { value, onChange } }) => ( |
||||||
|
<InputField |
||||||
|
placeholder={"Коротка назва"} |
||||||
|
label={"Коротка назва"} |
||||||
|
onChange={onChange} |
||||||
|
value={value} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
|
||||||
|
<Controller |
||||||
|
name="parentId" |
||||||
|
control={control} |
||||||
|
render={({ field: { onChange, onBlur, value, ref } }) => ( |
||||||
|
<TreeSelectField |
||||||
|
register={{ onChange, onBlur, ref, value }} |
||||||
|
label="Батьківська категорія" |
||||||
|
placeholder="Батьківська категорія" |
||||||
|
tree={parentCategoryTree} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
|
||||||
|
<Controller |
||||||
|
name="directorId" |
||||||
|
control={control} |
||||||
|
defaultValue={5} |
||||||
|
render={({ field: { onChange } }) => ( |
||||||
|
<SelectField |
||||||
|
onChange={onChange} |
||||||
|
label="Директор" |
||||||
|
placeholder="Директор" |
||||||
|
options={users} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
|
||||||
|
<Controller |
||||||
|
name="usersAccessPermission" |
||||||
|
control={control} |
||||||
|
render={({ field: { onChange } }) => ( |
||||||
|
<SelectMultiField |
||||||
|
onChange={onChange} |
||||||
|
label="Права доступу на підприємство" |
||||||
|
placeholder="Права доступу на підприємство" |
||||||
|
options={[ |
||||||
|
{ title: "Перегляд задач", value: "find" }, |
||||||
|
{ title: "Створення задач", value: "create" }, |
||||||
|
{ title: "Редагування задач", value: "update" }, |
||||||
|
{ title: "Видалення задач", value: "destroy" }, |
||||||
|
]} |
||||||
|
/> |
||||||
|
)} |
||||||
|
/> |
||||||
|
|
||||||
|
<ButtonToolbar className="form__button-toolbar"> |
||||||
|
<Button color="primary" type="submit"> |
||||||
|
Зберегти |
||||||
|
</Button> |
||||||
|
</ButtonToolbar> |
||||||
|
</form> |
||||||
|
); |
||||||
|
}; |
@ -1,69 +0,0 @@ |
|||||||
import React, { Component } from 'react' |
|
||||||
import PropTypes from 'prop-types' |
|
||||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; |
|
||||||
import { connect } from 'react-redux' |
|
||||||
import { showModal } from '../../actions'; |
|
||||||
import FormFactory from '../FormFactory' |
|
||||||
|
|
||||||
class ModalForm extends Component { |
|
||||||
constructor(props) { |
|
||||||
super(props) |
|
||||||
} |
|
||||||
|
|
||||||
componentWillMount() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentDidMount() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentWillUnmount() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
toggle = () => { |
|
||||||
this.props.showModal(false) |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
render() { |
|
||||||
let {show_modal} = this.props; |
|
||||||
let color = 'primary'; |
|
||||||
return ( |
|
||||||
<Modal |
|
||||||
isOpen={show_modal} |
|
||||||
toggle={this.toggle} |
|
||||||
className={`modal-dialog--${color} modal-dialog--header`} |
|
||||||
> |
|
||||||
<div className="modal__header"> |
|
||||||
<button className="lnr lnr-cross modal__close-btn" type="button" onClick={this.toggle} /> |
|
||||||
<h4 className="bold-text modal__title" style={{color: 'white'}}>Підприємство</h4> |
|
||||||
</div> |
|
||||||
<div className="modal__body"> |
|
||||||
<FormFactory/> |
|
||||||
</div> |
|
||||||
</Modal> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ModalForm.propTypes = { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({ |
|
||||||
show_modal: state.factory.show_modal, |
|
||||||
}) |
|
||||||
|
|
||||||
const mapDispatchToProps = { |
|
||||||
showModal |
|
||||||
} |
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ModalForm) |
|
@ -0,0 +1,57 @@ |
|||||||
|
import { |
||||||
|
ICreateFactory, |
||||||
|
IUpdateFactory, |
||||||
|
} from "@/api/factories/requests.interface"; |
||||||
|
import { IFactory, Loader } from "@/shared"; |
||||||
|
import React, { FC } from "react"; |
||||||
|
import { Modal } from "reactstrap"; |
||||||
|
import { FormFactory } from "../FormFactory"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
isOpen: boolean; |
||||||
|
selectedFactory: IFactory; |
||||||
|
factories: IFactory[]; |
||||||
|
closeModal: () => void; |
||||||
|
createFactory: (factory: ICreateFactory) => void; |
||||||
|
updateFactory: (factory: IUpdateFactory) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const ModalForm: FC<IProps> = ({ |
||||||
|
isOpen, |
||||||
|
selectedFactory, |
||||||
|
factories, |
||||||
|
closeModal, |
||||||
|
createFactory, |
||||||
|
updateFactory, |
||||||
|
}) => { |
||||||
|
return ( |
||||||
|
<Modal |
||||||
|
isOpen={isOpen} |
||||||
|
toggle={closeModal} |
||||||
|
className={`modal-dialog--primary modal-dialog--header`} |
||||||
|
> |
||||||
|
<div className="modal__header"> |
||||||
|
<button |
||||||
|
className="lnr lnr-cross modal__close-btn" |
||||||
|
type="button" |
||||||
|
onClick={closeModal} |
||||||
|
/> |
||||||
|
|
||||||
|
<h4 className="bold-text modal__title" style={{ color: "white" }}> |
||||||
|
Підприємство |
||||||
|
</h4> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div className="modal__body"> |
||||||
|
<FormFactory |
||||||
|
selectedFactory={selectedFactory} |
||||||
|
factories={factories} |
||||||
|
users={[]} |
||||||
|
closeModal={closeModal} |
||||||
|
updateFactory={updateFactory} |
||||||
|
createFactory={createFactory} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
</Modal> |
||||||
|
); |
||||||
|
}; |
@ -1,167 +0,0 @@ |
|||||||
import React, { Component } from 'react'; |
|
||||||
import PropTypes from 'prop-types'; |
|
||||||
import {Tree, Popconfirm, Input } from 'antd'; |
|
||||||
import { connect } from 'react-redux'; |
|
||||||
import { genTree, getPermissionCheck } from '../../../../lib/helper'; |
|
||||||
import _ from 'lodash'; |
|
||||||
import { selectFactory, setItemFactory, showModal, deleteFactory } from '../../actions'; |
|
||||||
import './style.scss'; |
|
||||||
const { Search } = Input; |
|
||||||
|
|
||||||
|
|
||||||
const getParentKey = (id, tree) => { |
|
||||||
let parentKey; |
|
||||||
for (let i = 0; i < tree.length; i++) { |
|
||||||
const node = tree[i]; |
|
||||||
if (node.children) { |
|
||||||
if (node.children.some(item => item.id === id)) { |
|
||||||
parentKey = node.id; |
|
||||||
} else if (getParentKey(id, node.children)) { |
|
||||||
parentKey = getParentKey(id, node.children); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return parentKey; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TreeFactory extends Component { |
|
||||||
constructor(props) { |
|
||||||
super(props) |
|
||||||
this.state = { |
|
||||||
autoExpandParent: false, |
|
||||||
searchValue: '', |
|
||||||
expandedKeys: [] |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
onSelect = (value) => { |
|
||||||
// if(value[0]){ |
|
||||||
// this.props.selectFactory(value[0]); |
|
||||||
// } else { |
|
||||||
// this.props.selectFactory(null); |
|
||||||
// } |
|
||||||
} |
|
||||||
|
|
||||||
onExpand = expandedKeys => { |
|
||||||
this.setState({ |
|
||||||
expandedKeys, |
|
||||||
autoExpandParent: false, |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
renderTitle = (item) => { |
|
||||||
let {profile} = this.props; |
|
||||||
|
|
||||||
const {searchValue} = this.state; |
|
||||||
|
|
||||||
const index = item.name.indexOf(searchValue); |
|
||||||
const beforeStr = item.name.substr(0, index); |
|
||||||
const afterStr = item.name.substr(index + searchValue.length); |
|
||||||
|
|
||||||
const title = index > -1 ? ( |
|
||||||
<span> |
|
||||||
{beforeStr} |
|
||||||
<span style={{color: 'white', background: '#0cbf00'}}>{searchValue}</span> |
|
||||||
{afterStr} |
|
||||||
</span> |
|
||||||
) : ( |
|
||||||
<span>{item.name}</span> |
|
||||||
); |
|
||||||
|
|
||||||
return <div className="tree-title" onDoubleClick={() => { |
|
||||||
|
|
||||||
console.log("TreeFactory -> renderTitle -> item", item) |
|
||||||
this.props.setItemFactory(item); |
|
||||||
this.props.showModal(true); |
|
||||||
}}> |
|
||||||
{(item.children && item.children.length) && <i className="fas fa-folder"></i>} |
|
||||||
{(!item.children || !item.children.length) && <i className="lnr lnr-apartment"></i>} |
|
||||||
<div className="text">{title}</div> |
|
||||||
{getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm |
|
||||||
title="Ви справді хочете видалити?" |
|
||||||
onConfirm={() => {this.props.deleteFactory(item.id)}} |
|
||||||
> |
|
||||||
<a className="lnr lnr-trash" href="javascript:;"></a> |
|
||||||
</Popconfirm> : null} |
|
||||||
</div> |
|
||||||
} |
|
||||||
|
|
||||||
renderTree = (tree) => { |
|
||||||
if(Array.isArray(tree)){ |
|
||||||
return tree.map(item => <Tree.TreeNode title={this.renderTitle(item)} key={item.id}> |
|
||||||
{item.children ? this.renderTree(item.children) : null} |
|
||||||
</Tree.TreeNode>) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
onChange = (e) => { |
|
||||||
const { value } = e.target; |
|
||||||
let {data} = this.props; |
|
||||||
if(!value){ |
|
||||||
this.setState({ |
|
||||||
searchValue: value, |
|
||||||
expandedKeys: [], |
|
||||||
autoExpandParent: true, |
|
||||||
}); |
|
||||||
return; |
|
||||||
} |
|
||||||
let tree = genTree(_.cloneDeep(data)); |
|
||||||
const expandedKeys = data |
|
||||||
.map(item => { |
|
||||||
if (item.name.indexOf(value) > -1) { |
|
||||||
return getParentKey(item.id, tree); |
|
||||||
} |
|
||||||
return null; |
|
||||||
}) |
|
||||||
.filter((item, i, self) => item && self.indexOf(item) === i); |
|
||||||
this.setState({ |
|
||||||
searchValue: value, |
|
||||||
expandedKeys, |
|
||||||
autoExpandParent: true, |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
render() { |
|
||||||
let {data} = this.props; |
|
||||||
let tree = genTree(_.cloneDeep(data)); |
|
||||||
const { autoExpandParent, expandedKeys } = this.state; |
|
||||||
return ( |
|
||||||
<div className="TreeFactory"> |
|
||||||
<Search style={{ marginBottom: 8, maxWidth: 200 }} placeholder="Search" onChange={this.onChange} /> |
|
||||||
<Tree |
|
||||||
onSelect={this.onSelect} |
|
||||||
showArrow |
|
||||||
autoExpandParent={autoExpandParent} |
|
||||||
onExpand={this.onExpand} |
|
||||||
expandedKeys={expandedKeys} |
|
||||||
> |
|
||||||
{this.renderTree(tree)} |
|
||||||
|
|
||||||
</Tree> |
|
||||||
</div> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
TreeFactory.propTypes = { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({ |
|
||||||
is_load: state.factory.is_load, |
|
||||||
data: state.factory.data, |
|
||||||
profile: state.auth.profile, |
|
||||||
}) |
|
||||||
|
|
||||||
const mapDispatchToProps = { |
|
||||||
selectFactory, |
|
||||||
setItemFactory, |
|
||||||
showModal, |
|
||||||
deleteFactory, |
|
||||||
} |
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(TreeFactory) |
|
@ -0,0 +1,151 @@ |
|||||||
|
import { Input, Popconfirm, Tree } from "antd"; |
||||||
|
import React, { FC, useMemo, useState, useCallback, useEffect } from "react"; |
||||||
|
import { genTree, getPermissionCheck } from "@/lib/helper"; |
||||||
|
import _ from "lodash"; |
||||||
|
import { IFactory, IUser } from "@/shared"; |
||||||
|
import "./style.scss"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
factories: IFactory[]; |
||||||
|
profile: IUser; |
||||||
|
openModal: () => void; |
||||||
|
setSelectedFactory: (factory: IFactory) => void; |
||||||
|
deleteFactory: (id: number) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const TreeFactory: FC<IProps> = ({ |
||||||
|
factories, |
||||||
|
profile, |
||||||
|
openModal, |
||||||
|
deleteFactory, |
||||||
|
setSelectedFactory, |
||||||
|
}) => { |
||||||
|
const [searchVal, setSearchVal] = useState<string>(""); |
||||||
|
const [expandedKeys, setExpandedKeys] = useState<string[]>([]); |
||||||
|
const [autoExpandParent, setAutoExpandParent] = useState<boolean>(false); |
||||||
|
|
||||||
|
const preparedTree = genTree(_.cloneDeep(factories)); |
||||||
|
|
||||||
|
const getParentKey = (id, tree) => { |
||||||
|
let parentKey; |
||||||
|
|
||||||
|
tree.forEach((el) => { |
||||||
|
if (el.children) { |
||||||
|
if (el.children.some((it) => it.id === id)) { |
||||||
|
parentKey = el.id; |
||||||
|
} else if (getParentKey(id, el.children)) { |
||||||
|
parentKey = getParentKey(id, el.children); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return parentKey; |
||||||
|
}; |
||||||
|
|
||||||
|
const findParentKeys = (arr: IFactory[], str: string): string[] => |
||||||
|
arr |
||||||
|
.map((item) => { |
||||||
|
if (item.name.toLowerCase().indexOf(str.toLowerCase()) > -1) { |
||||||
|
const parentKey = getParentKey(item.id, preparedTree); |
||||||
|
|
||||||
|
return parentKey; |
||||||
|
} |
||||||
|
if (item.children.length) return findParentKeys(item.children, str); |
||||||
|
}) |
||||||
|
.filter((item, i, self) => { |
||||||
|
return item && self.indexOf(item) === i; |
||||||
|
}); |
||||||
|
|
||||||
|
const onExpend = (expandedKeys: string[]) => { |
||||||
|
setExpandedKeys(expandedKeys); |
||||||
|
setAutoExpandParent(false); |
||||||
|
}; |
||||||
|
|
||||||
|
const onChange = (value: string) => { |
||||||
|
if (!value) { |
||||||
|
setSearchVal(value); |
||||||
|
setExpandedKeys([]); |
||||||
|
setAutoExpandParent(true); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
const expandedKeys = findParentKeys(factories, value); |
||||||
|
|
||||||
|
setSearchVal(value); |
||||||
|
setAutoExpandParent(true); |
||||||
|
setExpandedKeys(expandedKeys); |
||||||
|
}; |
||||||
|
|
||||||
|
const renderTitle = (item) => { |
||||||
|
const index = item.name.indexOf(searchVal); |
||||||
|
const beforeStr = item.name.substr(0, index); |
||||||
|
const afterStr = item.name.substr(index + searchVal.length); |
||||||
|
|
||||||
|
const title = |
||||||
|
index > -1 ? ( |
||||||
|
<span> |
||||||
|
{beforeStr} |
||||||
|
<span style={{ color: "white", background: "#0cbf00" }}> |
||||||
|
{searchVal} |
||||||
|
</span> |
||||||
|
{afterStr} |
||||||
|
</span> |
||||||
|
) : ( |
||||||
|
<span>{item.name}</span> |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div |
||||||
|
className="tree-title" |
||||||
|
onClick={() => { |
||||||
|
setSelectedFactory(item); |
||||||
|
}} |
||||||
|
> |
||||||
|
<div style={{ display: "flex" }} onClick={openModal}> |
||||||
|
{item.children.length ? <i className="fas fa-folder" /> : null} |
||||||
|
{!item.children.length ? <i className="lnr lnr-apartment" /> : null} |
||||||
|
<div className="text">{title}</div> |
||||||
|
</div> |
||||||
|
{getPermissionCheck("factory", "destroy", profile) && ( |
||||||
|
<Popconfirm |
||||||
|
title="Ви справді хочете видалити?" |
||||||
|
onConfirm={() => deleteFactory(item.id)} |
||||||
|
> |
||||||
|
<a className="lnr lnr-trash" href="javascript:;" /> |
||||||
|
</Popconfirm> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const renderTree = useCallback( |
||||||
|
(preparedTree) => { |
||||||
|
if (Array.isArray(preparedTree)) { |
||||||
|
return preparedTree.map((item) => ( |
||||||
|
<Tree.TreeNode title={renderTitle(item)} key={item.id}> |
||||||
|
{item.children ? renderTree(item.children) : null} |
||||||
|
</Tree.TreeNode> |
||||||
|
)); |
||||||
|
} |
||||||
|
}, |
||||||
|
[factories, searchVal] |
||||||
|
); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="TreeFactory"> |
||||||
|
<Input.Search |
||||||
|
style={{ marginBottom: 8, maxWidth: 200 }} |
||||||
|
placeholder="Search" |
||||||
|
onChange={(e) => onChange(e.target.value)} |
||||||
|
/> |
||||||
|
|
||||||
|
<Tree |
||||||
|
onExpand={onExpend} |
||||||
|
autoExpandParent={autoExpandParent} |
||||||
|
expandedKeys={expandedKeys} |
||||||
|
> |
||||||
|
{renderTree(preparedTree)} |
||||||
|
</Tree> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1 @@ |
|||||||
|
export * from "./use-factory.hook"; |
@ -0,0 +1,69 @@ |
|||||||
|
import { useSelector } from "react-redux"; |
||||||
|
import { IUpdateFactory } from "./../../../api/factories/requests.interface"; |
||||||
|
import { useEffect, useState } from "react"; |
||||||
|
import { factoriesService } from "@/services/domain"; |
||||||
|
import { IFactory } from "@/shared"; |
||||||
|
import { ICreateFactory } from "@/api/factories/requests.interface"; |
||||||
|
import { isLoadingFactories } from "@/store/factories"; |
||||||
|
|
||||||
|
export const useFactory = () => { |
||||||
|
const [factories, setFactories] = useState<IFactory[]>([]); |
||||||
|
const [needRefetch, setRefetch] = useState<boolean>(false); |
||||||
|
|
||||||
|
const isLoading = useSelector(isLoadingFactories); |
||||||
|
|
||||||
|
const fetchFactories = async () => { |
||||||
|
const factories = await factoriesService.fetchFactories(); |
||||||
|
|
||||||
|
setFactories(factories); |
||||||
|
}; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
fetchFactories(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
useEffect( |
||||||
|
() => { |
||||||
|
if (needRefetch) { |
||||||
|
fetchFactories(); |
||||||
|
setRefetch(false); |
||||||
|
} |
||||||
|
}, |
||||||
|
[needRefetch] |
||||||
|
); |
||||||
|
|
||||||
|
const createFactory = async (factory: ICreateFactory) => { |
||||||
|
const createdFactory = await factoriesService.createFactory(factory); |
||||||
|
|
||||||
|
setRefetch(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const updateFactory = async (factory: IUpdateFactory) => { |
||||||
|
const updatedFactory = await factoriesService.updateFactory(factory); |
||||||
|
|
||||||
|
factories.forEach((it) => { |
||||||
|
if (it.id !== factory.id) return it; |
||||||
|
else return updatedFactory; |
||||||
|
}); |
||||||
|
|
||||||
|
setFactories(factories); |
||||||
|
setRefetch(true); |
||||||
|
}; |
||||||
|
|
||||||
|
const deleteFactory = async (id: number) => { |
||||||
|
await factoriesService.deleteFactory(id); |
||||||
|
|
||||||
|
factories.filter((it) => it.id !== id); |
||||||
|
|
||||||
|
setFactories(factories); |
||||||
|
setRefetch(true); |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
factories, |
||||||
|
isLoading, |
||||||
|
createFactory, |
||||||
|
updateFactory, |
||||||
|
deleteFactory, |
||||||
|
}; |
||||||
|
}; |
@ -1,103 +0,0 @@ |
|||||||
import React, { Component } from 'react' |
|
||||||
import PropTypes from 'prop-types' |
|
||||||
import { Col, Container, Row, Card, CardBody, Button } from 'reactstrap'; |
|
||||||
import { connect } from 'react-redux' |
|
||||||
import TreeFactory from './components/TreeFactory'; |
|
||||||
import { getFactory, showModal, setItemFactory, deleteFactory } from './actions'; |
|
||||||
import DataFactory from './components/DataFactory'; |
|
||||||
import ModalForm from './components/ModalForm' |
|
||||||
import { Popconfirm } from 'antd'; |
|
||||||
import { getPermissionCheck } from '../../lib/helper'; |
|
||||||
import histoty from '../../lib/histoty'; |
|
||||||
import './style.scss'; |
|
||||||
|
|
||||||
class Factory extends Component { |
|
||||||
constructor(props) { |
|
||||||
super(props) |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentWillMount() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentDidMount() { |
|
||||||
this.props.getFactory() |
|
||||||
} |
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
componentWillUnmount() { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
render() { |
|
||||||
let {active_factory, profile} = this.props; |
|
||||||
let permissions = profile.permissions || {}; |
|
||||||
if(!getPermissionCheck('factory', 'find', profile)){ |
|
||||||
histoty.push('/'); |
|
||||||
} |
|
||||||
return ( |
|
||||||
<Container className="factory"> |
|
||||||
<Row> |
|
||||||
<Col md={12}> |
|
||||||
<Card> |
|
||||||
<CardBody> |
|
||||||
<ModalForm/> |
|
||||||
<Row> |
|
||||||
<div className='col-md-12 justify-content-end flex'> |
|
||||||
{getPermissionCheck('factory', 'create', profile) ? <Button color="primary" size='sm' onClick={() => { |
|
||||||
this.props.setItemFactory({}); |
|
||||||
this.props.showModal(true); |
|
||||||
}}><i className="fal fa-building"></i> Створити</Button> : null} |
|
||||||
{/* {getPermissionCheck('factory', 'destroy', profile) ? <Popconfirm |
|
||||||
title="Ви справді хочете видалити?" |
|
||||||
onConfirm={() => { |
|
||||||
this.props.deleteFactory(active_factory) |
|
||||||
}} |
|
||||||
disabled={!active_factory} |
|
||||||
> |
|
||||||
<Button color="danger" disabled={!active_factory} size='sm'> <i className="fal fa-trash-alt"></i> Видалити</Button> |
|
||||||
</Popconfirm> : null} */} |
|
||||||
|
|
||||||
</div> |
|
||||||
</Row> |
|
||||||
<Row> |
|
||||||
<Col md={12}> |
|
||||||
<TreeFactory/> |
|
||||||
</Col> |
|
||||||
|
|
||||||
</Row> |
|
||||||
</CardBody> |
|
||||||
</Card> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</Container> |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
Factory.propTypes = { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({ |
|
||||||
is_load: state.factory.is_load, |
|
||||||
active_factory: state.factory.active_factory, |
|
||||||
data: state.factory.data, |
|
||||||
profile: state.auth.profile, |
|
||||||
}) |
|
||||||
|
|
||||||
const mapDispatchToProps = { |
|
||||||
getFactory, |
|
||||||
setItemFactory, |
|
||||||
deleteFactory, |
|
||||||
showModal, |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Factory) |
|
@ -0,0 +1,74 @@ |
|||||||
|
import { getPermissionCheck } from "@/lib/helper"; |
||||||
|
import { IFactory, Loader } from "@/shared"; |
||||||
|
import { getProfile } from "@/store/account"; |
||||||
|
import { Button, Card, Col, Row } from "antd"; |
||||||
|
import React, { FC, useState } from "react"; |
||||||
|
import { useSelector } from "react-redux"; |
||||||
|
import { Container } from "reactstrap"; |
||||||
|
import { ModalForm } from "./components/ModalForm"; |
||||||
|
import { TreeFactory } from "./components/TreeFactory"; |
||||||
|
import { useFactory } from "./hooks"; |
||||||
|
|
||||||
|
export const Factory: FC = () => { |
||||||
|
const [isOpenModal, setOpenModal] = useState<boolean>(false); |
||||||
|
const [selectedFactory, setFactory] = useState<IFactory>(); |
||||||
|
|
||||||
|
const profile = useSelector(getProfile); |
||||||
|
const { |
||||||
|
isLoading, |
||||||
|
factories, |
||||||
|
createFactory, |
||||||
|
updateFactory, |
||||||
|
deleteFactory, |
||||||
|
} = useFactory(); |
||||||
|
|
||||||
|
return ( |
||||||
|
<Container className="factory"> |
||||||
|
<Row> |
||||||
|
<Col md={12} /> |
||||||
|
<Card> |
||||||
|
<ModalForm |
||||||
|
isOpen={isOpenModal} |
||||||
|
selectedFactory={selectedFactory} |
||||||
|
factories={factories} |
||||||
|
closeModal={() => setOpenModal(false)} |
||||||
|
createFactory={createFactory} |
||||||
|
updateFactory={updateFactory} |
||||||
|
/> |
||||||
|
|
||||||
|
<Row> |
||||||
|
<div className="col-md-12 justify-content-end flex"> |
||||||
|
{getPermissionCheck("factory", "create", profile) ? ( |
||||||
|
<Button |
||||||
|
type="primary" |
||||||
|
onClick={() => { |
||||||
|
setOpenModal(true); |
||||||
|
setFactory(null); |
||||||
|
}} |
||||||
|
> |
||||||
|
<i className="fal fa-building" /> Створити |
||||||
|
</Button> |
||||||
|
) : null} |
||||||
|
</div> |
||||||
|
</Row> |
||||||
|
|
||||||
|
<Row> |
||||||
|
<Col md={12}> |
||||||
|
{isLoading ? ( |
||||||
|
<Loader /> |
||||||
|
) : ( |
||||||
|
<TreeFactory |
||||||
|
factories={factories} |
||||||
|
profile={profile} |
||||||
|
openModal={() => setOpenModal(true)} |
||||||
|
setSelectedFactory={setFactory} |
||||||
|
deleteFactory={deleteFactory} |
||||||
|
/> |
||||||
|
)} |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</Card> |
||||||
|
</Row> |
||||||
|
</Container> |
||||||
|
); |
||||||
|
}; |
@ -1,27 +1,31 @@ |
|||||||
import React from 'react'; |
import React from "react"; |
||||||
import { SignInForm } from "../components"; |
import { SignInForm } from "../components"; |
||||||
import logo from '@/assets/img/logo_task2.png' |
import logo from "@/assets/img/logo_task2.png"; |
||||||
|
|
||||||
interface IProps { } |
interface IProps {} |
||||||
|
|
||||||
export const SignIn = (props: IProps) => { |
export const SignIn = (props: IProps) => { |
||||||
|
|
||||||
|
|
||||||
return ( |
return ( |
||||||
<div className="account"> |
<div className="account"> |
||||||
<div className="account__wrapper"> |
<div className="account__wrapper"> |
||||||
<div className="account__card"> |
<div className="account__card"> |
||||||
<div className="account__head" style={{ border: 'none', paddingLeft: 0 }}> |
<div |
||||||
|
className="account__head" |
||||||
|
style={{ border: "none", paddingLeft: 0 }} |
||||||
|
> |
||||||
<h3 className="account__title"> |
<h3 className="account__title"> |
||||||
<span className="account__logo"> |
<span className="account__logo"> |
||||||
<img style={{ width: '140px', marginLeft: '-5px' }} src={logo} /> |
<img |
||||||
|
style={{ width: "140px", marginLeft: "-5px" }} |
||||||
|
src={logo} |
||||||
|
/> |
||||||
</span> |
</span> |
||||||
</h3> |
</h3> |
||||||
<h4 className="account__subhead subhead"></h4> |
<h4 className="account__subhead subhead" /> |
||||||
</div> |
</div> |
||||||
<SignInForm /> |
<SignInForm /> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
</div> |
</div> |
||||||
) |
); |
||||||
} |
}; |
||||||
|
@ -0,0 +1,65 @@ |
|||||||
|
import { simpleDispatch } from "@/store/store-helpers"; |
||||||
|
import { IFactory } from "@/shared"; |
||||||
|
import { factoriesApi } from "@/api"; |
||||||
|
import { |
||||||
|
ICreateFactory, |
||||||
|
IUpdateFactory, |
||||||
|
} from "@/api/factories/requests.interface"; |
||||||
|
import { FactoriesIsLoading } from "@/store/factories"; |
||||||
|
|
||||||
|
const fetchFactories = async (): Promise<IFactory[]> => { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: true })); |
||||||
|
try { |
||||||
|
const { data: factoriesList } = await factoriesApi.fetchFactories(); |
||||||
|
|
||||||
|
return factoriesList; |
||||||
|
} catch (err) { |
||||||
|
console.log("FETCH FACTORIES ERROR: ", err); |
||||||
|
} finally { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: false })); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const createFactory = async (payload: ICreateFactory): Promise<IFactory> => { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: true })); |
||||||
|
try { |
||||||
|
const { data: newFactory } = await factoriesApi.createFactory(payload); |
||||||
|
|
||||||
|
return newFactory; |
||||||
|
} catch (err) { |
||||||
|
console.log("CREATE FACTORY ERROR: ", err); |
||||||
|
} finally { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: false })); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const updateFactory = async (payload: IUpdateFactory): Promise<IFactory> => { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: true })); |
||||||
|
try { |
||||||
|
const { data: updatedFactory } = await factoriesApi.updateFactory(payload); |
||||||
|
|
||||||
|
return updatedFactory; |
||||||
|
} catch (err) { |
||||||
|
console.log("UPDATE FACTORY ERROR: ", err); |
||||||
|
} finally { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: false })); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const deleteFactory = async (id: number): Promise<void> => { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: true })); |
||||||
|
try { |
||||||
|
await factoriesApi.deleteFactory(id); |
||||||
|
} catch (err) { |
||||||
|
console.log("DELETE FACTORY ERROR: ", err); |
||||||
|
} finally { |
||||||
|
simpleDispatch(new FactoriesIsLoading({ isLoading: false })); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
export const factoriesService = { |
||||||
|
fetchFactories, |
||||||
|
createFactory, |
||||||
|
updateFactory, |
||||||
|
deleteFactory, |
||||||
|
}; |
@ -1 +1,4 @@ |
|||||||
export * from './auth.service' |
export * from "./auth.service"; |
||||||
|
export * from "./factories.service"; |
||||||
|
export * from "./account.service"; |
||||||
|
export * from "./taxonomies.service"; |
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
export * from "./input-field.component"; |
||||||
|
export * from "./select-field.component"; |
||||||
|
export * from "./select-multi-field.component"; |
@ -0,0 +1,29 @@ |
|||||||
|
import React, { FC, CSSProperties } from "react"; |
||||||
|
import { Input } from "antd"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
label: string; |
||||||
|
value: string | number; |
||||||
|
placeholder: string; |
||||||
|
style?: CSSProperties; |
||||||
|
onChange: (val: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const InputField: FC<IProps> = ({ |
||||||
|
label, |
||||||
|
value, |
||||||
|
placeholder, |
||||||
|
style, |
||||||
|
onChange, |
||||||
|
}) => { |
||||||
|
return ( |
||||||
|
<div style={{ ...style, width: "100%", marginBottom: 20 }}> |
||||||
|
<span>{label}</span> |
||||||
|
<Input |
||||||
|
placeholder={placeholder} |
||||||
|
value={value} |
||||||
|
onChange={({ target: { value } }) => onChange(value)} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,54 @@ |
|||||||
|
import React, { FC } from "react"; |
||||||
|
import { Select } from "antd"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
options: any[]; |
||||||
|
label: string; |
||||||
|
placeholder: string; |
||||||
|
isDisabled?: boolean; |
||||||
|
touched?: boolean; |
||||||
|
error?: string; |
||||||
|
onChange: (val: string) => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const SelectField: FC<IProps> = ({ |
||||||
|
options, |
||||||
|
label, |
||||||
|
placeholder, |
||||||
|
isDisabled, |
||||||
|
touched, |
||||||
|
error, |
||||||
|
onChange, |
||||||
|
}) => { |
||||||
|
return ( |
||||||
|
<div className="form__form-group select-field"> |
||||||
|
{label ? <span className="form__form-group-label">{label}</span> : null} |
||||||
|
|
||||||
|
<div className="form__form-group-field"> |
||||||
|
<div className="form__form-group-input-wrap"> |
||||||
|
<Select |
||||||
|
showSearch |
||||||
|
style={{ width: "100%" }} |
||||||
|
placeholder={placeholder} |
||||||
|
optionFilterProp="children" |
||||||
|
onChange={onChange} |
||||||
|
disabled={isDisabled} |
||||||
|
allowClear |
||||||
|
filterOption={(input, { props }) => |
||||||
|
props.title.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
{options.map((item, index) => ( |
||||||
|
<Select.Option key={item.value + "-" + index} value={item.value}> |
||||||
|
{item.title} |
||||||
|
</Select.Option> |
||||||
|
))} |
||||||
|
</Select> |
||||||
|
|
||||||
|
{touched && |
||||||
|
error && <span className="form__form-group-error">{error}</span>} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -0,0 +1,83 @@ |
|||||||
|
import { Select } from "antd"; |
||||||
|
import React, { FC } from "react"; |
||||||
|
|
||||||
|
interface IProps { |
||||||
|
label?: string; |
||||||
|
placeholder: string; |
||||||
|
options: any[]; |
||||||
|
touched?: boolean; |
||||||
|
error?: string; |
||||||
|
input?: any; |
||||||
|
onChange: () => void; |
||||||
|
} |
||||||
|
|
||||||
|
export const SelectMultiField: FC<IProps> = ({ |
||||||
|
label, |
||||||
|
placeholder, |
||||||
|
options, |
||||||
|
touched, |
||||||
|
error, |
||||||
|
input, |
||||||
|
onChange, |
||||||
|
}) => { |
||||||
|
return ( |
||||||
|
<div className="form__form-group select-field"> |
||||||
|
{label ? <span className="form__form-group-label">{label}</span> : null} |
||||||
|
|
||||||
|
<div className="form__form-group-field"> |
||||||
|
<div className="form__form-group-input-wrap"> |
||||||
|
<div style={{ position: "relative" }}> |
||||||
|
<Select |
||||||
|
showSearch |
||||||
|
style={{ width: "100%" }} |
||||||
|
placeholder={placeholder} |
||||||
|
optionFilterProp="children" |
||||||
|
mode="multiple" |
||||||
|
onChange={onChange} |
||||||
|
allowClear |
||||||
|
filterOption={(input, { props }) => |
||||||
|
props.title.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
{options.map((item, index) => ( |
||||||
|
<Select.Option |
||||||
|
key={item.value + "-" + index} |
||||||
|
value={item.value} |
||||||
|
> |
||||||
|
{item.title} |
||||||
|
</Select.Option> |
||||||
|
))} |
||||||
|
</Select> |
||||||
|
|
||||||
|
{!input?.value || !input?.value.length ? ( |
||||||
|
<span |
||||||
|
className="ant-select-arrow" |
||||||
|
style={{ outline: "currentcolor none medium" }} |
||||||
|
> |
||||||
|
<i |
||||||
|
aria-label="icon: down" |
||||||
|
className="anticon anticon-down ant-select-arrow-icon" |
||||||
|
> |
||||||
|
<svg |
||||||
|
viewBox="64 64 896 896" |
||||||
|
className="" |
||||||
|
data-icon="down" |
||||||
|
width="1em" |
||||||
|
height="1em" |
||||||
|
fill="currentColor" |
||||||
|
aria-hidden="true" |
||||||
|
> |
||||||
|
<path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z" /> |
||||||
|
</svg> |
||||||
|
</i> |
||||||
|
</span> |
||||||
|
) : null} |
||||||
|
</div> |
||||||
|
|
||||||
|
{touched && |
||||||
|
error && <span className="form__form-group-error">{error}</span>} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
@ -1,3 +1,4 @@ |
|||||||
export * from './form' |
export * from "./form"; |
||||||
export * from './layouts' |
export * from "./layouts"; |
||||||
export * from './elements' |
export * from "./elements"; |
||||||
|
export * from "./fields"; |
||||||
|
@ -0,0 +1,15 @@ |
|||||||
|
export interface IFactory { |
||||||
|
id: number; |
||||||
|
name: string; |
||||||
|
shortName: string; |
||||||
|
description: string; |
||||||
|
path?: string; |
||||||
|
directorId: number; |
||||||
|
authorId: number; |
||||||
|
parentId?: number; |
||||||
|
children?: IFactory[]; |
||||||
|
usersAccessPermission: any[]; |
||||||
|
createdAt: string; |
||||||
|
updatedAt: string; |
||||||
|
}; |
||||||
|
|
@ -1,6 +1,7 @@ |
|||||||
export * from './token-pair.interfaces' |
export * from "./token-pair.interfaces"; |
||||||
export * from './user.interfaces' |
export * from "./user.interfaces"; |
||||||
export * from './permissions.interface' |
export * from "./permissions.interface"; |
||||||
export * from './comment.interfaces' |
export * from "./comment.interfaces"; |
||||||
export * from './task.interfaces' |
export * from "./task.interfaces"; |
||||||
export * from './taxonomy.interfaces' |
export * from "./taxonomy.interfaces"; |
||||||
|
export * from "./factories.interfaces"; |
||||||
|
@ -1,54 +1,53 @@ |
|||||||
import { ETaxonomyType } from "../enums"; |
import { ETaxonomyType } from "../enums"; |
||||||
|
|
||||||
export interface ITaxonomy { |
export interface ITaxonomy { |
||||||
/** Ідентифікатор */ |
/** Ідентифікатор */ |
||||||
id: number |
id: number; |
||||||
|
|
||||||
/** Ідентифікатор батьківської таксономії */ |
/** Ідентифікатор батьківської таксономії */ |
||||||
parentId?: number |
parentId?: number; |
||||||
|
|
||||||
/** Дочерні таксономії */ |
/** Дочерні таксономії */ |
||||||
children?: ITaxonomy[] |
children?: ITaxonomy[]; |
||||||
|
|
||||||
/** Назва */ |
/** Назва */ |
||||||
name?: string |
name?: string; |
||||||
|
|
||||||
/** Тип класифікації */ |
/** Тип класифікації */ |
||||||
type: ETaxonomyType |
type: ETaxonomyType; |
||||||
|
|
||||||
/** Чи видалено */ |
/** Чи видалено */ |
||||||
isDeleted?: boolean |
isDeleted?: boolean; |
||||||
|
|
||||||
/** Чи за замовчуванням */ |
/** Чи за замовчуванням */ |
||||||
isDefault?: boolean |
isDefault?: boolean; |
||||||
|
|
||||||
/** Посилання на зображення */ |
/** Посилання на зображення */ |
||||||
iconUrl?: string |
iconUrl?: string; |
||||||
} |
} |
||||||
|
|
||||||
export interface ICreateOrUpdateTaxonomy { |
export interface ICreateOrUpdateTaxonomy { |
||||||
/** Ідентифікатор */ |
/** Ідентифікатор */ |
||||||
id?: number |
id?: number; |
||||||
|
|
||||||
/** Ідентифікатор батьківської таксономії */ |
/** Ідентифікатор батьківської таксономії */ |
||||||
parentId?: number |
parentId?: number; |
||||||
|
|
||||||
/** Дочерні таксономії */ |
/** Дочерні таксономії */ |
||||||
children?: ITaxonomy[] |
children?: ITaxonomy[]; |
||||||
|
|
||||||
/** Назва */ |
/** Назва */ |
||||||
name?: string |
name?: string; |
||||||
|
|
||||||
/** Тип класифікації */ |
/** Тип класифікації */ |
||||||
type: ETaxonomyType |
type: ETaxonomyType; |
||||||
|
|
||||||
/** Чи видалено */ |
/** Чи видалено */ |
||||||
isDeleted?: boolean |
isDeleted?: boolean; |
||||||
|
|
||||||
/** Чи за замовчуванням */ |
/** Чи за замовчуванням */ |
||||||
isDefault?: boolean |
isDefault?: boolean; |
||||||
|
|
||||||
/** Посилання на зображення */ |
|
||||||
iconUrl?: string |
|
||||||
|
|
||||||
|
/** Посилання на зображення */ |
||||||
|
iconUrl?: string; |
||||||
} |
} |
||||||
|
@ -0,0 +1,3 @@ |
|||||||
|
export * from "./reducer"; |
||||||
|
export * from "./selectors"; |
||||||
|
export * from "./types"; |
@ -0,0 +1,19 @@ |
|||||||
|
import { TFactoriesActions } from "./types"; |
||||||
|
import { createReducer } from "@bitalikrty/redux-create-reducer"; |
||||||
|
|
||||||
|
export interface IFactoriesState { |
||||||
|
isLoading: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
const initialState: IFactoriesState = { |
||||||
|
isLoading: false, |
||||||
|
}; |
||||||
|
|
||||||
|
export const factoriesReducer = createReducer< |
||||||
|
IFactoriesState, |
||||||
|
TFactoriesActions |
||||||
|
>(initialState, { |
||||||
|
IS_LOADING_FACTORIES: (state, action) => { |
||||||
|
return { ...state, isLoading: action.payload.isLoading }; |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,4 @@ |
|||||||
|
import { RootState } from "@/store"; |
||||||
|
|
||||||
|
export const isLoadingFactories = (state: RootState) => |
||||||
|
state.factories.isLoading; |
@ -0,0 +1,10 @@ |
|||||||
|
export class FactoriesIsLoading { |
||||||
|
readonly type = "IS_LOADING_FACTORIES"; |
||||||
|
constructor( |
||||||
|
public readonly payload: { |
||||||
|
isLoading: boolean; |
||||||
|
} |
||||||
|
) {} |
||||||
|
} |
||||||
|
|
||||||
|
export type TFactoriesActions = FactoriesIsLoading; |
@ -1,37 +1,31 @@ |
|||||||
import { |
import { createStore, combineReducers } from "redux"; |
||||||
createStore, |
import { GlobalContainerService } from "@/services/system"; |
||||||
combineReducers, |
import { authReducer, IAuthState } from "./auth"; |
||||||
} from 'redux' |
import { composeWithDevTools } from "redux-devtools-extension"; |
||||||
import { GlobalContainerService } from '@/services/system' |
import { accountReducer, IAccountState } from "./account"; |
||||||
import { authReducer, IAuthState } from './auth' |
import { ISharedState, sharedReducer } from "./shared"; |
||||||
import { composeWithDevTools } from 'redux-devtools-extension' |
import { ITaxonomiesState, TaxonomiesReducer } from "./taxonomies/reducer"; |
||||||
import { accountReducer, IAccountState } from "./account" |
import { factoriesReducer, IFactoriesState } from "./factories"; |
||||||
import { ISharedState, sharedReducer } from "./shared" |
|
||||||
import { ITaxonomiesState, TaxonomiesReducer } from './taxonomies/reducer'; |
|
||||||
|
|
||||||
|
|
||||||
const rootReducer = combineReducers<{ |
const rootReducer = combineReducers<{ |
||||||
auth: IAuthState |
auth: IAuthState; |
||||||
account: IAccountState |
account: IAccountState; |
||||||
taxonomies: ITaxonomiesState |
taxonomies: ITaxonomiesState; |
||||||
shared: ISharedState |
shared: ISharedState; |
||||||
|
factories: IFactoriesState; |
||||||
}>({ |
}>({ |
||||||
auth: authReducer, |
auth: authReducer, |
||||||
account: accountReducer, |
account: accountReducer, |
||||||
taxonomies: TaxonomiesReducer, |
taxonomies: TaxonomiesReducer, |
||||||
// task: taskReducer,
|
// task: taskReducer,
|
||||||
shared: sharedReducer, |
shared: sharedReducer, |
||||||
}) |
factories: factoriesReducer, |
||||||
|
}); |
||||||
export type RootState = ReturnType<typeof rootReducer> |
|
||||||
|
|
||||||
|
|
||||||
const store = createStore( |
export type RootState = ReturnType<typeof rootReducer>; |
||||||
rootReducer, |
|
||||||
composeWithDevTools() |
|
||||||
) |
|
||||||
|
|
||||||
|
const store = createStore(rootReducer, composeWithDevTools()); |
||||||
|
|
||||||
GlobalContainerService.set('store', store) |
GlobalContainerService.set("store", store); |
||||||
|
|
||||||
export default store |
export default store; |
||||||
|
Loading…
Reference in new issue