settings admin form updates

This commit is contained in:
merelendor 2022-09-05 10:19:40 +03:00
parent 092ce951e8
commit fb1e50102c
9 changed files with 244 additions and 102 deletions

View File

@ -41,30 +41,30 @@ export const sendInvite = ({ name, email, companies }) =>
{
console.log("ACTION", "sendInvite()", "response.data", response.data);
resolve();
resolve(response.data);
})
.catch((error) =>
{
console.error("ACTION", "sendInvite()", "error");
console.error(error);
console.log("error.response.data", error.response.data);
reject();
reject(error.response.data);
});
});
}
export const removeUser = ({ email }) =>
export const removeUser = ({ account, emails }) =>
{
console.log("ACTION", "removeUser()", { email });
console.log("ACTION", "removeUser()", { account, emails });
return new Promise((resolve, reject) =>
{
axios.post(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/admin/remove`, { email })
axios.post(`${ process.env.NEXT_PUBLIC_SELF_API_HOST }/api/admin/remove`, { account, emails })
.then(async (response) =>
{
console.log("ACTION", "removeUser()", "response.data", response.data);
resolve();
resolve(response.data);
})
.catch((error) =>
{

View File

@ -41,7 +41,7 @@ export default async function handler(req, res)
console.error(error);
res.status(403).json();
});
});
}
else
{

View File

@ -4,9 +4,16 @@ import { Cookies } from 'react-cookie';
import cookie from 'cookie';
import moment from 'moment';
import jwt from 'jsonwebtoken';
import { inspect } from 'util';
import { cors } from '../../../lib/cors';
const validateEmail = (email) => {
return email.match(
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
};
export default async function handler(req, res)
{
await cors(req, res);
@ -28,32 +35,53 @@ export default async function handler(req, res)
const admin_companies = client_jwt_decoded.companies;
const allowed_companies = [];
if(admin_companies !== undefined && admin_companies !== null)
if(validateEmail(email))
{
if(companies !== undefined && companies !== null)
if(admin_companies !== undefined && admin_companies !== null)
{
for(let c in companies)
if(companies !== undefined && companies !== null)
{
for(let ac in admin_companies)
for(let c in companies)
{
if(admin_companies[ac].acc_number === companies[c])
for(let ac in admin_companies)
{
allowed_companies.push(companies[c]);
if(admin_companies[ac].acc_number === companies[c])
{
allowed_companies.push(companies[c]);
}
}
}
}
if(allowed_companies.length > 0)
{
const payload = { name, email, companies, };
console.log("payload");
console.log(payload);
if(allowed_companies.length > 0)
{
const payload = { name, email, companies, };
console.log("payload");
console.log(payload);
const crm_jwt = jwt.sign(payload, process.env.JWT_SECRET_CRM, { noTimestamp: true });
const crm_jwt_token = jwt.sign(payload, process.env.JWT_SECRET_CRM, { noTimestamp: true });
res.status(200).send({
status: "success",
});
await axios.post(`${ process.env.NEXT_PUBLIC_API_HOST }/api/admin/invite/`, {
token: crm_jwt_token,
})
.then((api_response) =>
{
console.log("API", "auth/invite", "RESPONSE");
console.log(inspect(api_response.data, true, null, true));
res.status(200).send(api_response.data);
})
.catch((error) =>
{
console.error("API", "auth/invite", "error");
console.error(error);
res.status(403).json();
});
}
else
{
res.status(403).json();
}
}
else
{
@ -67,7 +95,7 @@ export default async function handler(req, res)
}
else
{
res.status(403).json();
res.status(400).json({ errors: [ "email" ]});
}
}
else

View File

@ -4,13 +4,14 @@ import { Cookies } from 'react-cookie';
import cookie from 'cookie';
import moment from 'moment';
import jwt from 'jsonwebtoken';
import { inspect } from 'util';
import { cors } from '../../../lib/cors';
export default async function handler(req, res)
{
await cors(req, res);
let { email } = req.body;
let { emails } = req.body;
console.log("API", "admin/remove");
if(req.headers.cookie !== undefined)
@ -19,8 +20,32 @@ export default async function handler(req, res)
if(cookies.jwt !== undefined && cookies.jwt !== null)
{
res.status(200).send({
status: "success",
let client_jwt_decoded = jwt.verify(cookies.jwt, process.env.JWT_SECRET_CLIENT);
console.log("API", "admin/remove", "client_jwt_decoded", client_jwt_decoded);
const payload = {
acc_number: client_jwt_decoded.acc_number,
emails,
};
const crm_jwt_token = jwt.sign(payload, process.env.JWT_SECRET_CRM, { noTimestamp: true });
await axios.post(`${ process.env.NEXT_PUBLIC_API_HOST }/api/admin/remove/`, {
token: crm_jwt_token,
})
.then((api_response) =>
{
console.log("API", "auth/remove", "RESPONSE");
console.log(inspect(api_response.data, true, null, true));
res.status(200).send(api_response.data);
})
.catch((error) =>
{
console.error("API", "auth/remove", "error");
console.error(error);
res.status(403).json();
});
}
else

View File

@ -43,10 +43,11 @@ export default async function handler(req, res)
{
const new_client_jwt = jwt.sign({
acc_number: acc_number,
is_admin: company.is_admin,
login: client_jwt_decoded.login,
companies: client_jwt_decoded.companies,
}, process.env.JWT_SECRET_CLIENT, { noTimestamp: true });
res.status(200).send({
status: "success",
token: new_client_jwt,

View File

@ -34,6 +34,17 @@ class Form extends React.Component
};
}
componentDidMount()
{
const { errors } = this.props;
if(errors.length > 0)
{
const update = { ...this.state, ...this.props.form };
this.setState(update);
}
}
_handle_onChange = (field, value) =>
{
this.setState({ [ field ]: value }, () =>
@ -109,16 +120,16 @@ class Form extends React.Component
render()
{
const { companies } = this.props;
const { companies, errors } = this.props;
const { name, email, selection, selected_companies_all, selected_companies_list, } = this.state;
return (
<div className="table_row editable">
<div className="table_cell" data-title="ФИО пользователя">
<input type="text" placeholder="Введите ФИО" value={ name } onChange={ (event) => { this._handle_onChange("name", event.target.value) } }/>
<input type="text" placeholder="Введите ФИО" value={ name } style={ errors.indexOf("name") > -1 ? { color: "#ED0A34" } : {} } onChange={ (event) => { this._handle_onChange("name", event.target.value) } }/>
</div>
<div className="table_cell" data-title="Почта">
<input type="email" placeholder="Введите почту" value={ email } onChange={ (event) => { this._handle_onChange("email", event.target.value) } }/>
<input type="email" placeholder="Введите почту" value={ email } style={ errors.indexOf("email") > -1 ? { color: "#ED0A34" } : {} } onChange={ (event) => { this._handle_onChange("email", event.target.value) } }/>
</div>
<div className="table_cell" data-title="Роль">Пользователь</div>
<div className="table_cell" data-title="Доступные организации">
@ -178,15 +189,19 @@ class AdminPage extends React.Component
users: null,
companies: null,
add: false,
edit: false,
edit: false,
save: false,
form: {},
form: {},
observer: false,
to_delete: [],
errors: [],
};
}
static getDerivedStateFromProps(nextProps, prevState)
{
return {
observer: nextProps.observer,
user: nextProps.user,
users: nextProps.users,
companies: nextProps.companies,
@ -197,15 +212,22 @@ class AdminPage extends React.Component
{
this.setState({ loading: true }, () =>
{
getUsers({ dispatch: this.props.dispatch })
.then(() =>
if(!this.state.observer && !this.state.user.is_admin)
{
this.setState({ loading: false, })
})
.catch(() =>
this.props.router.push("/");
}
else
{
});
getUsers({ dispatch: this.props.dispatch })
.then(() =>
{
this.setState({ loading: false, })
})
.catch(() =>
{
this.setState({ loading: false });
});
}
});
}
@ -221,40 +243,85 @@ class AdminPage extends React.Component
_handle_onSave = () =>
{
const { form } = this.state;
const { name, email, selected_companies_list } = form;
console.log("form");
console.log(form);
const { form, add, edit, to_delete } = this.state;
const companies = [];
for(let i in selected_companies_list)
if(add)
{
companies.push(selected_companies_list[i].acc_number);
}
const { name, email, selected_companies_list } = form;
this.setState({ add: false, edit: false, loading: true }, () =>
{
sendInvite({ name, email, companies })
.then(() =>
const companies = [];
for(let i in selected_companies_list)
{
this.setState({ loading: false });
})
.catch(() =>
companies.push(selected_companies_list[i].acc_number);
}
this.setState({ add: false, edit: false, loading: true }, () =>
{
this.setState({ loading: false });
sendInvite({ name, email, companies })
.then((result) =>
{
if(result.status === "success")
{
getUsers({ dispatch: this.props.dispatch })
.then(() =>
{
this.setState({ loading: false, })
})
.catch(() =>
{
this.setState({ loading: false });
});
}
else
{
this.setState({ loading: false });
}
})
.catch((error) =>
{
this.setState({ loading: false, add: true, errors: error.errors });
});
});
});
}
else
{
this.setState({ add: false, edit: false, loading: true }, () =>
{
removeUser({ emails: to_delete })
.then((result) =>
{
if(result.status === "success")
{
getUsers({ dispatch: this.props.dispatch })
.then(() =>
{
this.setState({ loading: false, to_delete: [] })
})
.catch(() =>
{
this.setState({ loading: false, to_delete: [] });
});
}
else
{
this.setState({ loading: false });
}
})
.catch(() =>
{
this.setState({ loading: false });
});
});
}
}
_handle_onCancel = () =>
{
this.setState({ add: false, edit: false });
this.setState({ add: false, edit: false, to_delete: [], });
}
_onFormEdit = (form) =>
{
console.log("_onFormEdit", form);
let save = true;
if(form.name === "")
{
@ -272,22 +339,29 @@ class AdminPage extends React.Component
}
}
this.setState({ form, save });
this.setState({ form, save, errors: [] });
}
_handle_onMarkToDelete = (email) =>
{
const to_delete = [ ...this.state.to_delete ];
if(to_delete.indexOf(email) > -1)
{
to_delete.splice(to_delete.indexOf(email), 1);
}
else
{
to_delete.push(email);
}
this.setState({ to_delete });
}
render()
{
const { loading, user, users, companies, add, edit, save } = this.state;
console.log("users");
console.log(users);
console.log(".".repeat(100));
console.log("user");
console.log(user);
console.log(".".repeat(100));
console.log("companies");
console.log(companies);
const { loading, user, observer, users, companies, errors, form, add, edit, save, to_delete } = this.state;
return (
<React.Fragment>
@ -307,7 +381,7 @@ class AdminPage extends React.Component
<Company { ...this.props }/>
</div>
<div className="aside_container about">
<InnerMenu { ...this.props }/>
<InnerMenu { ...this.props } user={ user } observer={ observer }/>
<article>
{ loading ? (
<div className="table_row table_header" style={{ minHeight: 300, display: "flex", justifyContent: "center", alignItems: "center" }}>
@ -321,7 +395,7 @@ class AdminPage extends React.Component
{ add || edit ? (
<>
<button className="button button-blue" onClick={ this._handle_onCancel }>Отменить</button>
<button className="button button-blue" onClick={ this._handle_onSave } disabled={ save ? false : true }>Сохранить</button>
<button className="button button-blue" onClick={ this._handle_onSave } disabled={ add ? save ? false : true : to_delete.length > 0 ? false : true }>Сохранить</button>
</>
) : (
<>
@ -349,10 +423,10 @@ class AdminPage extends React.Component
return (
<div className="table_row" key={ index }>
<div className="table_cell" data-title="ФИО пользователя">{ entry.name } (Вы)</div>
<div className="table_cell" data-title="Почта">{ entry.email }</div>
<div className="table_cell" data-title="Роль">Администратор</div>
<div className="table_cell" data-title="Почта">{ entry.email } [{user.email}]</div>
<div className="table_cell" data-title="Роль">{ entry.is_admin ? "Администратор" : "Пользователь" }</div>
<div className="table_cell" data-title="Доступные организации">Все организации</div>
<div className="table_cell" data-title="Статус">Активен</div>
<div className="table_cell" data-title="Статус">{ entry.is_admin ? "Активен" : entry.last !== null ? "Активен" : "Приглашен" }</div>
{ edit && (
<div className="table_cell delete" style={{ position: 'relative' }}></div>
) }
@ -366,17 +440,17 @@ class AdminPage extends React.Component
if(entry.email !== user.email)
{
return (
<div className="table_row" key={ index }>
<div className="table_cell" data-title="ФИО пользователя">{ entry.name }</div>
<div className="table_cell" data-title="Почта">{ entry.email }</div>
<div className="table_cell" data-title="Роль">Администратор</div>
<div className="table_row" key={ index } style={ to_delete.indexOf(entry.email) > -1 ? { backgroundColor: "#eee" } : {} }>
<div className="table_cell" data-title="ФИО пользователя" style={ to_delete.indexOf(entry.email) > -1 ? { textDecoration: "line-through" } : {} }>{ entry.name }</div>
<div className="table_cell" data-title="Почта" style={ to_delete.indexOf(entry.email) > -1 ? { textDecoration: "line-through" } : {} }>{ entry.email }</div>
<div className="table_cell" data-title="Роль" style={ to_delete.indexOf(entry.email) > -1 ? { textDecoration: "line-through" } : {} }>{ entry.is_admin ? "Администратор" : "Пользователь" }</div>
<div className="table_cell" data-title="Доступные организации">{ entry.companies.map((company, c_index) => (
<p key={ c_index }>{ company.title }</p>
<p key={ c_index } style={ to_delete.indexOf(entry.email) > -1 ? { textDecoration: "line-through" } : {} }>{ company.title }</p>
)) }</div>
<div className="table_cell" data-title="Статус">Активен</div>
<div className="table_cell" data-title="Статус" style={ to_delete.indexOf(entry.email) > -1 ? { textDecoration: "line-through" } : {} }>{ entry.is_admin ? "Активен" : entry.last !== null ? "Активен" : "Приглашен" }</div>
{ edit && (
<div className="table_cell delete" style={{ position: 'relative' }}>
<button className="delete_user" title="Удалить пользователя"></button>
<button className="delete_user" title="Удалить пользователя" onClick={ () => { this._handle_onMarkToDelete(entry.email) }}></button>
</div>
) }
</div>
@ -410,7 +484,7 @@ class AdminPage extends React.Component
</div>
{*/}
{ add && (
<Form companies={ companies } onForm={ this._onFormEdit }/>
<Form companies={ companies } errors={ errors } form={ form } onForm={ this._onFormEdit }/>
) }
{/*}
{*/}
@ -429,6 +503,7 @@ class AdminPage extends React.Component
function mapStateToProps(state, ownProps)
{
return {
observer: state.auth.observer,
user: state.user,
users: state.admin.users,
companies: state.companies.list,

View File

@ -2,9 +2,9 @@ import React from "react";
import Link from "next/link";
const menu = [
{id: 1, name: "Номер телефона", link: "/settings/phone"},
{id: 2, name: "Пароль", link: "/settings/password"},
{id: 3, name: "Настройки доступа", link: "/settings/admin"},
{id: 1, type: "phone", name: "Номер телефона", link: "/settings/phone"},
{id: 2, type: "password", name: "Пароль", link: "/settings/password"},
{id: 3, type: "admin", name: "Настройки доступа", link: "/settings/admin"},
]
export default class InnerMenu extends React.Component
@ -47,19 +47,26 @@ export default class InnerMenu extends React.Component
render()
{
const { user, observer } = this.props;
return (
<aside>
<ul className="aside_nav" ref={ this.menuRef }>
{ menu.map(item => (
<li key = {item.id} ref={this[item.id]} onClick={() => this.scrollToCategory(item.id)}>
<Link
href={item.link}
shallow
>
<a className={ this.props.router && this.props.router.route === item.link ? "active" : "" }>{item.name}</a>
</Link>
</li>
)) }
{ menu.map(item =>
{
if(item.type === "admin" && !observer && !user.is_admin) { return null; }
return (
<li key = {item.id} ref={this[item.id]} onClick={() => this.scrollToCategory(item.id)}>
<Link
href={item.link}
shallow
>
<a className={ this.props.router && this.props.router.route === item.link ? "active" : "" }>{item.name}</a>
</Link>
</li>
)
}) }
</ul>
</aside>
)

View File

@ -34,12 +34,14 @@ class IndexPage extends React.Component
new_password_submit_disabled: true,
new_password_error: false,
user: {},
observer: false,
};
}
static getDerivedStateFromProps(nextProps, prevState)
{
return {
observer: nextProps.observer,
user: nextProps.user,
};
}
@ -127,7 +129,7 @@ class IndexPage extends React.Component
render()
{
console.log("this.state.user", this.state.user);
const { password, password_error, new_password, new_password_repeat, new_password_submit_disabled, new_password_check_loading, new_password_error, password_form_step } = this.state;
const { user, observer, password, password_error, new_password, new_password_repeat, new_password_submit_disabled, new_password_check_loading, new_password_error, password_form_step } = this.state;
return (
<React.Fragment>
@ -147,7 +149,7 @@ class IndexPage extends React.Component
<Company { ...this.props }/>
</div>
<div className="aside_container about">
<InnerMenu { ...this.props }/>
<InnerMenu { ...this.props } user={ user } observer={ observer }/>
<article>
{ password_form_step === 1 && (
<div className={`login recovery`}>
@ -193,6 +195,7 @@ class IndexPage extends React.Component
function mapStateToProps(state, ownProps)
{
return {
observer: state.auth.observer,
user: state.user,
}
}

View File

@ -40,12 +40,14 @@ class IndexPage extends React.Component
phone_check_loading: false,
code_check_loading: false,
user: {},
observer: false,
};
}
static getDerivedStateFromProps(nextProps, prevState)
{
return {
observer: nextProps.observer,
user: nextProps.user,
};
}
@ -174,7 +176,7 @@ class IndexPage extends React.Component
<Company { ...this.props }/>
</div>
<div className="aside_container about">
<InnerMenu { ...this.props }/>
<InnerMenu { ...this.props } user={ user }/>
<article>
{ phone_form_step !== 2 && phone_form_step !== 3 && (
<>
@ -243,6 +245,7 @@ class IndexPage extends React.Component
function mapStateToProps(state, ownProps)
{
return {
observer: state.auth.observer,
user: state.user,
}
}