This commit is contained in:
NikolayRechkin 2019-07-19 12:48:56 +03:00
commit 975609fa6f
30 changed files with 816 additions and 454 deletions

View File

@ -58,7 +58,7 @@ server {
proxy_pass http://localhost:5000;
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;
location ~ /people {
location ~ /(people|group) {
proxy_pass http://localhost:5004;
proxy_set_header X-REWRITER-URL $X_REWRITER_URL;

View File

@ -3,3 +3,5 @@ export const SET_CURRENT_USER = 'SET_CURRENT_USER';
export const SET_MODULES = 'SET_MODULES';
export const SET_IS_LOADED = 'SET_IS_LOADED';
export const LOGOUT = 'LOGOUT';
export const SET_GROUPS = 'SET_GROUPS';
export const SET_USERS = 'SET_USERS';

View File

@ -1,5 +1,6 @@
import * as api from '../utils/api';
import { SET_CURRENT_USER, SET_MODULES, SET_IS_LOADED, LOGOUT } from './actionTypes';
import { setGroups, setUsers } from './peopleActions';
import setAuthorizationToken from '../utils/setAuthorizationToken';
export function setCurrentUser(user) {
@ -30,11 +31,17 @@ export function setLogout() {
};
};
const filterOptions = {StartIndex: 0, Count: 25, sortby: "lastname", sortorder: "ascending"};
export function getUserInfo(dispatch) {
return api.getUser()
.then((res) => dispatch(setCurrentUser(res.data.response)))
.then(api.getModulesList)
.then((res) => dispatch(setModules(res.data.response)))
.then(api.getGroupList)
.then((res) => dispatch(setGroups(res.data.response)))
.then(() => api.getUserList(filterOptions))
.then((res) => dispatch(setUsers(res.data.response)))
.then(() => dispatch(setIsLoaded(true)));
};

View File

@ -0,0 +1,15 @@
import { SET_GROUPS, SET_USERS } from './actionTypes';
export function setUsers(users) {
return {
type: SET_USERS,
users
};
};
export function setGroups(groups) {
return {
type: SET_GROUPS,
groups
};
};

View File

@ -1,224 +1,238 @@
import React, {useState} from 'react';
import React, { useState } from 'react';
import { connect } from 'react-redux';
import {
MainButton,
DropDownItem,
TreeMenu,
TreeNode,
Icons
} from "asc-web-components";
MainButton,
DropDownItem,
TreeMenu,
TreeNode,
Icons
} from "asc-web-components";
const treeData = [
{
key: "0-0",
title: "Departments",
root: true,
children: [
{ key: "0-0-0", title: "Development", root: false },
{ key: "0-0-1", title: "Direction", root: false },
{ key: "0-0-2", title: "Marketing", root: false },
{ key: "0-0-3", title: "Mobile", root: false },
{ key: "0-0-4", title: "Support", root: false },
{ key: "0-0-5", title: "Web", root: false }
]
}
];
const PeopleTreeMenu = props => {
const { data } = props;
const PeopleTreeMenu = props => {
const { data } = props;
const [gData, setGData] = useState(data);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [gData, setGData] = useState(data);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [expandedKeys, setExpandedKeys] = useState([
"0-0-key",
"0-0-0-key",
"0-0-0-0-key"
]);
const [expandedKeys, setExpandedKeys] = useState([
"0-0-key",
"0-0-0-key",
"0-0-0-0-key"
]);
const onDragStart = info => {
info.event.persist();
};
const onDragStart = info => {
info.event.persist();
};
const onDragEnter = info => {
setExpandedKeys(info.expandedKeys);
};
const onDragEnter = info => {
setExpandedKeys(info.expandedKeys);
};
const onDrop = info => {
info.event.persist();
const dropKey = info.node.props.eventKey;
const dragKey = info.dragNode.props.eventKey;
const dropPos = info.node.props.pos.split("-");
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const onDrop = info => {
info.event.persist();
const dropKey = info.node.props.eventKey;
const dragKey = info.dragNode.props.eventKey;
const dropPos = info.node.props.pos.split("-");
const dropPosition =
info.dropPosition - Number(dropPos[dropPos.length - 1]);
const getItems = (data, key, callback) => {
data.forEach((item, index, arr) => {
if (item.key === key) {
callback(item, index, arr);
return;
}
if (item.children) {
getItems(item.children, key, callback);
}
});
};
const data = [...gData];
let dragObj;
getItems(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
const getItems = (data, key, callback) => {
data.forEach((item, index, arr) => {
if (item.key === key) {
callback(item, index, arr);
return;
}
if (item.children) {
getItems(item.children, key, callback);
}
});
};
const data = [...gData];
if (!info.dropToGap) {
getItems(data, dropKey, item => {
item.children = item.children || [];
item.children.push(dragObj);
});
} else if (
(info.node.props.children || []).length > 0 &&
info.node.props.expanded &&
dropPosition === 1
) {
getItems(data, dropKey, item => {
item.children = item.children || [];
item.children.unshift(dragObj);
});
let dragObj;
getItems(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
getItems(data, dropKey, item => {
item.children = item.children || [];
item.children.push(dragObj);
});
} else if (
(info.node.props.children || []).length > 0 &&
info.node.props.expanded &&
dropPosition === 1
) {
getItems(data, dropKey, item => {
item.children = item.children || [];
item.children.unshift(dragObj);
});
} else {
let ar;
let i;
getItems(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
let ar;
let i;
getItems(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
ar.splice(i + 1, 0, dragObj);
}
setGData(data);
};
const onExpand = expandedKeys => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
}
setGData(data);
};
const onExpand = expandedKeys => {
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const getItems = data => {
return data.map(item => {
if (item.children && item.children.length) {
return (
<TreeNode
title={item.title}
key={item.key}
icon={
item.root ? (
<Icons.CatalogDepartmentsIcon
size="scale"
isfill={true}
color="dimgray"
/>
) : (
""
)
}
>
{getItems(item.children)}
</TreeNode>
);
}
const getItems = data => {
return data.map(item => {
if (item.children && item.children.length) {
return (
<TreeNode
key={item.key}
title={item.title}
key={item.key}
icon={
!item.root ? (
<Icons.CatalogFolderIcon
item.root ? (
<Icons.CatalogDepartmentsIcon
size="scale"
isfill={true}
color="dimgray"
/>
) : (
""
)
}
>
{getItems(item.children)}
</TreeNode>
);
}
return (
<TreeNode
key={item.key}
title={item.title}
icon={
!item.root ? (
<Icons.CatalogFolderIcon
size="scale"
isfill={true}
color="dimgray"
/>
) : (
""
)
}
/>
);
});
};
const switcherIcon = obj => {
if (obj.isLeaf) {
return null;
}
if (obj.expanded) {
return (
<Icons.ExpanderDownIcon size="scale" isfill={true} color="dimgray" />
);
} else {
return (
<Icons.ExpanderRightIcon size="scale" isfill={true} color="dimgray" />
);
}
};
return (
<>
<MainButton
style={{ marginBottom: 5 }}
isDisabled={false}
isDropdown={true}
text={"Actions"}
clickAction={() => console.log("MainButton clickAction")}
>
<DropDownItem
label="New employee"
onClick={() => console.log("New employee clicked")}
/>
<DropDownItem
label="New quest"
onClick={() => console.log("New quest clicked")}
/>
<DropDownItem
label="New department"
onClick={() => console.log("New department clicked")}
/>
<DropDownItem isSeparator />
<DropDownItem
label="Invitation link"
onClick={() => console.log("Invitation link clicked")}
/>
<DropDownItem
label="Invite again"
onClick={() => console.log("Invite again clicked")}
/>
<DropDownItem
label="Import people"
onClick={() => console.log("Import people clicked")}
/>
</MainButton>
<TreeMenu
checkable={false}
draggable={false}
disabled={false}
multiple={false}
showIcon={true}
defaultExpandAll={true}
defaultExpandParent={true}
onExpand={onExpand}
autoExpandParent={autoExpandParent}
expandedKeys={expandedKeys}
onDragStart={info => onDragStart(info)}
onDrop={info => onDrop(info)}
onDragEnter={onDragEnter}
switcherIcon={switcherIcon}
>
{getItems(gData)}
</TreeMenu>
</>
);
}
/>
);
});
};
const ArticleBodyContent = <PeopleTreeMenu data={treeData} />;
const switcherIcon = obj => {
if (obj.isLeaf) {
return null;
}
if (obj.expanded) {
return (
<Icons.ExpanderDownIcon size="scale" isfill={true} color="dimgray" />
);
} else {
return (
<Icons.ExpanderRightIcon size="scale" isfill={true} color="dimgray" />
);
}
};
export default ArticleBodyContent;
return (
<>
<MainButton
style={{ marginBottom: 5 }}
isDisabled={false}
isDropdown={true}
text={"Actions"}
clickAction={() => console.log("MainButton clickAction")}
>
<DropDownItem
label="New employee"
onClick={() => console.log("New employee clicked")}
/>
<DropDownItem
label="New quest"
onClick={() => console.log("New quest clicked")}
/>
<DropDownItem
label="New department"
onClick={() => console.log("New department clicked")}
/>
<DropDownItem isSeparator />
<DropDownItem
label="Invitation link"
onClick={() => console.log("Invitation link clicked")}
/>
<DropDownItem
label="Invite again"
onClick={() => console.log("Invite again clicked")}
/>
<DropDownItem
label="Import people"
onClick={() => console.log("Import people clicked")}
/>
</MainButton>
<TreeMenu
checkable={false}
draggable={false}
disabled={false}
multiple={false}
showIcon={true}
defaultExpandAll={true}
defaultExpandParent={true}
onExpand={onExpand}
autoExpandParent={autoExpandParent}
expandedKeys={expandedKeys}
onDragStart={info => onDragStart(info)}
onDrop={info => onDrop(info)}
onDragEnter={onDragEnter}
switcherIcon={switcherIcon}
>
{getItems(gData)}
</TreeMenu>
</>
);
};
const ArticleBodyContent = ({ treeData }) => <PeopleTreeMenu data={treeData} />;
const convertGroups = (groups) => {
const children = groups.map(g => {
return {
key: g.id, title: g.name, root: false
};
}) || [];
const treeData = [
{
key: "0-0",
title: "Departments",
root: true,
children: children
}
];
return treeData;
};
function mapStateToProps(state) {
console.log("ArticleBodyContent mapStateToProps state=", state);
const treeData = convertGroups(state.people.groups);
console.log("ArticleBodyContent mapStateToProps treeData=", treeData);
return {
treeData: treeData
};
}
export default connect(mapStateToProps)(ArticleBodyContent);

View File

@ -1,3 +1,6 @@
const ArticleHeaderContent = "People";
import React from 'react';
import { Text } from 'asc-web-components';
const ArticleHeaderContent = () => <Text.MenuHeader>People</Text.MenuHeader>;
export default ArticleHeaderContent;

View File

@ -1,133 +1,38 @@
import React from 'react';
import { Container, Row, Col } from "reactstrap";
import {ContentRow, Link} from 'asc-web-components';
import { ContentRow } from 'asc-web-components';
import UserContent from './userContent';
const UserContent = ({
userName,
department,
phone,
email,
headDepartment,
status
}) => (
<Container fluid={true}>
<Row className="justify-content-start no-gutters">
<Col className="col-12 col-sm-12 col-lg-4 text-truncate">
<Link
style={
status === "pending" ? { color: "#A3A9AE" } : { color: "#333333" }
}
type="action"
title={userName}
text={userName}
isBold={true}
fontSize={15}
onClick={() => console.log("User name action")}
/>
</Col>
<Col
className={`${
headDepartment ? "col-3" : "col-auto"
} col-sm-auto col-lg-2 text-truncate`}
>
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={headDepartment ? "Head of department" : ""}
text={headDepartment ? "Head of department" : ""}
onClick={() => console.log("Head of department action")}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{headDepartment && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{department.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={department.title}
text={department.title}
onClick={department.action}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{department.title && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{phone.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
title={phone.title}
text={phone.title}
onClick={phone.action}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{phone.title && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{email.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={email.title}
text={email.title}
onClick={email.action}
/>
</Col>
</Row>
</Container>
);
const SectionBodyContent = ({ users, onSelect/*, isHeaderChecked*/ }) => {
//const [isChecked, toggleChecked] = useState(false);
//console.log("Body isHeaderChecked=", isHeaderChecked);
return (
<>
{users.map((user, index) => (
<ContentRow
key={user.id}
const SectionBodyContent = ({ users, onSelect, isHeaderChecked }) => {
return (
<>
{users.map(user => (
<ContentRow
key={user.id}
status={user.status}
checked={isHeaderChecked}
data={user}
onSelect={(checked, data) => {
// console.log("ContentRow onSelect", checked, data);
onSelect(checked, data);
}}
avatarRole={user.role}
avatarSource={user.avatar}
avatarName={user.userName}
contextOptions={user.contextOptions}
>
<UserContent
userName={user.userName}
department={user.departments[0]}
phone={user.phones[0]}
email={user.emails[0]}
headDepartment={user.isHead}
status={user.status}
checked={false} //{isHeaderChecked}
data={user}
onSelect={(checked, data) => {
//toggleChecked(e.target.checked);
console.log("ContentRow onSelect", checked, data);
onSelect(checked, data);
}}
avatarRole={user.role}
avatarSource={user.avatar}
avatarName={user.userName}
contextOptions={user.contextOptions}
>
<UserContent
userName={user.userName}
department={user.departments[0]}
phone={user.phones[0]}
email={user.emails[0]}
headDepartment={user.isHead}
status={user.status}
/>
</ContentRow>
))}
</>
);
};
/>
</ContentRow>
))}
</>
);
};
export default SectionBodyContent;
export default SectionBodyContent;

View File

@ -0,0 +1,98 @@
import React from 'react';
import { Container, Row, Col } from "reactstrap";
import {Link} from 'asc-web-components';
const UserContent = ({
userName,
department,
phone,
email,
headDepartment,
status
}) => (
<Container fluid={true}>
<Row className="justify-content-start no-gutters">
<Col className="col-12 col-sm-12 col-lg-4 text-truncate">
<Link
style={
status === "pending" ? { color: "#A3A9AE" } : { color: "#333333" }
}
type="action"
title={userName}
text={userName}
isBold={true}
fontSize={15}
onClick={() => console.log("User name action")}
/>
</Col>
<Col
className={`${
headDepartment ? "col-3" : "col-auto"
} col-sm-auto col-lg-2 text-truncate`}
>
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={headDepartment ? "Head of department" : ""}
text={headDepartment ? "Head of department" : ""}
onClick={() => console.log("Head of department action")}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{headDepartment && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{department.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={department.title}
text={department.title}
onClick={department.action}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{department.title && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{phone.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
title={phone.title}
text={phone.title}
onClick={phone.action}
/>
</Col>
<Col className={`col-3 col-sm-auto col-lg-2 text-truncate`}>
{phone.title && (
<span className="d-lg-none" style={{ margin: "0 4px" }}>
{email.title ? "|" : ""}
</span>
)}
<Link
style={
status === "pending" ? { color: "#D0D5DA" } : { color: "#A3A9AE" }
}
type="action"
isHovered
title={email.title}
text={email.title}
onClick={email.action}
/>
</Col>
</Row>
</Container>
);
export default UserContent;

View File

@ -1,4 +1,4 @@
const fakeUsers = [
export const fakeUsers = [
{
id: "1",
userName: "Helen Walton",
@ -327,5 +327,3 @@ const fakeUsers = [
]
}
];
export default fakeUsers;

View File

@ -4,22 +4,46 @@ import PropTypes from "prop-types";
import { withRouter } from "react-router";
import _ from "lodash";
import { PageLayout } from "asc-web-components";
import fakeUsers from './fakseUsers';
import { fakeUsers } from './fakseUsers';
import {ArticleHeaderContent, ArticleBodyContent} from './Article';
import {SectionHeaderContent, SectionBodyContent} from './Section';
let selection = [];
const Home = () => {
const Home = ({users}) => {
const [isHeaderVisible, toggleHeaderVisible] = useState(false);
const [isHeaderIndeterminate, toggleHeaderIndeterminate] = useState(false);
const [isHeaderChecked, toggleHeaderChecked] = useState(false);
let id = -1;
const renderGroupButtonMenu = () => {
const headerVisible = selection.length > 0;
const headerIndeterminate = headerVisible && selection.length > 0 && selection.length < users.length;
const headerChecked = headerVisible && selection.length === users.length;
const onBodySelect = (checked, data) => {
//toggleChecked(checked);
id = _.result(
/*console.log(`renderGroupButtonMenu()
headerVisible=${headerVisible}
headerIndeterminate=${headerIndeterminate}
headerChecked=${headerChecked}
selection.length=${selection.length}
users.length=${users.length}`);*/
if(headerVisible)
toggleHeaderVisible(headerVisible);
if(isHeaderIndeterminate !== headerIndeterminate)
toggleHeaderIndeterminate(headerIndeterminate);
if(isHeaderChecked !== headerChecked)
toggleHeaderChecked(headerChecked);
};
const onRowSelect = (checked, data) => {
/*console.log(`onBodySelect
row.checked=${checked}`,
data,
selection);*/
const id = _.result(
_.find(selection, function(obj) {
return obj.id === data.id;
}),
@ -33,47 +57,32 @@ const Home = () => {
});
}
let headerVisible = selection.length > 0;
let headerIndeterminate = headerVisible && selection.length < fakeUsers.length;
let headerChecked = !headerIndeterminate;
console.log(`onBodySelect
row.checked=${checked}
headerVisible=${headerVisible}
headerIndeterminate=${headerIndeterminate}
headerChecked=${headerChecked}`,
data,
selection);
if(isHeaderVisible !== headerVisible)
toggleHeaderVisible(headerVisible);
if(isHeaderIndeterminate !== headerIndeterminate)
toggleHeaderIndeterminate(headerIndeterminate);
if(isHeaderChecked !== headerChecked)
toggleHeaderChecked(headerChecked);
renderGroupButtonMenu();
};
return (
<PageLayout
articleHeaderContent={ArticleHeaderContent}
articleBodyContent={ArticleBodyContent}
articleHeaderContent={<ArticleHeaderContent />}
articleBodyContent={<ArticleBodyContent />}
sectionHeaderContent={
<SectionHeaderContent
isHeaderVisible={isHeaderVisible}
isHeaderIndeterminate={isHeaderIndeterminate}
isHeaderChecked={isHeaderChecked}
onCheck={checked => {
console.log("SectionHeaderContent onCheck", checked)
toggleHeaderChecked(checked);
selection = checked ? [...users] : [];
/*console.log(`SectionHeaderContent onCheck
selection.length=${selection.length}`)*/
renderGroupButtonMenu();
}}
/>
}
sectionBodyContent={
<SectionBodyContent
users={fakeUsers}
// isHeaderChecked={isHeaderChecked}
onSelect={onBodySelect}
users={users}
isHeaderChecked={isHeaderChecked}
onSelect={onRowSelect}
/>
}
/>
@ -81,14 +90,117 @@ const Home = () => {
};
Home.propTypes = {
modules: PropTypes.array.isRequired,
users: PropTypes.array.isRequired,
history: PropTypes.object.isRequired,
isLoaded: PropTypes.bool
};
const getUserDepartments = (user) => {
return [
{
title: user.department,
action: () => console.log("Department action")
}
];
};
const getUserPhones = (user) => {
return [
{
title: "+5 104 6473420",
action: () => console.log("Phone action")
}
];
}
const getUserEmails = (user) => {
return [
{
title: user.email,
action: () => console.log("Email action")
}
];
}
const getUserContextOptions = (user) => {
return [
{
key: "key1",
label: "Send e-mail",
onClick: () => console.log("Context action: Send e-mail")
},
{
key: "key2",
label: "Send message",
onClick: () => console.log("Context action: Send message")
},
{ key: "key3", isSeparator: true },
{
key: "key4",
label: "Edit",
onClick: () => console.log("Context action: Edit")
},
{
key: "key5",
label: "Change password",
onClick: () => console.log("Context action: Change password")
},
{
key: "key6",
label: "Change e-mail",
onClick: () => console.log("Context action: Change e-mail")
},
{
key: "key7",
label: "Disable",
onClick: () => console.log("Context action: Disable")
}
];
}
const getUserRole = (user) => {
if(user.isOwner)
return "owner";
else if(user.isAdmin)
return "admin";
else if(user.isVisitor)
return "guest";
else
return "user";
};
const getUserStatus = (user) => {
if(user.state === 1 && user.activationStatus === 1)
return "normal";
else if(user.state === 1 && user.activationStatus === 2)
return "pending";
else if(user.state === 2)
return "disabled";
else
return "normal";
};
const convertUsers = (users) => {
return users.map(user => {
return {
id: user.id,
userName: user.userName,
avatar: user.avatar,
role: getUserRole(user),
status: getUserStatus(user),
isHead: false,
departments: getUserDepartments(user),
phones: getUserPhones(user),
emails: getUserEmails(user),
contextOptions: getUserContextOptions(user)
}
});
};
function mapStateToProps(state) {
const users = convertUsers(state.people.users)
return {
modules: state.auth.modules,
users: users,
isLoaded: state.auth.isLoaded
};
}

View File

@ -0,0 +1,23 @@
import { SET_GROUPS, SET_USERS } from '../actions/actionTypes';
const initialState = {
users: [],
groups: []
};
const people = (state = initialState, action) => {
switch (action.type) {
case SET_GROUPS:
return Object.assign({}, state, {
groups: action.groups
});
case SET_USERS:
return Object.assign({}, state, {
users: action.users
});
default:
return state;
}
}
export default people;

View File

@ -1,8 +1,10 @@
import { combineReducers } from 'redux';
import auth from './auth';
import people from './people';
const rootReducer = combineReducers({
auth
auth,
people
});
export default rootReducer;

View File

@ -1,6 +1,8 @@
import axios from 'axios';
import * as fakeApi from './fakeApi';
import isEmpty from 'lodash/isEmpty';
import { objectToUrlQuery } from './converter'
const PREFIX = 'api';
const VERSION = '2.0';
@ -28,3 +30,12 @@ export function getModulesList() {
export function getUser() {
return IS_FAKE ? fakeApi.getUser() : axios.get(`${API_URL}/people/@self.json`);
};
export function getUserList(filterOptions = {}) {
const filter = !isEmpty(filterOptions) ? `/filter.json?${objectToUrlQuery(filterOptions)}` : ""; // /filter.json?StartIndex=0&Count=25&sortby=lastname&sortorder=ascending
return IS_FAKE ? fakeApi.getUsers() : axios.get(`${API_URL}/people${filter}`);
};
export function getGroupList() {
return IS_FAKE ? fakeApi.getGroups() : axios.get(`${API_URL}/group`);
};

View File

@ -0,0 +1,11 @@
export const objectToUrlQuery = obj => {
let str = "";
for (var key in obj) {
if (str !== "") {
str += "&";
}
str += key + "=" + encodeURIComponent(obj[key]);
}
return str;
};

View File

@ -20,7 +20,7 @@ export function login(data) {
};
export function getModulesList() {
let data = [
const data = [
{
title: "Documents",
link: "/products/files/",
@ -40,7 +40,7 @@ export function getModulesList() {
};
export function getUser() {
let data = {
const data = {
"index": "a",
"type": "person",
"id": "2881e6c6-7c9a-11e9-81fb-0242ac120002",
@ -89,3 +89,143 @@ export function getUser() {
return fakeResponse(data);
};
export function getUsers() {
const data = [
{
"id": "657d1d0e-c9f3-4c9d-bd48-07525e539fd6",
"userName": "Alexey.Safronov",
"isVisitor": false,
"firstName": "Alexey",
"lastName": "Safronov",
"email": "Alexey.Safronov@avsmedia.net",
"status": 1,
"activationStatus": 1,
"terminated": null,
"department": "",
"workFrom": "2017-07-11T20:22:53.0000000+03:00",
"displayName": "Safronov Alexey",
"contacts": [
{
"type": "mail",
"value": "alexey.safronov@onlyoffice.com"
}
],
"avatarMedium": "/images/default_user_photo_size_48-48.png?_=-48038267",
"avatar": "/images/default_user_photo_size_82-82.png?_=-48038267",
"isAdmin": false,
"isLDAP": true,
"isOwner": false,
"isSSO": false,
"avatarSmall": "/images/default_user_photo_size_32-32.png?_=-48038267",
"profileUrl": "http://localhost:8092/products/people/profile.aspx?user=alexey.safronov"
},
{
"id": "646a6cff-df57-4b83-8ffe-91a24910328c",
"userName": "Nikolay.Ivanov",
"isVisitor": false,
"firstName": "Nikolay",
"lastName": "Ivanov",
"email": "profi.troll@outlook.com",
"birthday": "1983-09-15T04:00:00.0000000+04:00",
"sex": "male",
"status": 1,
"activationStatus": 1,
"terminated": null,
"department": "",
"workFrom": "2007-10-03T04:00:00.0000000+04:00",
"displayName": "Ivanov Nikolay",
"mobilePhone": "79081612979",
"avatarMedium": "/images/default_user_photo_size_48-48.png?_=-562774739",
"avatar": "/images/default_user_photo_size_82-82.png?_=-562774739",
"isAdmin": true,
"isLDAP": false,
"listAdminModules": [
"people"
],
"isOwner": true,
"isSSO": false,
"avatarSmall": "/images/default_user_photo_size_32-32.png?_=-562774739",
"profileUrl": "http://localhost:8092/products/people/profile.aspx?user=nikolay.ivanov"
}
];
return fakeResponse(data);
};
export function getGroups() {
const data = [
{
"id": "0824d8a0-d860-46df-8142-9313bb298a5c",
"name": "Отдел продвижения и рекламы",
"manager": null
},
{
"id": "098f3dac-92bd-455f-8138-966313c098da",
"name": "Группа интернет-рекламы",
"manager": "galina.medvedeva"
},
{
"id": "1518cad6-c2b9-4644-bcb9-3fc816714ecc",
"name": "Отдел тестирования",
"manager": null
},
{
"id": "1d42a4fb-755e-44ab-bcf5-38482c9b2415",
"name": "Отдел программирования форматов",
"manager": "Sergey.Kirillov"
},
{
"id": "36800583-b347-4646-b303-65d969fabec1",
"name": "Рига",
"manager": "Kate.Osipova"
},
{
"id": "3b99e536-7b68-44c4-8d44-6e745fe48348",
"name": "Группа проектирования",
"manager": "Anna.Medvedeva"
},
{
"id": "40e2b7b4-bdb8-43f8-a012-5ab382754dba",
"name": "Группа технической поддержки клиентов",
"manager": "Alexey.Micheev"
},
{
"id": "5fd54d7e-aff8-435f-85d0-af0e2129be85",
"name": "Группа PR и продвижения",
"manager": "Alexander.Galkin"
},
{
"id": "613fc896-3ddd-4de1-a567-edbbc6cf1fc8",
"name": "Администрация",
"manager": "Lev.Bannov"
},
{
"id": "70fe34d0-e589-4810-bcf8-f3a791db20bf",
"name": "Отдел технической документации",
"manager": "Alexander.Vnuchkov"
},
{
"id": "72940d26-57f7-4994-aff9-e505e48558d4",
"name": "Даллас",
"manager": null
},
{
"id": "cc8eea30-1260-427e-83c4-ff9e9680edba",
"name": "Отдел интернет-приложений",
"manager": "Alex.Bannov"
},
{
"id": "f34754ff-ba4f-4f5e-bc35-1ea2921b2c44",
"name": "Отдел продаж",
"manager": "galina.goduhina"
},
{
"id": "ff00f2ea-2960-4fe9-b477-6a5a6ab14187",
"name": "Группа по работе с клиентами",
"manager": "Evgenia.Olshanskaja"
}
];
return fakeResponse(data);
}

View File

@ -1874,7 +1874,7 @@ asap@~2.0.3, asap@~2.0.6:
react-custom-scrollbars "^4.2.1"
react-datepicker "^2.7.0"
react-lifecycles-compat "^3.0.4"
react-toastify "^5.3.1"
react-toastify "^5.3.2"
reactstrap "^8.0.0"
styled-components "^4.3.2"
@ -8885,7 +8885,7 @@ react-scripts@3.0.1:
optionalDependencies:
fsevents "2.0.6"
react-toastify@^5.3.1:
react-toastify@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-5.3.2.tgz#9de4b426656587722f7e2f99f5f90e974ba5fee6"
integrity sha512-YHTTey7JWqXVkkBIeJ34PAvQELmGfLEGCx9bu68aIZYd+kRU2u9k/nG3AydgbX/uevIb4QNpeeE98DjkooMs5w==

View File

@ -19,13 +19,14 @@ namespace ASC.Employee.Core.Controllers
public class GroupController : ControllerBase
{
public Common.Logging.LogManager LogManager { get; }
public ApiContext ApiContext { get; }
private ApiContext apiContext;
public ApiContext ApiContext { get { return apiContext ?? (apiContext = HttpContext); } }
public MessageService MessageService { get; }
public GroupController(Common.Logging.LogManager logManager, MessageService messageService)
{
LogManager = logManager;
ApiContext = HttpContext;
MessageService = messageService;
}

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Security;
using System.Threading.Tasks;
using ASC.Api.Core;
using ASC.Common.Web;
using ASC.Core;
@ -28,10 +28,12 @@ using ASC.Web.Studio.Core;
using ASC.Web.Studio.Core.Notify;
using ASC.Web.Studio.UserControls.Statistics;
using ASC.Web.Studio.Utility;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using SecurityContext = ASC.Core.SecurityContext;
namespace ASC.Employee.Core.Controllers
@ -41,7 +43,9 @@ namespace ASC.Employee.Core.Controllers
public class PeopleController : ControllerBase
{
public Common.Logging.LogManager LogManager { get; }
public ApiContext ApiContext { get; }
private ApiContext apiContext;
public ApiContext ApiContext { get { return apiContext ?? (apiContext = HttpContext); } }
public MessageService MessageService { get; }
public QueueWorkerReassign QueueWorkerReassign { get; }
public QueueWorkerRemove QueueWorkerRemove { get; }
@ -49,7 +53,6 @@ namespace ASC.Employee.Core.Controllers
public PeopleController(Common.Logging.LogManager logManager, MessageService messageService, QueueWorkerReassign queueWorkerReassign, QueueWorkerRemove queueWorkerRemove)
{
LogManager = logManager;
ApiContext = HttpContext;
MessageService = messageService;
QueueWorkerReassign = queueWorkerReassign;
QueueWorkerRemove = queueWorkerRemove;

View File

@ -1874,7 +1874,7 @@ asap@~2.0.3, asap@~2.0.6:
react-custom-scrollbars "^4.2.1"
react-datepicker "^2.7.0"
react-lifecycles-compat "^3.0.4"
react-toastify "^5.3.1"
react-toastify "^5.3.2"
reactstrap "^8.0.0"
styled-components "^4.3.2"
@ -8885,7 +8885,7 @@ react-scripts@3.0.1:
optionalDependencies:
fsevents "2.0.6"
react-toastify@^5.3.1:
react-toastify@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-5.3.2.tgz#9de4b426656587722f7e2f99f5f90e974ba5fee6"
integrity sha512-YHTTey7JWqXVkkBIeJ34PAvQELmGfLEGCx9bu68aIZYd+kRU2u9k/nG3AydgbX/uevIb4QNpeeE98DjkooMs5w==

View File

@ -78,9 +78,11 @@ class ComboBox extends React.Component {
isReadOnly={true}
onIconClick={() => false}
>
<DropDown direction={this.props.direction || 'left'}
<DropDown
directionX={this.props.directionX}
directionY={this.props.directionY}
manualWidth='100%'
manualTop='102%'
manualY='102%'
isOpen={this.state.isOpen}
>
{this.state.items.map(item =>

View File

@ -129,7 +129,7 @@ class ContentRow extends React.Component {
<StyledContent>{children}</StyledContent>
{contextOptions &&
<StyledOptionButton>
<ContextMenuButton direction='right' getData={() => contextOptions} />
<ContextMenuButton directionX='right' getData={() => contextOptions} />
</StyledOptionButton>
}
</StyledContentRow>

View File

@ -71,7 +71,7 @@ class ContextMenuButton extends React.Component {
onMouseOver={this.props.onMouseOver}
onMouseOut={this.props.onMouseOut}
/>
<DropDown direction={this.props.direction || 'left'} isOpen={this.state.isOpen}>
<DropDown directionX={this.props.directionX || 'left'} isOpen={this.state.isOpen}>
{
this.state.data.map(item =>
<DropDownItem

View File

@ -11,9 +11,10 @@ const StyledDropdown = styled.div`
position: absolute;
${props => props.manualWidth && `width: ${props.manualWidth};`}
top: ${props => props.manualTop ? props.manualTop : '100%'};
${props => (props.direction === 'right' && css`right: 0px;`)}
${props => (props.direction === 'left' && css`left: 0px;`)}
${props => (props.directionY === 'top' && css`bottom: ${props => props.manualY ? props.manualY : '100%'};`)}
${props => (props.directionY === 'bottom' && css`top: ${props => props.manualY ? props.manualY : '100%'};`)}
${props => (props.directionX === 'right' && css`right: 0px;`)}
${props => (props.directionX === 'left' && css`left: 0px;`)}
z-index: 1000;
margin-top: ${props => (props.isUserPreview ? '6px' : '0px')};
margin-right: ${props => (props.isUserPreview ? '6px' : '0px')};
@ -30,16 +31,17 @@ const StyledDropdown = styled.div`
const Arrow = styled.div`
position: absolute;
top: -6px;
${props => (props.direction === 'right' && css`right: 16px;`)}
${props => (props.direction === 'left' && css`left: 16px;`)}
${props => (props.directionX === 'right' && css`right: 16px;`)}
${props => (props.directionX === 'left' && css`left: 16px;`)}
width: 24px;
height: 6px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M9.27954 1.12012C10.8122 -0.295972 13.1759 -0.295971 14.7086 1.12012L18.8406 4.93793C19.5796 5.62078 20.5489 6 21.5551 6H24H0H2.43299C3.4392 6 4.40845 5.62077 5.1475 4.93793L9.27954 1.12012Z' fill='%23206FA4'/%3E%3C/svg%3E");
`;
`;
const DropDown = props => {
return (
<StyledDropdown {...props}>
{props.withArrow && <Arrow direction={props.direction} />}
{props.withArrow && <Arrow directionX={props.directionX} />}
{React.Children.map(props.children, (child) =>
<DropDownItem {...child.props}/>
)}
@ -48,14 +50,16 @@ const DropDown = props => {
};
DropDown.propTypes = {
direction: PropTypes.oneOf(['left', 'right']),
directionX: PropTypes.oneOf(['left', 'right']),
directionY: PropTypes.oneOf(['bottom', 'top']),
withArrow: PropTypes.bool,
manualWidth: PropTypes.string,
manualTop: PropTypes.string
manualY: PropTypes.string
};
DropDown.defaultProps = {
direction: 'left',
directionX: 'left',
directionY: 'bottom',
withArrow: false
};

View File

@ -31,7 +31,7 @@ const StyledPage = styled.div`
`;
const Paging = props => {
const {previousLabel, nextLabel, previousAction, nextAction, pageItems, perPageItems} = props;
const {previousLabel, nextLabel, previousAction, nextAction, pageItems, perPageItems, openDirection} = props;
return (
<StyledPaging>
@ -39,13 +39,13 @@ const Paging = props => {
<>
<Button size='medium' label={previousLabel} onClick={previousAction}/>
<StyledPage>
<ComboBox items={pageItems} />
<ComboBox directionY={openDirection} items={pageItems} />
</StyledPage>
<Button size='medium' label={nextLabel} onClick={nextAction}/>
</>
}
<StyledOnPage>
<ComboBox items={perPageItems} />
<ComboBox directionY={openDirection} items={perPageItems} />
</StyledOnPage>
</StyledPaging>
);

View File

@ -29,8 +29,7 @@ const toastr = {
};
const notify = (type, text, title, autoClosed = true) => {
const notify = (type, text, title, timeout = 5000, withCross = false, centerPosition = false) => {
return toast(
<>
<div>
@ -43,27 +42,28 @@ const notify = (type, text, title, autoClosed = true) => {
</>,
{
type: type,
closeOnClick: autoClosed,
closeButton: !autoClosed,
autoClose: autoClosed
closeOnClick: !withCross,
closeButton: withCross,
autoClose: timeout === 0 ? false : timeout < 0 ? 5000 : (timeout || 5000),
position: centerPosition && toast.POSITION.TOP_CENTER
}
);
};
function success(text, title, autoClosed) {
return notify('success', text, title, autoClosed);
function success(text, title, timeout, withCross, centerPosition) {
return notify('success', text, title, timeout, withCross, centerPosition);
}
function error(text, title, autoClosed) {
return notify('error', text, title, autoClosed);
function error(text, title, timeout, withCross, centerPosition) {
return notify('error', text, title, timeout, withCross, centerPosition);
}
function warning(text, title, autoClosed) {
return notify('warning', text, title, autoClosed);
function warning(text, title, timeout, withCross, centerPosition) {
return notify('warning', text, title, timeout, withCross, centerPosition);
}
function info(text, title, autoClosed) {
return notify('info', text, title, autoClosed);
function info(text, title, timeout, withCross, centerPosition) {
return notify('info', text, title, timeout, withCross, centerPosition);
}
function clear() {

View File

@ -22,3 +22,4 @@ import { Paging } from 'asc-web-components';
| `nextLabel` | `string` | - | - | `Next` | Label for next button |
| `previousAction` | `function` | - | - | - | Action for previous button |
| `nextAction` | `function` | - | - | - | Action for next button |
| `openDirection` | `string` | - | `top`, `bottom` | `bottom`| Indicates opening direction of combo box |

View File

@ -11,22 +11,27 @@ storiesOf('Components|Paging', module)
const pageItems = [
{
key: '1',
label: '1 of 5',
onClick: () => console.log('set paging 1 of 5')
},
{
key: '2',
label: '2 of 5',
onClick: () => console.log('set paging 2 of 5')
},
{
key: '3',
label: '3 of 5',
onClick: () => console.log('set paging 3 of 5')
},
{
key: '4',
label: '4 of 5',
onClick: () => console.log('set paging 4 of 5')
},
{
key: '5',
label: '5 of 5',
onClick: () => console.log('set paging 5 of 5')
}
@ -34,14 +39,17 @@ storiesOf('Components|Paging', module)
const perPageItems = [
{
key: '1-1',
label: '25 per page',
onClick: () => console.log('set paging 25 action')
},
{
key: '1-2',
label: '50 per page',
onClick: () => console.log('set paging 50 action')
},
{
key: '1-3',
label: '100 per page',
onClick: () => console.log('set paging 100 action')
}
@ -55,6 +63,7 @@ storiesOf('Components|Paging', module)
perPageItems={perPageItems}
previousAction={ () => console.log('Prev')}
nextAction={ () => console.log('Next')}
openDirection='bottom'
/>
</Section>
)

View File

@ -9,33 +9,33 @@ storiesOf('Components|Toast', module)
.add('all', () => {
return (
<>
<Toast/>
<Section>
<button onClick = {()=> {
toastr.success('Demo text for success Toast');
toastr.error('Demo text for error Toast');
toastr.warning('Demo text for warning Toast');
toastr.info('Demo text for info Toast');
<Section>
toastr.success('Demo text for success Toast with title', 'Demo title');
toastr.error('Demo text for error Toast with title', 'Demo title');
toastr.warning('Demo text for warning Toast with title', 'Demo title');
toastr.info('Demo text for info Toast with title', 'Demo title');
<Toast>
toastr.success('Demo text for success manual closed Toast', null, false);
toastr.error('Demo text for error manual closed Toast', null, false);
toastr.warning('Demo text for warning manual closed Toast', null, false);
toastr.info('Demo text for info manual closed Toast', null, false);
{toastr.success('Demo text for success Toast closes in 30 seconds or on click', null, 30000)}
{toastr.error('Demo text for error Toast closes in 28 seconds or on click', null, 28000)}
{toastr.warning('Demo text for warning Toast closes in 25 seconds or on click', null, 25000)}
{toastr.info('Demo text for info Toast closes in 15 seconds or on click', null, 15000)}
toastr.success('Demo text for success manual closed Toast with title', 'Demo title', false);
toastr.error('Demo text for error manual closed Toast with title', 'Demo title', false);
toastr.warning('Demo text for warning manual closed Toast with title', 'Demo title', false);
toastr.info('Demo text for info manual closed Toast with title', 'Demo title', false);
{toastr.success('Demo text for success Toast with title closes in 12 seconds or on click', 'Demo title', 12000)}
{toastr.error('Demo text for error Toast with title closes in 10 seconds or on click', 'Demo title', 10000)}
{toastr.warning('Demo text for warning Toast with title closes in 8 seconds or on click', 'Demo title', 8000)}
{toastr.info('Demo text for info Toast with title closes in 6 seconds or on click', 'Demo title', 6000)}
}}>Show all Toastr</button>
{toastr.success('Demo text for success manual closed Toast', null, 0, true, true)}
{toastr.error('Demo text for error manual closed Toast', null, 0, true, true)}
{toastr.warning('Demo text for warning manual closed Toast', null, 0, true, true)}
{toastr.info('Demo text for info manual closed Toast', null, 0, true, true)}
</Section>
</>
);
});
{toastr.success('Demo text for success manual closed Toast with title', 'Demo title', 0, true, true)}
{toastr.error('Demo text for error manual closed Toast with title', 'Demo title', 0, true, true)}
{toastr.warning('Demo text for warning manual closed Toast with title', 'Demo title', 0, true, true)}
{toastr.info('Demo text for info manual closed Toast with title', 'Demo title', 0, true, true)}
</Toast>
</Section>
</>
);
});

View File

@ -35,7 +35,8 @@ or
| `type` | `oneOf` | ✅ | success, error, warning, info | - | Define color and icon of toast |
| `text` | `string` | - | - | - | Text inside a toast |
| `title` | `string` | - | - | - | Title inside a toast |
| `autoClosed` | `bool` | - | true, false | `true`|If `true`: toast disappeared after 5 seconds or after clicking on any area of toast. If `false`: included close button, toast can be closed by clicking on it.|
| `withCross` | `bool` | - | true, false | `false`|If `false`: toast disappeared after clicking on any area of toast. If `true`: toast disappeared after clicking on close button|
| `timeout` | `number` | - | all positive numbers | `5000`|Time (in milliseconds) for showing your toast. Setting in `0` let you to show toast constantly until clicking on it|
#### Other Options
```js

View File

@ -2,7 +2,7 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import { Toast, toastr } from 'asc-web-components';
import Readme from './README.md';
import { text, boolean, withKnobs, select } from '@storybook/addon-knobs/react';
import { text, boolean, withKnobs, select, number } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme';
import Section from '../../../.storybook/decorators/section';
@ -15,8 +15,8 @@ storiesOf('Components|Toast', module)
const toastType = `${select('type', type, 'success')}`;
const toastText = `${text('text', 'Demo text for Toast')}`;
const titleToast = `${text('title', 'Demo title')}`;
const autoClosed = `${boolean('autoClosed', true)}`;
const withCross = `${boolean('withCross', false)}`;
const timeout = `${number('timeout', '5000')}`;
return (
<>
<Toast />
@ -24,16 +24,16 @@ storiesOf('Components|Toast', module)
<button onClick={() => {
switch (toastType) {
case 'error':
toastr.error(toastText, titleToast, JSON.parse(autoClosed));
toastr.error(toastText, titleToast, JSON.parse(timeout), JSON.parse(withCross));
break;
case 'warning':
toastr.warning(toastText, titleToast, JSON.parse(autoClosed));
toastr.warning(toastText, titleToast, JSON.parse(timeout), JSON.parse(withCross));
break;
case 'info':
toastr.info(toastText, titleToast, JSON.parse(autoClosed));
toastr.info(toastText, titleToast, JSON.parse(timeout), JSON.parse(withCross));
break;
default:
toastr.success(toastText, titleToast, JSON.parse(autoClosed));
toastr.success(toastText, titleToast, JSON.parse(timeout), JSON.parse(withCross));
break;
}
}}>