Merge pull request #312 from ONLYOFFICE/feature/table-view
Feature/table view
This commit is contained in:
commit
43ee204e80
@ -161,7 +161,9 @@ export function getFoldersTree() {
|
||||
// : null,
|
||||
folders: null,
|
||||
pathParts: data.pathParts,
|
||||
foldersCount: !isRecycleBinFolder ? data.current.foldersCount : null,
|
||||
foldersCount: !isRecycleBinFolder
|
||||
? data.current.foldersCount || data.folders.length
|
||||
: null,
|
||||
newItems: data.new,
|
||||
};
|
||||
});
|
||||
|
@ -832,6 +832,7 @@ class FilterInput extends React.Component {
|
||||
isMobile,
|
||||
sectionWidth,
|
||||
getViewSettingsData,
|
||||
viewSelectorVisible,
|
||||
} = this.props;
|
||||
/* eslint-enable react/prop-types */
|
||||
|
||||
@ -911,31 +912,33 @@ class FilterInput extends React.Component {
|
||||
</SearchInput>
|
||||
</div>
|
||||
<div ref={this.rectComboBoxRef}>
|
||||
<SortComboBox
|
||||
options={getSortData()}
|
||||
viewSettings={this.state.viewSettings}
|
||||
isDisabled={isDisabled}
|
||||
onChangeSortId={this.onClickSortItem}
|
||||
onChangeView={this.props.onChangeViewAs}
|
||||
onChangeSortDirection={this.onChangeSortDirection}
|
||||
selectedOption={
|
||||
getSortData().length > 0
|
||||
? getSortData().find((x) => x.key === sortId)
|
||||
: {}
|
||||
}
|
||||
onButtonClick={this.onSortDirectionClick}
|
||||
viewAs={viewAs}
|
||||
sortDirection={+sortDirection}
|
||||
directionAscLabel={directionAscLabel}
|
||||
directionDescLabel={directionDescLabel}
|
||||
/>
|
||||
{viewAs !== "table" && (
|
||||
<SortComboBox
|
||||
options={getSortData()}
|
||||
viewSettings={this.state.viewSettings}
|
||||
isDisabled={isDisabled}
|
||||
onChangeSortId={this.onClickSortItem}
|
||||
onChangeView={this.props.onChangeViewAs}
|
||||
onChangeSortDirection={this.onChangeSortDirection}
|
||||
selectedOption={
|
||||
getSortData().length > 0
|
||||
? getSortData().find((x) => x.key === sortId)
|
||||
: {}
|
||||
}
|
||||
onButtonClick={this.onSortDirectionClick}
|
||||
viewAs={viewAs}
|
||||
sortDirection={+sortDirection}
|
||||
directionAscLabel={directionAscLabel}
|
||||
directionDescLabel={directionDescLabel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{viewAs && !isMobileOnly && (
|
||||
{viewAs && !isMobileOnly && viewSelectorVisible && (
|
||||
<ViewSelector
|
||||
className="view-selector-button"
|
||||
className="view-selector-button not-selectable"
|
||||
isDisabled={isDisabled}
|
||||
onChangeView={this.props.onChangeViewAs}
|
||||
viewAs={viewAs}
|
||||
viewAs={viewAs === "table" ? "row" : viewAs}
|
||||
viewSettings={this.state.viewSettings}
|
||||
/>
|
||||
)}
|
||||
@ -976,6 +979,7 @@ FilterInput.defaultProps = {
|
||||
needForUpdate: false,
|
||||
directionAscLabel: "A-Z",
|
||||
directionDescLabel: "Z-A",
|
||||
viewSelectorVisible: true,
|
||||
};
|
||||
|
||||
export default FilterInput;
|
||||
|
@ -166,7 +166,11 @@ class PageLayout extends React.Component {
|
||||
(x) => x.classList && x.classList.contains("not-selectable")
|
||||
);
|
||||
|
||||
if (notSelectablePath || isBackdrop) {
|
||||
const isDraggable = path.some(
|
||||
(x) => x.classList && x.classList.contains("draggable")
|
||||
);
|
||||
|
||||
if (notSelectablePath || isBackdrop || isDraggable) {
|
||||
return false;
|
||||
} else return true;
|
||||
};
|
||||
@ -191,7 +195,6 @@ class PageLayout extends React.Component {
|
||||
//withBodyAutoFocus,
|
||||
withBodyScroll,
|
||||
children,
|
||||
isLoaded,
|
||||
isHeaderVisible,
|
||||
//headerBorderBottom,
|
||||
onOpenUploadPanel,
|
||||
@ -277,7 +280,6 @@ class PageLayout extends React.Component {
|
||||
<Article
|
||||
visible={isArticleVisible}
|
||||
pinned={isArticlePinned}
|
||||
isLoaded={isLoaded}
|
||||
firstLoad={firstLoad}
|
||||
>
|
||||
{isArticleHeaderAvailable && (
|
||||
@ -489,7 +491,6 @@ PageLayout.propTypes = {
|
||||
setSelections: PropTypes.func,
|
||||
uploadFiles: PropTypes.bool,
|
||||
hideAside: PropTypes.bool,
|
||||
isLoaded: PropTypes.bool,
|
||||
viewAs: PropTypes.string,
|
||||
uploadPanelVisible: PropTypes.bool,
|
||||
onOpenUploadPanel: PropTypes.func,
|
||||
|
@ -56,6 +56,7 @@ class Checkbox extends React.Component {
|
||||
}
|
||||
|
||||
onInputChange(e) {
|
||||
e.stopPropagation();
|
||||
this.setState({ checked: e.target.checked });
|
||||
this.props.onChange && this.props.onChange(e);
|
||||
}
|
||||
|
@ -12,9 +12,19 @@ const DragAndDrop = (props) => {
|
||||
acceptedFiles.length && props.onDrop && props.onDrop(acceptedFiles);
|
||||
};
|
||||
|
||||
const onDragOver = (e) => {
|
||||
props.onDragOver && props.onDragOver(isDragActive, e);
|
||||
};
|
||||
|
||||
const onDragLeave = (e) => {
|
||||
props.onDragLeave && props.onDragLeave(e);
|
||||
};
|
||||
|
||||
const { getRootProps, isDragActive } = useDropzone({
|
||||
noDragEventsBubbling: !isDropZone,
|
||||
onDrop,
|
||||
onDragOver,
|
||||
onDragLeave,
|
||||
});
|
||||
|
||||
return (
|
||||
@ -44,6 +54,8 @@ DragAndDrop.propTypes = {
|
||||
onMouseDown: PropTypes.func,
|
||||
/** Occurs when the dragged element is dropped on the drop target */
|
||||
onDrop: PropTypes.func,
|
||||
onDragOver: PropTypes.func,
|
||||
onDragLeave: PropTypes.func,
|
||||
};
|
||||
|
||||
export default DragAndDrop;
|
||||
|
@ -62,3 +62,4 @@ export { default as DragAndDrop } from "./drag-and-drop";
|
||||
export { default as ViewSelector } from "./view-selector";
|
||||
export * as Themes from "./themes";
|
||||
export { default as Portal } from "./portal";
|
||||
export { default as TableContainer } from "./TableContainer";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import {
|
||||
TabletSideInfo,
|
||||
@ -46,7 +47,6 @@ const RowContent = (props) => {
|
||||
sideColor,
|
||||
onClick,
|
||||
sectionWidth,
|
||||
isMobile,
|
||||
convertSideInfo,
|
||||
} = props;
|
||||
|
||||
|
@ -0,0 +1,237 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import Base from "../themes/base";
|
||||
|
||||
const HeaderStyles = css`
|
||||
height: 39px;
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
z-index: 1;
|
||||
border-bottom: 1px solid #eceef1;
|
||||
`;
|
||||
|
||||
const StyledTableContainer = styled.div`
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
margin-top: -18px;
|
||||
|
||||
display: grid;
|
||||
|
||||
.table-column {
|
||||
user-select: none;
|
||||
position: relative;
|
||||
min-width: 10%;
|
||||
}
|
||||
|
||||
.resize-handle {
|
||||
display: block;
|
||||
cursor: ew-resize;
|
||||
height: 10px;
|
||||
margin: 14px 8px 0 auto;
|
||||
z-index: 1;
|
||||
border-right: 2px solid #d0d5da;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.children-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
height: 47px;
|
||||
border-bottom: 1px solid #eceef1;
|
||||
}
|
||||
|
||||
.table-container_group-menu {
|
||||
.table-container_group-menu-checkbox {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.table-container_group-menu-combobox {
|
||||
height: 24px;
|
||||
width: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.combo-button {
|
||||
height: 24px;
|
||||
margin-top: 8px;
|
||||
width: 16px;
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
margin: 8px 16px 0 0;
|
||||
|
||||
/* svg {
|
||||
path {
|
||||
fill: #333;
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-container_group-menu-separator {
|
||||
border-right: 1px solid #eceef1;
|
||||
width: 2px;
|
||||
height: 10px;
|
||||
margin: 0 8px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableGroupMenu = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: ${(props) => props.width};
|
||||
|
||||
${HeaderStyles}
|
||||
|
||||
.table-container_group-menu-checkbox {
|
||||
${(props) => props.checkboxMargin && `margin-left: ${props.checkboxMargin}`}
|
||||
}
|
||||
|
||||
.table-container_group-menu_button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableHeader = styled.div`
|
||||
display: grid;
|
||||
|
||||
${HeaderStyles}
|
||||
|
||||
.table-container_header-checkbox {
|
||||
${(props) => props.checkboxMargin && `margin-left: ${props.checkboxMargin}`}
|
||||
}
|
||||
|
||||
.table-container_header-cell {
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableHeaderCell = styled.div`
|
||||
.table-container_header-item {
|
||||
display: flex;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.header-container-text-wrapper {
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
.header-container-text-icon {
|
||||
padding: 16px 0 0 4px;
|
||||
|
||||
display: ${(props) => (props.isActive ? "block" : "none")};
|
||||
${(props) =>
|
||||
props.sorted &&
|
||||
css`
|
||||
transform: scale(1, -1);
|
||||
padding: 14px 0 0 4px;
|
||||
`}
|
||||
}
|
||||
|
||||
:hover {
|
||||
.header-container-text-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-container-text {
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableBody = styled.div`
|
||||
display: contents;
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled.div`
|
||||
display: contents;
|
||||
|
||||
.table-container_header-checkbox {
|
||||
svg {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.droppable-hover {
|
||||
background: ${(props) =>
|
||||
props.dragging
|
||||
? `${props.theme.dragAndDrop.acceptBackground} !important`
|
||||
: "none"};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTableCell = styled.div`
|
||||
/* padding-right: 8px; */
|
||||
height: 40px;
|
||||
max-height: 40px;
|
||||
border-bottom: 1px solid #eceef1;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.react-svg-icon svg {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.table-container_element {
|
||||
display: ${(props) => (props.checked ? "none" : "flex")};
|
||||
}
|
||||
.table-container_row-checkbox {
|
||||
display: ${(props) => (props.checked ? "flex" : "none")};
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.hasAccess &&
|
||||
css`
|
||||
:hover {
|
||||
.table-container_element {
|
||||
display: none;
|
||||
}
|
||||
.table-container_row-checkbox {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledTableSettings = styled.div`
|
||||
margin: 14px 0 0px 8px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
.table-container_settings-checkbox {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledEmptyTableContainer = styled.div`
|
||||
grid-column-start: 1;
|
||||
grid-column-end: -1;
|
||||
height: 40px;
|
||||
`;
|
||||
|
||||
StyledTableRow.defaultProps = { theme: Base };
|
||||
|
||||
export {
|
||||
StyledTableContainer,
|
||||
StyledTableRow,
|
||||
StyledTableBody,
|
||||
StyledTableHeader,
|
||||
StyledTableHeaderCell,
|
||||
StyledTableCell,
|
||||
StyledTableSettings,
|
||||
StyledTableGroupMenu,
|
||||
StyledEmptyTableContainer,
|
||||
};
|
8
packages/asc-web-components/table-container/TableBody.js
Normal file
8
packages/asc-web-components/table-container/TableBody.js
Normal file
@ -0,0 +1,8 @@
|
||||
import React from "react";
|
||||
import { StyledTableBody } from "./StyledTableContainer";
|
||||
|
||||
const TableBody = (props) => {
|
||||
return <StyledTableBody className="table-container_body" {...props} />;
|
||||
};
|
||||
|
||||
export default TableBody;
|
20
packages/asc-web-components/table-container/TableCell.js
Normal file
20
packages/asc-web-components/table-container/TableCell.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { StyledTableCell } from "./StyledTableContainer";
|
||||
|
||||
const TableCell = ({ className, forwardedRef, ...rest }) => {
|
||||
return (
|
||||
<StyledTableCell
|
||||
className={`${className} table-container_cell`}
|
||||
ref={forwardedRef}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
TableCell.propTypes = {
|
||||
className: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
forwardedRef: PropTypes.shape({ current: PropTypes.any }),
|
||||
};
|
||||
|
||||
export default TableCell;
|
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { StyledTableContainer } from "./StyledTableContainer";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const TableContainer = (props) => {
|
||||
return (
|
||||
<StyledTableContainer
|
||||
id="table-container"
|
||||
className="table-container"
|
||||
ref={props.forwardedRef}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
TableContainer.propTypes = {
|
||||
forwardedRef: PropTypes.shape({ current: PropTypes.any }),
|
||||
};
|
||||
|
||||
export default TableContainer;
|
@ -0,0 +1,85 @@
|
||||
import React, { useEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Checkbox from "../checkbox";
|
||||
import { StyledTableGroupMenu } from "./StyledTableContainer";
|
||||
import Button from "../button";
|
||||
import ComboBox from "../combobox";
|
||||
|
||||
const TableGroupMenu = (props) => {
|
||||
const {
|
||||
isChecked,
|
||||
isIndeterminate,
|
||||
headerMenu,
|
||||
containerRef,
|
||||
onChange,
|
||||
checkboxOptions,
|
||||
columnStorageName,
|
||||
checkboxMargin,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const onCheckboxChange = (e) => {
|
||||
onChange && onChange(e.target && e.target.checked);
|
||||
};
|
||||
|
||||
const width = containerRef.current
|
||||
? containerRef.current.clientWidth + "px"
|
||||
: "100%";
|
||||
|
||||
useEffect(() => {
|
||||
const storageSize = localStorage.getItem(columnStorageName);
|
||||
if (containerRef.current)
|
||||
containerRef.current.style.gridTemplateColumns = storageSize;
|
||||
}, [containerRef]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTableGroupMenu
|
||||
width={width}
|
||||
className="table-container_group-menu"
|
||||
checkboxMargin={checkboxMargin}
|
||||
{...rest}
|
||||
>
|
||||
<Checkbox
|
||||
className="table-container_group-menu-checkbox"
|
||||
onChange={onCheckboxChange}
|
||||
isChecked={isChecked}
|
||||
isIndeterminate={isIndeterminate}
|
||||
/>
|
||||
<ComboBox
|
||||
advancedOptions={checkboxOptions}
|
||||
className="table-container_group-menu-combobox not-selectable"
|
||||
options={[]}
|
||||
selectedOption={{}}
|
||||
/>
|
||||
<div className="table-container_group-menu-separator" />
|
||||
{headerMenu.map((item, index) => {
|
||||
const { label, disabled, onClick } = item;
|
||||
return (
|
||||
<Button
|
||||
key={index}
|
||||
className="table-container_group-menu_button not-selectable"
|
||||
isDisabled={disabled}
|
||||
onClick={onClick}
|
||||
label={label}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</StyledTableGroupMenu>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
TableGroupMenu.propTypes = {
|
||||
isChecked: PropTypes.bool,
|
||||
isIndeterminate: PropTypes.bool,
|
||||
headerMenu: PropTypes.arrayOf(PropTypes.object),
|
||||
checkboxOptions: PropTypes.any.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
containerRef: PropTypes.shape({ current: PropTypes.any }),
|
||||
columnStorageName: PropTypes.string,
|
||||
checkboxMargin: PropTypes.string,
|
||||
};
|
||||
|
||||
export default TableGroupMenu;
|
417
packages/asc-web-components/table-container/TableHeader.js
Normal file
417
packages/asc-web-components/table-container/TableHeader.js
Normal file
@ -0,0 +1,417 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import throttle from "lodash.throttle";
|
||||
import {
|
||||
StyledTableHeader,
|
||||
StyledTableRow,
|
||||
StyledEmptyTableContainer,
|
||||
} from "./StyledTableContainer";
|
||||
import Checkbox from "../checkbox";
|
||||
import TableSettings from "./TableSettings";
|
||||
import TableHeaderCell from "./TableHeaderCell";
|
||||
import { size } from "../utils/device";
|
||||
import TableGroupMenu from "./TableGroupMenu";
|
||||
|
||||
const minColumnSize = 90;
|
||||
const settingsSize = 24;
|
||||
|
||||
class TableHeader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { columnIndex: null };
|
||||
|
||||
this.headerRef = React.createRef();
|
||||
this.throttledResize = throttle(this.onResize, 300);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.onResize();
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.sectionWidth >= size.tablet + 24) {
|
||||
this.onResize();
|
||||
}
|
||||
}
|
||||
|
||||
getSubstring = (str) => +str.substring(0, str.length - 2);
|
||||
|
||||
getNextColumn = (array, index) => {
|
||||
let i = 1;
|
||||
while (array.length !== i) {
|
||||
const item = array[index + i];
|
||||
|
||||
if (!item) return null;
|
||||
else if (!item.enable) i++;
|
||||
else return item;
|
||||
}
|
||||
};
|
||||
|
||||
getColumn = (array, index) => {
|
||||
let i = 1;
|
||||
while (array.length !== i) {
|
||||
const item = array[index + i];
|
||||
if (!item) return [0, i];
|
||||
else if (item === "0px") i++;
|
||||
else return [this.getSubstring(item), i];
|
||||
}
|
||||
};
|
||||
|
||||
moveToLeft = (widths, newWidth, index) => {
|
||||
const { columnIndex } = this.state;
|
||||
|
||||
let leftColumn;
|
||||
let colIndex = index ? index : columnIndex - 1;
|
||||
|
||||
if (colIndex === 0) return;
|
||||
|
||||
while (colIndex !== 0) {
|
||||
leftColumn = document.getElementById("column_" + colIndex);
|
||||
if (leftColumn) {
|
||||
if (leftColumn.dataset.enable === "true") break;
|
||||
else colIndex--;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
const minSize = leftColumn.dataset.minWidth
|
||||
? leftColumn.dataset.minWidth
|
||||
: minColumnSize;
|
||||
|
||||
if (leftColumn.clientWidth <= minSize) {
|
||||
if (colIndex === 1) return false;
|
||||
return this.moveToLeft(widths, newWidth, colIndex - 1);
|
||||
}
|
||||
|
||||
const offset = this.getSubstring(widths[+columnIndex]) - newWidth;
|
||||
const column2Width = this.getSubstring(widths[colIndex]);
|
||||
|
||||
const leftColumnWidth = column2Width - offset;
|
||||
const newLeftWidth = leftColumnWidth < minSize ? minSize : leftColumnWidth;
|
||||
|
||||
widths[colIndex] = newLeftWidth + "px";
|
||||
widths[+columnIndex] =
|
||||
this.getSubstring(widths[+columnIndex]) +
|
||||
(offset - (newLeftWidth - leftColumnWidth)) +
|
||||
"px";
|
||||
};
|
||||
|
||||
moveToRight = (widths, newWidth, index) => {
|
||||
const { columnIndex } = this.state;
|
||||
|
||||
let rightColumn;
|
||||
let colIndex = index ? index : +columnIndex + 1;
|
||||
|
||||
while (colIndex !== this.props.columns.length) {
|
||||
rightColumn = document.getElementById("column_" + colIndex);
|
||||
if (rightColumn) {
|
||||
if (rightColumn.dataset.enable === "true") break;
|
||||
else colIndex++;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
const offset = this.getSubstring(widths[+columnIndex]) - newWidth;
|
||||
const column2Width = this.getSubstring(widths[colIndex]);
|
||||
|
||||
if (column2Width + offset >= minColumnSize) {
|
||||
widths[+columnIndex] = newWidth + "px";
|
||||
widths[colIndex] = column2Width + offset + "px";
|
||||
} else {
|
||||
if (colIndex === this.props.columns.length) return false;
|
||||
return this.moveToRight(widths, newWidth, colIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
addNewColumns = (gridTemplateColumns, columnIndex) => {
|
||||
const filterColumns = this.props.columns
|
||||
.filter((x) => x.enable)
|
||||
.filter((x) => x.key !== this.props.columns[columnIndex - 1].key)
|
||||
.filter((x) => !x.defaultSize);
|
||||
|
||||
let index = this.props.columns.length;
|
||||
while (index !== 0) {
|
||||
index--;
|
||||
const someItem = this.props.columns[index];
|
||||
|
||||
const isFind = filterColumns.find((x) => x.key === someItem.key);
|
||||
if (isFind) {
|
||||
const someItemById = document.getElementById("column_" + (index + 1));
|
||||
|
||||
const columnSize = someItemById.clientWidth - minColumnSize;
|
||||
|
||||
if (columnSize >= minColumnSize) {
|
||||
return (gridTemplateColumns[index + 1] = columnSize + "px");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMouseMove = (e) => {
|
||||
const { columnIndex } = this.state;
|
||||
const { containerRef } = this.props;
|
||||
if (!columnIndex) return;
|
||||
const column = document.getElementById("column_" + columnIndex);
|
||||
const columnSize = column.getBoundingClientRect();
|
||||
const newWidth = e.clientX - columnSize.left;
|
||||
|
||||
const tableContainer = containerRef.current.style.gridTemplateColumns;
|
||||
const widths = tableContainer.split(" ");
|
||||
|
||||
const minSize = column.dataset.minWidth
|
||||
? column.dataset.minWidth
|
||||
: minColumnSize;
|
||||
|
||||
if (newWidth <= minSize) {
|
||||
const columnChanged = this.moveToLeft(widths, newWidth);
|
||||
|
||||
if (!columnChanged) {
|
||||
widths[+columnIndex] = widths[+columnIndex];
|
||||
}
|
||||
} else {
|
||||
this.moveToRight(widths, newWidth);
|
||||
}
|
||||
|
||||
containerRef.current.style.gridTemplateColumns = widths.join(" ");
|
||||
this.headerRef.current.style.gridTemplateColumns = widths.join(" ");
|
||||
};
|
||||
|
||||
onMouseUp = () => {
|
||||
localStorage.setItem(
|
||||
this.props.columnStorageName,
|
||||
this.props.containerRef.current.style.gridTemplateColumns
|
||||
);
|
||||
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
};
|
||||
|
||||
onMouseDown = (event) => {
|
||||
this.setState({ columnIndex: event.target.dataset.column });
|
||||
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
};
|
||||
|
||||
onResize = () => {
|
||||
const { containerRef, columnStorageName, checkboxSize } = this.props;
|
||||
|
||||
let activeColumnIndex = null;
|
||||
|
||||
const container = containerRef.current
|
||||
? containerRef.current
|
||||
: document.getElementById("table-container");
|
||||
|
||||
if (!container) return;
|
||||
|
||||
const storageSize = localStorage.getItem(columnStorageName);
|
||||
const tableContainer = storageSize
|
||||
? storageSize.split(" ")
|
||||
: container.style.gridTemplateColumns.split(" ");
|
||||
|
||||
const containerWidth = +container.clientWidth;
|
||||
const newContainerWidth =
|
||||
containerWidth - this.getSubstring(checkboxSize) - 80 - settingsSize; // TODO: 80
|
||||
|
||||
const oldWidth = tableContainer
|
||||
.map((column) => this.getSubstring(column))
|
||||
.reduce((x, y) => x + y);
|
||||
|
||||
const enableColumns = this.props.columns
|
||||
.filter((x) => !x.default)
|
||||
.filter((x) => x.enable)
|
||||
.filter((x) => !x.defaultSize);
|
||||
|
||||
const isSingleTable = enableColumns.length > 0;
|
||||
|
||||
let str = "";
|
||||
let disableColumnWidth = 0;
|
||||
|
||||
if (tableContainer.length > 1) {
|
||||
const gridTemplateColumns = [];
|
||||
|
||||
for (let index in tableContainer) {
|
||||
const item = tableContainer[index];
|
||||
|
||||
const column = document.getElementById("column_" + index);
|
||||
const enable =
|
||||
index == 0 ||
|
||||
index == tableContainer.length - 1 ||
|
||||
(column ? column.dataset.enable === "true" : item !== "0px");
|
||||
|
||||
const isActiveNow = item === "0px" && enable;
|
||||
if (isActiveNow && column) activeColumnIndex = index;
|
||||
|
||||
if (!enable) {
|
||||
gridTemplateColumns.push("0px");
|
||||
gridTemplateColumns[1] =
|
||||
this.getSubstring(gridTemplateColumns[1]) +
|
||||
this.getSubstring(item) +
|
||||
"px";
|
||||
} else if (
|
||||
item !== `${settingsSize}px` &&
|
||||
item !== checkboxSize &&
|
||||
item !== "80px"
|
||||
) {
|
||||
const percent = (this.getSubstring(item) / oldWidth) * 100;
|
||||
|
||||
if (index == 1) {
|
||||
const newItemWidth =
|
||||
(containerWidth * percent) / 100 + disableColumnWidth + "px";
|
||||
gridTemplateColumns.push(newItemWidth);
|
||||
} else {
|
||||
const newItemWidth =
|
||||
percent === 0
|
||||
? `${minColumnSize}px`
|
||||
: (containerWidth * percent) / 100 + "px";
|
||||
|
||||
gridTemplateColumns.push(newItemWidth);
|
||||
}
|
||||
} else {
|
||||
gridTemplateColumns.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (activeColumnIndex) {
|
||||
this.addNewColumns(gridTemplateColumns, activeColumnIndex);
|
||||
}
|
||||
|
||||
str = gridTemplateColumns.join(" ");
|
||||
} else {
|
||||
const column =
|
||||
(newContainerWidth * (isSingleTable ? 60 : 100)) / 100 + "px";
|
||||
const percent = 40 / enableColumns.length;
|
||||
const otherColumns = (newContainerWidth * percent) / 100 + "px";
|
||||
|
||||
str = `${checkboxSize} ${column} `;
|
||||
for (let col of this.props.columns) {
|
||||
if (!col.default) {
|
||||
str += col.enable
|
||||
? col.defaultSize
|
||||
? `${col.defaultSize}px `
|
||||
: `${otherColumns} `
|
||||
: "0px ";
|
||||
}
|
||||
}
|
||||
|
||||
str += `${settingsSize}px`;
|
||||
}
|
||||
container.style.gridTemplateColumns = str;
|
||||
if (this.headerRef.current) {
|
||||
this.headerRef.current.style.gridTemplateColumns = str;
|
||||
this.headerRef.current.style.width = containerWidth + "px";
|
||||
}
|
||||
|
||||
localStorage.setItem(columnStorageName, str);
|
||||
};
|
||||
|
||||
onChange = (checked) => {
|
||||
this.props.setSelected(checked);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
columns,
|
||||
sortBy,
|
||||
sorted,
|
||||
isHeaderVisible,
|
||||
checkboxOptions,
|
||||
containerRef,
|
||||
onChange,
|
||||
isChecked,
|
||||
isIndeterminate,
|
||||
headerMenu,
|
||||
columnStorageName,
|
||||
hasAccess,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
//console.log("TABLE HEADER RENDER", columns);
|
||||
|
||||
return (
|
||||
<>
|
||||
{isHeaderVisible ? (
|
||||
<TableGroupMenu
|
||||
checkboxOptions={checkboxOptions}
|
||||
containerRef={containerRef}
|
||||
onChange={onChange}
|
||||
isChecked={isChecked}
|
||||
isIndeterminate={isIndeterminate}
|
||||
headerMenu={headerMenu}
|
||||
columnStorageName={columnStorageName}
|
||||
{...rest}
|
||||
/>
|
||||
) : (
|
||||
<StyledTableHeader
|
||||
className="table-container_header"
|
||||
ref={this.headerRef}
|
||||
{...rest}
|
||||
>
|
||||
<StyledTableRow>
|
||||
{hasAccess ? (
|
||||
<Checkbox
|
||||
className="table-container_header-checkbox"
|
||||
onChange={this.onChange}
|
||||
isChecked={false}
|
||||
/>
|
||||
) : (
|
||||
<div></div>
|
||||
)}
|
||||
|
||||
{columns.map((column, index) => {
|
||||
const nextColumn = this.getNextColumn(columns, index);
|
||||
const resizable = nextColumn ? nextColumn.resizable : false;
|
||||
|
||||
return (
|
||||
<TableHeaderCell
|
||||
key={column.key}
|
||||
index={index}
|
||||
column={column}
|
||||
sorted={sorted}
|
||||
sortBy={sortBy}
|
||||
resizable={resizable}
|
||||
onMouseDown={this.onMouseDown}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<div className="table-container_header-settings">
|
||||
<TableSettings columns={columns} />
|
||||
</div>
|
||||
</StyledTableRow>
|
||||
</StyledTableHeader>
|
||||
)}
|
||||
<StyledEmptyTableContainer />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TableHeader.defaultProps = {
|
||||
hasAccess: true,
|
||||
};
|
||||
|
||||
TableHeader.propTypes = {
|
||||
containerRef: PropTypes.shape({ current: PropTypes.any }).isRequired,
|
||||
columns: PropTypes.array.isRequired,
|
||||
setSelected: PropTypes.func.isRequired,
|
||||
sortBy: PropTypes.string,
|
||||
sorted: PropTypes.bool,
|
||||
columnStorageName: PropTypes.string,
|
||||
checkboxSize: PropTypes.string,
|
||||
sectionWidth: PropTypes.number,
|
||||
isHeaderVisible: PropTypes.bool,
|
||||
checkboxOptions: PropTypes.any.isRequired,
|
||||
isChecked: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
isIndeterminate: PropTypes.bool,
|
||||
headerMenu: PropTypes.arrayOf(PropTypes.object),
|
||||
onClick: PropTypes.func,
|
||||
hasAccess: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TableHeader;
|
@ -0,0 +1,85 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import Text from "../text";
|
||||
import Link from "../link";
|
||||
import IconButton from "../icon-button";
|
||||
import globalColors from "../utils/globalColors";
|
||||
import { StyledTableHeaderCell } from "./StyledTableContainer";
|
||||
|
||||
const TableHeaderCell = ({
|
||||
column,
|
||||
index,
|
||||
onMouseDown,
|
||||
resizable,
|
||||
sortBy,
|
||||
sorted,
|
||||
}) => {
|
||||
const { options, title, enable, active, minWidth } = column;
|
||||
|
||||
const isActive = column.sortBy === sortBy || active;
|
||||
|
||||
const onClick = (e) => {
|
||||
column.onClick(column.sortBy, e);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTableHeaderCell
|
||||
sorted={sorted}
|
||||
isActive={isActive}
|
||||
className="table-container_header-cell"
|
||||
id={`column_${index + 1}`}
|
||||
data-enable={enable}
|
||||
data-min-width={minWidth}
|
||||
>
|
||||
<div className="table-container_header-item">
|
||||
{column.onClick ? (
|
||||
<div className="header-container-text-wrapper">
|
||||
<Link
|
||||
onClick={onClick}
|
||||
fontWeight={600}
|
||||
color={globalColors.gray}
|
||||
className="header-container-text"
|
||||
data={options}
|
||||
noHover
|
||||
>
|
||||
{enable ? title : ""}
|
||||
</Link>
|
||||
|
||||
<IconButton
|
||||
onClick={column.onIconClick ? column.onIconClick : onClick}
|
||||
iconName="/static/images/folder arrow.react.svg"
|
||||
className="header-container-text-icon"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Text
|
||||
fontWeight={600}
|
||||
color={globalColors.gray}
|
||||
className="header-container-text"
|
||||
>
|
||||
{enable ? title : ""}
|
||||
</Text>
|
||||
)}
|
||||
{resizable && (
|
||||
<div
|
||||
data-column={`${index + 1}`}
|
||||
className="resize-handle not-selectable"
|
||||
onMouseDown={onMouseDown}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</StyledTableHeaderCell>
|
||||
);
|
||||
};
|
||||
|
||||
TableHeaderCell.propTypes = {
|
||||
column: PropTypes.object,
|
||||
index: PropTypes.number,
|
||||
onMouseDown: PropTypes.func,
|
||||
resizable: PropTypes.bool,
|
||||
sorted: PropTypes.bool,
|
||||
sortBy: PropTypes.string,
|
||||
};
|
||||
|
||||
export default TableHeaderCell;
|
110
packages/asc-web-components/table-container/TableRow.js
Normal file
110
packages/asc-web-components/table-container/TableRow.js
Normal file
@ -0,0 +1,110 @@
|
||||
import React, { useRef } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { StyledTableRow } from "./StyledTableContainer";
|
||||
import TableCell from "./TableCell";
|
||||
import ContextMenu from "../context-menu";
|
||||
import ContextMenuButton from "../context-menu-button";
|
||||
import Checkbox from "../checkbox";
|
||||
|
||||
const TableRow = (props) => {
|
||||
const {
|
||||
fileContextClick,
|
||||
children,
|
||||
contextOptions,
|
||||
checked,
|
||||
element,
|
||||
onContentSelect,
|
||||
item,
|
||||
className,
|
||||
style,
|
||||
selectionProp,
|
||||
hasAccess,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const cm = useRef();
|
||||
const row = useRef();
|
||||
|
||||
const onContextMenu = (e) => {
|
||||
fileContextClick && fileContextClick();
|
||||
if (cm.current && !cm.current.menuRef.current) {
|
||||
row.current.click(e);
|
||||
}
|
||||
cm.current.show(e);
|
||||
};
|
||||
|
||||
const renderContext =
|
||||
Object.prototype.hasOwnProperty.call(props, "contextOptions") &&
|
||||
contextOptions.length > 0;
|
||||
|
||||
const getOptions = () => {
|
||||
fileContextClick && fileContextClick();
|
||||
return contextOptions;
|
||||
};
|
||||
|
||||
const onChange = (e) => {
|
||||
onContentSelect && onContentSelect(e.target.checked, item);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTableRow
|
||||
onContextMenu={onContextMenu}
|
||||
className={`${className} table-container_row`}
|
||||
{...rest}
|
||||
>
|
||||
<TableCell
|
||||
hasAccess={hasAccess}
|
||||
checked={checked}
|
||||
{...selectionProp}
|
||||
style={style}
|
||||
className={`${selectionProp?.className} table-container_row-checkbox-wrapper`}
|
||||
>
|
||||
<div className="table-container_element">{element}</div>
|
||||
<Checkbox
|
||||
className="table-container_row-checkbox"
|
||||
onChange={onChange}
|
||||
isChecked={checked}
|
||||
/>
|
||||
</TableCell>
|
||||
{children}
|
||||
<div>
|
||||
<TableCell {...selectionProp} style={style} forwardedRef={row}>
|
||||
<ContextMenu ref={cm} model={contextOptions}></ContextMenu>
|
||||
{renderContext ? (
|
||||
<ContextMenuButton
|
||||
color="#A3A9AE"
|
||||
hoverColor="#657077"
|
||||
className="expandButton"
|
||||
getData={getOptions}
|
||||
directionX="right"
|
||||
isNew={true}
|
||||
onClick={onContextMenu}
|
||||
/>
|
||||
) : (
|
||||
<div className="expandButton"> </div>
|
||||
)}
|
||||
</TableCell>
|
||||
</div>
|
||||
</StyledTableRow>
|
||||
);
|
||||
};
|
||||
|
||||
TableRow.defaultProps = {
|
||||
hasAccess: true,
|
||||
};
|
||||
|
||||
TableRow.propTypes = {
|
||||
fileContextClick: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
contextOptions: PropTypes.array,
|
||||
checked: PropTypes.bool,
|
||||
element: PropTypes.any,
|
||||
onContentSelect: PropTypes.func,
|
||||
item: PropTypes.object,
|
||||
selectionProp: PropTypes.object,
|
||||
className: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
style: PropTypes.object,
|
||||
hasAccess: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default TableRow;
|
70
packages/asc-web-components/table-container/TableSettings.js
Normal file
70
packages/asc-web-components/table-container/TableSettings.js
Normal file
@ -0,0 +1,70 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import IconButton from "../icon-button";
|
||||
import DropDown from "../drop-down";
|
||||
import { StyledTableSettings } from "./StyledTableContainer";
|
||||
import Checkbox from "../checkbox";
|
||||
|
||||
const TableSettings = ({ columns }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const ref = useRef();
|
||||
|
||||
const onClick = () => {
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
const clickOutsideAction = (e) => {
|
||||
const path = e.path || (e.composedPath && e.composedPath());
|
||||
const dropDownItem = path ? path.find((x) => x === ref.current) : null;
|
||||
if (dropDownItem) return;
|
||||
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTableSettings
|
||||
className="table-container_header-settings-icon"
|
||||
ref={ref}
|
||||
>
|
||||
<IconButton
|
||||
color="#A3A9AE"
|
||||
hoverColor="#657077"
|
||||
size={12}
|
||||
isFill
|
||||
iconName="/static/images/settings.react.svg"
|
||||
onClick={onClick}
|
||||
/>
|
||||
<DropDown
|
||||
className="table-container_settings"
|
||||
directionX="right"
|
||||
open={isOpen}
|
||||
clickOutsideAction={clickOutsideAction}
|
||||
withBackdrop={false}
|
||||
>
|
||||
{columns.map((column) => {
|
||||
const onChange = (e) =>
|
||||
column.onChange && column.onChange(column.key, e);
|
||||
|
||||
return (
|
||||
column.onChange && (
|
||||
<Checkbox
|
||||
className="table-container_settings-checkbox"
|
||||
isChecked={column.enable}
|
||||
onChange={onChange}
|
||||
key={column.key}
|
||||
label={column.title}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</DropDown>
|
||||
</StyledTableSettings>
|
||||
);
|
||||
};
|
||||
|
||||
TableSettings.propTypes = {
|
||||
columns: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default TableSettings;
|
1
packages/asc-web-components/table-container/index.js
Normal file
1
packages/asc-web-components/table-container/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default from "./TableContainer";
|
@ -10,7 +10,6 @@ const Text = ({
|
||||
fontWeight,
|
||||
color,
|
||||
textAlign,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
@ -21,7 +20,6 @@ const Text = ({
|
||||
textAlign={textAlign}
|
||||
as={!as && tag ? tag : as}
|
||||
title={title}
|
||||
className={`${className} not-selectable`}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
@ -11,17 +11,14 @@ const ViewSelector = ({
|
||||
onChangeView,
|
||||
...rest
|
||||
}) => {
|
||||
const [currentView, setCurrentView] = useState(viewAs);
|
||||
|
||||
const onChangeViewHandler = (e) => {
|
||||
if (isDisabled) return;
|
||||
const el = e.target.closest(".view-selector-icon");
|
||||
|
||||
const view = el.dataset.view;
|
||||
if (view !== currentView) {
|
||||
if (view !== viewAs) {
|
||||
const option = viewSettings.find((setting) => view === setting.value);
|
||||
option.callback && option.callback();
|
||||
setCurrentView(view);
|
||||
onChangeView(view);
|
||||
}
|
||||
};
|
||||
@ -42,7 +39,7 @@ const ViewSelector = ({
|
||||
return (
|
||||
<IconWrapper
|
||||
isDisabled={isDisabled}
|
||||
isChecked={currentView === value}
|
||||
isChecked={viewAs === value}
|
||||
firstItem={indx === 0}
|
||||
lastItem={indx === lastIndx}
|
||||
key={value}
|
||||
|
@ -0,0 +1,3 @@
|
||||
<svg width="8" height="4" viewBox="0 0 8 4" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.646447 0.146447C0.841709 -0.0488155 1.15829 -0.0488155 1.35355 0.146447L4 2.79289L6.64645 0.146447C6.84171 -0.0488155 7.15829 -0.0488155 7.35355 0.146447C7.54882 0.341709 7.54882 0.658291 7.35355 0.853553L4.35355 3.85355C4.15829 4.04882 3.84171 4.04882 3.64645 3.85355L0.646447 0.853553C0.451184 0.658291 0.451184 0.341709 0.646447 0.146447Z" fill="#657077"/>
|
||||
</svg>
|
After Width: | Height: | Size: 511 B |
@ -212,7 +212,7 @@ export default function withContent(WrappedContent) {
|
||||
};
|
||||
|
||||
getStatusByDate = () => {
|
||||
const { culture, t, item, sectionWidth } = this.props;
|
||||
const { culture, t, item, sectionWidth, viewAs } = this.props;
|
||||
const { created, updated, version, fileExst } = item;
|
||||
|
||||
const title =
|
||||
@ -224,11 +224,20 @@ export default function withContent(WrappedContent) {
|
||||
|
||||
const date = fileExst ? updated : created;
|
||||
const dateLabel = new Date(date).toLocaleString(culture);
|
||||
const mobile = (sectionWidth && sectionWidth <= 375) || isMobile;
|
||||
const mobile =
|
||||
(sectionWidth && sectionWidth <= 375) || isMobile || viewAs === "table";
|
||||
|
||||
return mobile ? dateLabel : `${title}: ${dateLabel}`;
|
||||
};
|
||||
|
||||
getTableStatusByDate = (create) => {
|
||||
const { created, updated, fileExst } = this.props.item;
|
||||
|
||||
const date = fileExst ? updated : created;
|
||||
const dateLabel = new Date(date).toLocaleString(this.props.culture);
|
||||
return dateLabel;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { itemTitle } = this.state;
|
||||
const {
|
||||
@ -240,6 +249,7 @@ export default function withContent(WrappedContent) {
|
||||
isTrashFolder,
|
||||
onFilesClick,
|
||||
viewAs,
|
||||
element,
|
||||
} = this.props;
|
||||
const { id, fileExst, updated, createdBy, access, fileStatus } = item;
|
||||
|
||||
@ -247,7 +257,11 @@ export default function withContent(WrappedContent) {
|
||||
|
||||
const isEdit = id === fileActionId && fileExst === fileActionExt;
|
||||
|
||||
const updatedDate = updated && this.getStatusByDate();
|
||||
const updatedDate =
|
||||
viewAs === "table"
|
||||
? this.getTableStatusByDate(false)
|
||||
: updated && this.getStatusByDate();
|
||||
const createdDate = this.getTableStatusByDate(true);
|
||||
|
||||
const fileOwner =
|
||||
createdBy &&
|
||||
@ -268,6 +282,7 @@ export default function withContent(WrappedContent) {
|
||||
return isEdit ? (
|
||||
<EditingWrapperComponent
|
||||
className={"editing-wrapper-component"}
|
||||
elementIcon={element}
|
||||
itemTitle={itemTitle}
|
||||
itemId={id}
|
||||
viewAs={viewAs}
|
||||
@ -279,6 +294,7 @@ export default function withContent(WrappedContent) {
|
||||
<WrappedContent
|
||||
titleWithoutExt={titleWithoutExt}
|
||||
updatedDate={updatedDate}
|
||||
createdDate={createdDate}
|
||||
fileOwner={fileOwner}
|
||||
accessToEdit={accessToEdit}
|
||||
linkStyles={linkStyles}
|
||||
|
@ -10,11 +10,8 @@ export default function withFileActions(WrappedFileItem) {
|
||||
class WithFileActions extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isMouseDown: false,
|
||||
};
|
||||
}
|
||||
|
||||
onContentFileSelect = (checked, file) => {
|
||||
const { selectRowAction } = this.props;
|
||||
if (!file || file.id === -1) return;
|
||||
@ -54,8 +51,6 @@ export default function withFileActions(WrappedFileItem) {
|
||||
} = this.props;
|
||||
const notSelectable = e.target.classList.contains("not-selectable");
|
||||
|
||||
this.setState({ isMouseDown: true });
|
||||
|
||||
if (!draggable || isPrivacy) return;
|
||||
|
||||
if (window.innerWidth < 1025 || notSelectable) {
|
||||
@ -78,9 +73,8 @@ export default function withFileActions(WrappedFileItem) {
|
||||
onMarkAsRead = (id) =>
|
||||
this.props.markAsRead([], [`${id}`], this.props.item);
|
||||
|
||||
onMouseUpHandler = (e) => {
|
||||
const { isMouseDown } = this.state;
|
||||
const { viewAs } = this.props;
|
||||
onMouseClick = (e) => {
|
||||
const { viewAs, isItemsSelected } = this.props;
|
||||
|
||||
if (
|
||||
e.target.closest(".checkbox") ||
|
||||
@ -89,23 +83,19 @@ export default function withFileActions(WrappedFileItem) {
|
||||
e.target.tagName === "A" ||
|
||||
e.target.closest(".expandButton") ||
|
||||
e.target.closest(".badges") ||
|
||||
e.button !== 0
|
||||
e.button !== 0 /* ||
|
||||
isItemsSelected */
|
||||
)
|
||||
return;
|
||||
|
||||
if (viewAs === "tile") {
|
||||
if (
|
||||
!isMouseDown ||
|
||||
e.target.closest(".edit-button") ||
|
||||
e.target.tagName === "IMG"
|
||||
)
|
||||
if (e.target.closest(".edit-button") || e.target.tagName === "IMG")
|
||||
return;
|
||||
|
||||
this.onFilesClick();
|
||||
} else {
|
||||
this.fileContextClick();
|
||||
}
|
||||
this.setState({ isMouseDown: false });
|
||||
};
|
||||
onFilesClick = (e) => {
|
||||
const {
|
||||
@ -220,7 +210,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
const isDragging = isFolder && access < 2 && !isTrashFolder && !isPrivacy;
|
||||
|
||||
let className = isDragging ? " droppable" : "";
|
||||
if (draggable) className += " draggable not-selectable";
|
||||
if (draggable) className += " draggable";
|
||||
|
||||
let value = fileExst || contentLength ? `file_${id}` : `folder_${id}`;
|
||||
value += draggable ? "_draggable" : "";
|
||||
@ -251,7 +241,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
onDrop={this.onDrop}
|
||||
onMouseDown={this.onMouseDown}
|
||||
onFilesClick={this.onFilesClick}
|
||||
onMouseUp={this.onMouseUpHandler}
|
||||
onMouseClick={this.onMouseClick}
|
||||
getClassName={this.getClassName}
|
||||
className={className}
|
||||
isDragging={isDragging}
|
||||
@ -376,6 +366,7 @@ export default function withFileActions(WrappedFileItem) {
|
||||
setConvertDialogVisible,
|
||||
isDesktop: auth.settingsStore.isDesktopClient,
|
||||
personal: auth.settingsStore.personal,
|
||||
isItemsSelected: selection.length > 0,
|
||||
};
|
||||
}
|
||||
)(observer(WithFileActions));
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
import styled, { css } from "styled-components";
|
||||
import Button from "@appserver/components/button";
|
||||
import TextInput from "@appserver/components/text-input";
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
@ -37,6 +37,17 @@ const EditingWrapper = styled.div`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
${(props) =>
|
||||
props.viewAs === "table" &&
|
||||
css`
|
||||
grid-column-start: 1;
|
||||
grid-column-end: -1;
|
||||
|
||||
border-bottom: 1px solid #eceef1;
|
||||
padding-bottom: 4px;
|
||||
margin-top: 4px;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.viewAs === "tile" &&
|
||||
`margin-right: 12px !important; margin-left: -4px;`}
|
||||
@ -60,6 +71,19 @@ const EditingWrapper = styled.div`
|
||||
height: 32px;
|
||||
padding: 8px 7px 7px 7px;
|
||||
|
||||
${(props) =>
|
||||
props.viewAs === "table" &&
|
||||
css`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px transparent;
|
||||
padding: 4px 0 0 0;
|
||||
|
||||
:hover {
|
||||
border: 1px solid #d0d5da;
|
||||
}
|
||||
`}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 4px;
|
||||
}
|
||||
@ -76,6 +100,10 @@ const EditingWrapper = styled.div`
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.is-edit {
|
||||
margin-top: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
const EditingWrapperComponent = (props) => {
|
||||
@ -87,8 +115,11 @@ const EditingWrapperComponent = (props) => {
|
||||
cancelUpdateItem,
|
||||
isLoading,
|
||||
viewAs,
|
||||
elementIcon,
|
||||
} = props;
|
||||
|
||||
const isTable = viewAs === "table";
|
||||
|
||||
const [OkIconIsHovered, setIsHoveredOk] = useState(false);
|
||||
const [CancelIconIsHovered, setIsHoveredCancel] = useState(false);
|
||||
|
||||
@ -116,6 +147,7 @@ const EditingWrapperComponent = (props) => {
|
||||
|
||||
return (
|
||||
<EditingWrapper viewAs={viewAs}>
|
||||
{isTable && elementIcon}
|
||||
<TextInput
|
||||
className="edit-text"
|
||||
name="title"
|
||||
@ -129,6 +161,7 @@ const EditingWrapperComponent = (props) => {
|
||||
onFocus={onFocus}
|
||||
isDisabled={isLoading}
|
||||
data-itemid={itemId}
|
||||
withBorder={!isTable}
|
||||
/>
|
||||
<Button
|
||||
className="edit-button not-selectable"
|
||||
|
@ -135,7 +135,7 @@ export const loopTreeFolders = (
|
||||
}
|
||||
loopTreeFolders(
|
||||
newPath,
|
||||
newItems.folders,
|
||||
newItems.folders ? newItems.folders : [],
|
||||
folders,
|
||||
foldersCount,
|
||||
currentFolder
|
||||
|
@ -1,35 +1,42 @@
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import RowContainer from "@appserver/components/row-container";
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
import SimpleFilesRow from "./SimpleFilesRow";
|
||||
|
||||
const FilesRowContainer = ({ filesList }) => {
|
||||
const FilesRowContainer = ({ filesList, sectionWidth, viewAs, setViewAs }) => {
|
||||
useEffect(() => {
|
||||
if (viewAs !== "table" && viewAs !== "row") return;
|
||||
|
||||
if (sectionWidth < 1025) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) => (
|
||||
<RowContainer
|
||||
className="files-row-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
>
|
||||
{filesList.map((item, index) => (
|
||||
<SimpleFilesRow
|
||||
key={`${item.id}_${index}`}
|
||||
item={item}
|
||||
sectionWidth={context.sectionWidth}
|
||||
/>
|
||||
))}
|
||||
</RowContainer>
|
||||
)}
|
||||
</Consumer>
|
||||
<RowContainer
|
||||
className="files-row-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
>
|
||||
{filesList.map((item, index) => (
|
||||
<SimpleFilesRow
|
||||
key={`${item.id}_${index}`}
|
||||
item={item}
|
||||
sectionWidth={sectionWidth}
|
||||
/>
|
||||
))}
|
||||
</RowContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => {
|
||||
const { filesList } = filesStore;
|
||||
const { filesList, viewAs, setViewAs } = filesStore;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
};
|
||||
})(observer(FilesRowContainer));
|
||||
|
@ -67,7 +67,7 @@ const SimpleFilesRow = (props) => {
|
||||
contextOptionsProps,
|
||||
checkedProps,
|
||||
onFilesClick,
|
||||
onMouseUp,
|
||||
onMouseClick,
|
||||
isEdit,
|
||||
showShare,
|
||||
} = props;
|
||||
@ -95,7 +95,6 @@ const SimpleFilesRow = (props) => {
|
||||
onDrop={onDrop}
|
||||
onMouseDown={onMouseDown}
|
||||
dragging={dragging && isDragging}
|
||||
{...contextOptionsProps}
|
||||
>
|
||||
<StyledSimpleFilesRow
|
||||
key={item.id}
|
||||
@ -107,7 +106,7 @@ const SimpleFilesRow = (props) => {
|
||||
onSelect={onContentFileSelect}
|
||||
rowContextClick={fileContextClick}
|
||||
isPrivacy={isPrivacy}
|
||||
onMouseUp={onMouseUp}
|
||||
onClick={onMouseClick}
|
||||
onDoubleClick={onFilesClick}
|
||||
checked={checkedProps}
|
||||
{...contextOptionsProps}
|
||||
|
@ -0,0 +1,41 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import TableContainer from "@appserver/components/table-container";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import TableRow from "./TableRow";
|
||||
import TableHeader from "./TableHeader";
|
||||
import TableBody from "@appserver/components/table-container/TableBody";
|
||||
|
||||
const Table = ({ filesList, sectionWidth, viewAs, setViewAs }) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (viewAs !== "table" && viewAs !== "row") return;
|
||||
|
||||
if (sectionWidth < 1025) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
return (
|
||||
<TableContainer forwardedRef={ref}>
|
||||
<TableHeader sectionWidth={sectionWidth} containerRef={ref} />
|
||||
<TableBody>
|
||||
{filesList.map((item) => (
|
||||
<TableRow key={item.id} item={item} />
|
||||
))}
|
||||
</TableBody>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => {
|
||||
const { filesList, viewAs, setViewAs } = filesStore;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
};
|
||||
})(observer(Table));
|
@ -0,0 +1,280 @@
|
||||
import React from "react";
|
||||
import TableHeader from "@appserver/components/table-container/TableHeader";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { FilterType } from "@appserver/common/constants";
|
||||
import DropDownItem from "@appserver/components/drop-down-item";
|
||||
|
||||
const TABLE_COLUMNS = "filesTableColumns";
|
||||
|
||||
class FilesTableHeader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { t, withContent } = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
sortBy: "AZ",
|
||||
minWidth: 180,
|
||||
onClick: this.onFilter,
|
||||
},
|
||||
{
|
||||
key: "Author",
|
||||
title: t("ByAuthor"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
sortBy: "Author",
|
||||
onClick: this.onFilter,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Created",
|
||||
title: t("ByCreationDate"),
|
||||
enable: false,
|
||||
resizable: true,
|
||||
sortBy: "DateAndTimeCreation",
|
||||
onClick: this.onFilter,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Modified",
|
||||
title: t("ByLastModifiedDate"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
sortBy: "DateAndTime",
|
||||
onClick: this.onFilter,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Size",
|
||||
title: t("Common:Size"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
sortBy: "Size",
|
||||
onClick: this.onFilter,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Type",
|
||||
title: t("Common:Type"),
|
||||
enable: false,
|
||||
resizable: true,
|
||||
sortBy: "Type",
|
||||
onClick: this.onFilter,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Share",
|
||||
title: "",
|
||||
enable: withContent,
|
||||
defaultSize: 80,
|
||||
resizable: false,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = this.getColumns(defaultColumns);
|
||||
|
||||
this.state = { columns };
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { columns } = this.state;
|
||||
if (this.props.withContent !== prevProps.withContent) {
|
||||
const columnIndex = columns.findIndex((c) => c.key === "Share");
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
columns[columnIndex].enable = this.props.withContent;
|
||||
this.setState({ columns });
|
||||
}
|
||||
}
|
||||
|
||||
getColumns = (defaultColumns) => {
|
||||
const storageColumns = localStorage.getItem(TABLE_COLUMNS);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
onColumnChange = (key, e) => {
|
||||
const { columns } = this.state;
|
||||
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
columns[columnIndex].enable = !columns[columnIndex].enable;
|
||||
this.setState({ columns });
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(TABLE_COLUMNS, tableColumns);
|
||||
};
|
||||
|
||||
onFilter = (sortBy) => {
|
||||
const { filter, selectedFolderId, setIsLoading, fetchFiles } = this.props;
|
||||
const newFilter = filter.clone();
|
||||
|
||||
if (newFilter.sortBy !== sortBy) {
|
||||
newFilter.sortBy = sortBy;
|
||||
} else {
|
||||
newFilter.sortOrder =
|
||||
newFilter.sortOrder === "ascending" ? "descending" : "ascending";
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
fetchFiles(selectedFolderId, newFilter).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
onChange = (checked) => {
|
||||
this.props.setSelected(checked ? "all" : "none");
|
||||
};
|
||||
|
||||
onSelect = (e) => {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.props.setSelected(key);
|
||||
};
|
||||
|
||||
setSelected = (checked) => {
|
||||
this.props.setSelected && this.props.setSelected(checked ? "all" : "none");
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
t,
|
||||
containerRef,
|
||||
isHeaderVisible,
|
||||
isHeaderChecked,
|
||||
isHeaderIndeterminate,
|
||||
getHeaderMenu,
|
||||
filter,
|
||||
sectionWidth,
|
||||
} = this.props;
|
||||
|
||||
const { sortBy, sortOrder } = filter;
|
||||
|
||||
const { columns } = this.state;
|
||||
|
||||
const checkboxOptions = (
|
||||
<>
|
||||
<DropDownItem label={t("All")} data-key="all" onClick={this.onSelect} />
|
||||
<DropDownItem
|
||||
label={t("Translations:Folders")}
|
||||
data-key={FilterType.FoldersOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Common:Documents")}
|
||||
data-key={FilterType.DocumentsOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Translations:Presentations")}
|
||||
data-key={FilterType.PresentationsOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Translations:Spreadsheets")}
|
||||
data-key={FilterType.SpreadsheetsOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Images")}
|
||||
data-key={FilterType.ImagesOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Media")}
|
||||
data-key={FilterType.MediaOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Archives")}
|
||||
data-key={FilterType.ArchiveOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("AllFiles")}
|
||||
data-key={FilterType.FilesOnly}
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
checkboxSize="32px"
|
||||
sorted={sortOrder === "descending"}
|
||||
sortBy={sortBy}
|
||||
setSelected={this.setSelected}
|
||||
containerRef={containerRef}
|
||||
columns={columns}
|
||||
columnStorageName="filesColumnsSize"
|
||||
sectionWidth={sectionWidth}
|
||||
isHeaderVisible={isHeaderVisible}
|
||||
checkboxOptions={checkboxOptions}
|
||||
onChange={this.onChange}
|
||||
isChecked={isHeaderChecked}
|
||||
isIndeterminate={isHeaderIndeterminate}
|
||||
headerMenu={getHeaderMenu(t)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default inject(
|
||||
({
|
||||
filesStore,
|
||||
filesActionsStore,
|
||||
selectedFolderStore,
|
||||
treeFoldersStore,
|
||||
}) => {
|
||||
const {
|
||||
setSelected,
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
setIsLoading,
|
||||
filter,
|
||||
fetchFiles,
|
||||
canShare,
|
||||
} = filesStore;
|
||||
const { getHeaderMenu } = filesActionsStore;
|
||||
const { isPrivacyFolder } = treeFoldersStore;
|
||||
|
||||
const withContent = canShare || (canShare && isPrivacyFolder && isDesktop);
|
||||
|
||||
return {
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
filter,
|
||||
selectedFolderId: selectedFolderStore.id,
|
||||
withContent,
|
||||
|
||||
setSelected,
|
||||
setIsLoading,
|
||||
fetchFiles,
|
||||
getHeaderMenu,
|
||||
};
|
||||
}
|
||||
)(
|
||||
withTranslation(["Home", "Common", "Translations"])(
|
||||
observer(FilesTableHeader)
|
||||
)
|
||||
);
|
@ -0,0 +1,190 @@
|
||||
import React, { useState } from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import withContent from "../../../../../HOCs/withContent";
|
||||
import withBadges from "../../../../../HOCs/withBadges";
|
||||
import withFileActions from "../../../../../HOCs/withFileActions";
|
||||
import withContextOptions from "../../../../../HOCs/withContextOptions";
|
||||
import ItemIcon from "../../../../../components/ItemIcon";
|
||||
import SharedButton from "../../../../../components/SharedButton";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import TableRow from "@appserver/components/table-container/TableRow";
|
||||
import TableCell from "@appserver/components/table-container/TableCell";
|
||||
import DragAndDrop from "@appserver/components/drag-and-drop";
|
||||
import FileNameCell from "./sub-components/FileNameCell";
|
||||
import SizeCell from "./sub-components/SizeCell";
|
||||
import AuthorCell from "./sub-components/AuthorCell";
|
||||
import DateCell from "./sub-components/DateCell";
|
||||
import TypeCell from "./sub-components/TypeCell";
|
||||
import globalColors from "@appserver/components/utils/globalColors";
|
||||
import styled from "styled-components";
|
||||
import Base from "@appserver/components/themes/base";
|
||||
|
||||
const sideColor = globalColors.gray;
|
||||
const { acceptBackground, background } = Base.dragAndDrop;
|
||||
|
||||
const StyledDragAndDrop = styled(DragAndDrop)`
|
||||
display: contents;
|
||||
`;
|
||||
|
||||
const StyledShare = styled.div`
|
||||
cursor: pointer;
|
||||
|
||||
.share-button {
|
||||
padding: 4px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
:hover {
|
||||
border: 1px solid #a3a9ae;
|
||||
svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.share-button-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledBadgesContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
margin-left: 8px;
|
||||
|
||||
.badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 19px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
cursor: pointer;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
`;
|
||||
|
||||
const FilesTableRow = (props) => {
|
||||
const {
|
||||
t,
|
||||
contextOptionsProps,
|
||||
fileContextClick,
|
||||
item,
|
||||
onContentFileSelect,
|
||||
checkedProps,
|
||||
className,
|
||||
value,
|
||||
onMouseClick,
|
||||
badgesComponent,
|
||||
dragging,
|
||||
isDragging,
|
||||
onDrop,
|
||||
onMouseDown,
|
||||
showShare,
|
||||
} = props;
|
||||
|
||||
const sharedButton =
|
||||
item.canShare && showShare ? (
|
||||
<SharedButton
|
||||
t={t}
|
||||
id={item.id}
|
||||
shared={item.shared}
|
||||
isFolder={item.isFolder}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const element = (
|
||||
<ItemIcon id={item.id} icon={item.icon} fileExst={item.fileExst} />
|
||||
);
|
||||
|
||||
const selectionProp = {
|
||||
className: `files-item ${className} ${value}`,
|
||||
value,
|
||||
};
|
||||
|
||||
const [isDragActive, setIsDragActive] = useState(false);
|
||||
|
||||
const dragStyles = {
|
||||
style: {
|
||||
background:
|
||||
dragging && isDragging
|
||||
? isDragActive
|
||||
? acceptBackground
|
||||
: background
|
||||
: "none",
|
||||
},
|
||||
};
|
||||
|
||||
const onDragOver = (dragActive) => {
|
||||
if (dragActive !== isDragActive) {
|
||||
setIsDragActive(dragActive);
|
||||
}
|
||||
};
|
||||
|
||||
const onDragLeave = () => {
|
||||
setIsDragActive(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledDragAndDrop
|
||||
data-title={item.title}
|
||||
value={value}
|
||||
className={`files-item ${className}`}
|
||||
onDrop={onDrop}
|
||||
onMouseDown={onMouseDown}
|
||||
dragging={dragging && isDragging}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
>
|
||||
<TableRow
|
||||
{...dragStyles}
|
||||
dragging={dragging && isDragging}
|
||||
selectionProp={selectionProp}
|
||||
key={item.id}
|
||||
item={item}
|
||||
element={element}
|
||||
fileContextClick={fileContextClick}
|
||||
onContentSelect={onContentFileSelect}
|
||||
onClick={onMouseClick}
|
||||
{...contextOptionsProps}
|
||||
checked={checkedProps}
|
||||
>
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<FileNameCell {...props} />
|
||||
<StyledBadgesContainer>{badgesComponent}</StyledBadgesContainer>
|
||||
</TableCell>
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<AuthorCell sideColor={sideColor} {...props} />
|
||||
</TableCell>
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<DateCell create sideColor={sideColor} {...props} />
|
||||
</TableCell>
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<DateCell sideColor={sideColor} {...props} />
|
||||
</TableCell>
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<SizeCell sideColor={sideColor} {...props} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<TypeCell sideColor={sideColor} {...props} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell {...dragStyles} {...selectionProp}>
|
||||
<StyledShare>{sharedButton}</StyledShare>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</StyledDragAndDrop>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation("Home")(
|
||||
withFileActions(
|
||||
withRouter(withContextOptions(withContent(withBadges(FilesTableRow))))
|
||||
)
|
||||
);
|
@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { StyledText, StyledAuthorAvatar } from "./CellStyles";
|
||||
|
||||
const AuthorCell = ({ fileOwner, sideColor, item }) => {
|
||||
return (
|
||||
<>
|
||||
<StyledAuthorAvatar
|
||||
src={item.createdBy.avatarSmall}
|
||||
className="author-avatar-cell"
|
||||
/>
|
||||
<StyledText
|
||||
color={sideColor}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
title={fileOwner}
|
||||
truncate
|
||||
>
|
||||
{fileOwner}
|
||||
</StyledText>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AuthorCell;
|
@ -0,0 +1,16 @@
|
||||
import styled from "styled-components";
|
||||
import Text from "@appserver/components/text";
|
||||
|
||||
const StyledText = styled(Text)`
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
`;
|
||||
|
||||
const StyledAuthorAvatar = styled.img`
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
border-radius: 20px;
|
||||
`;
|
||||
|
||||
export { StyledText, StyledAuthorAvatar };
|
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { StyledText } from "./CellStyles";
|
||||
|
||||
const DateCell = ({ create, updatedDate, createdDate, sideColor, item }) => {
|
||||
const { fileExst, contentLength, providerKey } = item;
|
||||
|
||||
const date = create ? createdDate : updatedDate;
|
||||
|
||||
return (
|
||||
<StyledText
|
||||
title={date}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideColor}
|
||||
className="row_update-text"
|
||||
truncate
|
||||
>
|
||||
{(fileExst || contentLength || !providerKey) && date && date}
|
||||
</StyledText>
|
||||
);
|
||||
};
|
||||
|
||||
export default DateCell;
|
@ -0,0 +1,35 @@
|
||||
import React from "react";
|
||||
import Link from "@appserver/components/link";
|
||||
import Text from "@appserver/components/text";
|
||||
|
||||
const FileNameCell = ({ item, titleWithoutExt, linkStyles }) => {
|
||||
const { fileExst } = item;
|
||||
return (
|
||||
<Link
|
||||
type="page"
|
||||
title={titleWithoutExt}
|
||||
fontWeight="600"
|
||||
fontSize="15px"
|
||||
{...linkStyles}
|
||||
color="#333"
|
||||
isTextOverflow
|
||||
>
|
||||
{titleWithoutExt}
|
||||
{fileExst ? (
|
||||
<Text
|
||||
className="badge-ext"
|
||||
as="span"
|
||||
color="#A3A9AE"
|
||||
fontSize="15px"
|
||||
fontWeight={600}
|
||||
title={fileExst}
|
||||
truncate={true}
|
||||
>
|
||||
{fileExst}
|
||||
</Text>
|
||||
) : null}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default FileNameCell;
|
@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import { StyledText } from "./CellStyles";
|
||||
|
||||
const SizeCell = ({ t, item, sideColor }) => {
|
||||
const {
|
||||
fileExst,
|
||||
contentLength,
|
||||
providerKey,
|
||||
filesCount,
|
||||
foldersCount,
|
||||
} = item;
|
||||
return (
|
||||
<StyledText
|
||||
color={sideColor}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
title=""
|
||||
truncate
|
||||
>
|
||||
{fileExst || contentLength
|
||||
? contentLength
|
||||
: !providerKey
|
||||
? `${t("TitleDocuments")}: ${filesCount} | ${t(
|
||||
"TitleSubfolders"
|
||||
)}: ${foldersCount}`
|
||||
: ""}
|
||||
</StyledText>
|
||||
);
|
||||
};
|
||||
|
||||
export default SizeCell;
|
@ -0,0 +1,40 @@
|
||||
import React from "react";
|
||||
import { StyledText } from "./CellStyles";
|
||||
import { FileType } from "@appserver/common/constants";
|
||||
|
||||
const TypeCell = ({ t, item, sideColor }) => {
|
||||
const { fileExst, fileType } = item;
|
||||
const getItemType = () => {
|
||||
switch (fileType) {
|
||||
case FileType.Unknown:
|
||||
return t("Common:Unknown");
|
||||
case FileType.Archive:
|
||||
return t("Common:Archive");
|
||||
case FileType.Video:
|
||||
return t("Common:Video");
|
||||
case FileType.Audio:
|
||||
return t("Common:Audio");
|
||||
case FileType.Image:
|
||||
return t("Common:Image");
|
||||
case FileType.Spreadsheet:
|
||||
return t("Spreadsheet");
|
||||
case FileType.Presentation:
|
||||
return t("Presentation");
|
||||
case FileType.Document:
|
||||
return t("Document");
|
||||
|
||||
default:
|
||||
return t("Folder");
|
||||
}
|
||||
};
|
||||
|
||||
const type = getItemType();
|
||||
const Exst = fileExst ? fileExst.slice(1).toUpperCase() : "";
|
||||
|
||||
return (
|
||||
<StyledText fontSize="12px" fontWeight="400" color={sideColor} truncate>
|
||||
{type} {Exst}
|
||||
</StyledText>
|
||||
);
|
||||
};
|
||||
export default TypeCell;
|
@ -33,7 +33,7 @@ const FilesTile = (props) => {
|
||||
//element,
|
||||
getIcon,
|
||||
onFilesClick,
|
||||
onMouseUp,
|
||||
onMouseClick,
|
||||
showShare,
|
||||
} = props;
|
||||
const temporaryIcon = getIcon(
|
||||
@ -80,7 +80,7 @@ const FilesTile = (props) => {
|
||||
tileContextClick={fileContextClick}
|
||||
isPrivacy={isPrivacy}
|
||||
dragging={dragging && isDragging}
|
||||
onMouseUp={onMouseUp}
|
||||
onClick={onMouseClick}
|
||||
thumbnailClick={onFilesClick}
|
||||
onDoubleClick={onFilesClick}
|
||||
{...checkedProps}
|
||||
|
@ -1,32 +1,25 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
|
||||
import TileContainer from "./sub-components/TileContainer";
|
||||
import FileTile from "./FileTile";
|
||||
|
||||
const FilesTileContainer = ({ filesList, t }) => {
|
||||
const FilesTileContainer = ({ filesList, t, sectionWidth }) => {
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) => (
|
||||
<TileContainer
|
||||
className="tile-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
headingFolders={t("Folders")}
|
||||
headingFiles={t("Files")}
|
||||
>
|
||||
{filesList.map((item, index) => (
|
||||
<FileTile
|
||||
key={`${item.id}_${index}`}
|
||||
item={item}
|
||||
sectionWidth={context.sectionWidth}
|
||||
/>
|
||||
))}
|
||||
</TileContainer>
|
||||
)}
|
||||
</Consumer>
|
||||
<TileContainer
|
||||
className="tile-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
headingFolders={t("Folders")}
|
||||
headingFiles={t("Files")}
|
||||
>
|
||||
{filesList.map((item, index) => (
|
||||
<FileTile
|
||||
key={`${item.id}_${index}`}
|
||||
item={item}
|
||||
sectionWidth={sectionWidth}
|
||||
/>
|
||||
))}
|
||||
</TileContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,8 @@ import FilesRowContainer from "./RowsView/FilesRowContainer";
|
||||
import FilesTileContainer from "./TilesView/FilesTileContainer";
|
||||
import EmptyContainer from "../../../../components/EmptyContainer";
|
||||
import withLoader from "../../../../HOCs/withLoader";
|
||||
import TableView from "./TableView/TableContainer";
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
|
||||
let currentDroppable = null;
|
||||
|
||||
@ -26,6 +28,7 @@ const SectionBodyContent = (props) => {
|
||||
moveDragItems,
|
||||
viewAs,
|
||||
setSelection,
|
||||
setViewAs,
|
||||
} = props;
|
||||
|
||||
useEffect(() => {
|
||||
@ -44,6 +47,7 @@ const SectionBodyContent = (props) => {
|
||||
document.addEventListener("dragover", onDragOver);
|
||||
document.addEventListener("dragleave", onDragLeaveDoc);
|
||||
document.addEventListener("drop", onDropEvent);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("mousedown", onMouseDown);
|
||||
window.removeEventListener("mouseup", onMouseUp);
|
||||
@ -79,13 +83,31 @@ const SectionBodyContent = (props) => {
|
||||
const droppable = wrapperElement.closest(".droppable");
|
||||
if (currentDroppable !== droppable) {
|
||||
if (currentDroppable) {
|
||||
currentDroppable.classList.remove("droppable-hover");
|
||||
if (viewAs === "table") {
|
||||
const value = currentDroppable.getAttribute("value");
|
||||
const classElements = document.getElementsByClassName(value);
|
||||
|
||||
for (let cl of classElements) {
|
||||
cl.classList.remove("droppable-hover");
|
||||
}
|
||||
} else {
|
||||
currentDroppable.classList.remove("droppable-hover");
|
||||
}
|
||||
}
|
||||
currentDroppable = droppable;
|
||||
|
||||
if (currentDroppable) {
|
||||
currentDroppable.classList.add("droppable-hover");
|
||||
currentDroppable = droppable;
|
||||
if (viewAs === "table") {
|
||||
const value = currentDroppable.getAttribute("value");
|
||||
const classElements = document.getElementsByClassName(value);
|
||||
|
||||
for (let cl of classElements) {
|
||||
cl.classList.add("droppable-hover");
|
||||
}
|
||||
} else {
|
||||
currentDroppable.classList.add("droppable-hover");
|
||||
currentDroppable = droppable;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -154,12 +176,23 @@ const SectionBodyContent = (props) => {
|
||||
};
|
||||
|
||||
//console.log("Files Home SectionBodyContent render", props);
|
||||
return (!fileActionId && isEmptyFilesList) || null ? (
|
||||
<EmptyContainer />
|
||||
) : viewAs === "tile" ? (
|
||||
<FilesTileContainer t={t} />
|
||||
) : (
|
||||
<FilesRowContainer tReady={tReady} />
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) =>
|
||||
(!fileActionId && isEmptyFilesList) || null ? (
|
||||
<EmptyContainer />
|
||||
) : viewAs === "tile" ? (
|
||||
<FilesTileContainer sectionWidth={context.sectionWidth} t={t} />
|
||||
) : viewAs === "table" ? (
|
||||
<TableView sectionWidth={context.sectionWidth} tReady={tReady} />
|
||||
) : (
|
||||
<FilesRowContainer
|
||||
sectionWidth={context.sectionWidth}
|
||||
tReady={tReady}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -180,6 +213,7 @@ export default inject(
|
||||
startDrag,
|
||||
setStartDrag,
|
||||
setSelection,
|
||||
setViewAs,
|
||||
} = filesStore;
|
||||
|
||||
return {
|
||||
@ -197,6 +231,7 @@ export default inject(
|
||||
moveDragItems: filesActionsStore.moveDragItems,
|
||||
viewAs,
|
||||
setSelection,
|
||||
setViewAs,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -94,7 +94,14 @@ class SectionFilterContent extends React.Component {
|
||||
|
||||
onChangeViewAs = (view) => {
|
||||
const { setViewAs } = this.props;
|
||||
setViewAs(view);
|
||||
//const tabletView = isTabletView();
|
||||
|
||||
if (view === "row") {
|
||||
//tabletView ? setViewAs("table") : setViewAs("row");
|
||||
setViewAs("table");
|
||||
} else {
|
||||
setViewAs(view);
|
||||
}
|
||||
};
|
||||
|
||||
getData = () => {
|
||||
@ -270,15 +277,11 @@ class SectionFilterContent extends React.Component {
|
||||
{
|
||||
value: "row",
|
||||
label: t("ViewList"),
|
||||
isSetting: isMobileOnly,
|
||||
default: true,
|
||||
icon: "/static/images/view-rows.react.svg",
|
||||
},
|
||||
{
|
||||
value: "tile",
|
||||
label: t("ViewTiles"),
|
||||
isSetting: isMobileOnly,
|
||||
default: true,
|
||||
icon: "/static/images/view-tiles.react.svg",
|
||||
callback: createThumbnails,
|
||||
},
|
||||
|
@ -216,7 +216,6 @@ class SectionHeaderContent extends React.Component {
|
||||
.downloadAction(this.props.t("Translations:ArchivingData"))
|
||||
.catch((err) => toastr.error(err));
|
||||
|
||||
downloadAsAction = () => this.props.setDownloadDialogVisible(true);
|
||||
renameAction = () => console.log("renameAction click");
|
||||
onOpenSharingPanel = () => this.props.setSharingPanelVisible(true);
|
||||
|
||||
@ -241,8 +240,6 @@ class SectionHeaderContent extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
onEmptyTrashAction = () => this.props.setEmptyTrashDialogVisible(true);
|
||||
|
||||
getContextOptionsFolder = () => {
|
||||
const { t } = this.props;
|
||||
return [
|
||||
@ -311,21 +308,9 @@ class SectionHeaderContent extends React.Component {
|
||||
};
|
||||
|
||||
getMenuItems = () => {
|
||||
const {
|
||||
t,
|
||||
hasSelection,
|
||||
isAccessedSelected,
|
||||
isWebEditSelected,
|
||||
isViewedSelected,
|
||||
deleteDialogVisible,
|
||||
isRecycleBin,
|
||||
isThirdPartySelection,
|
||||
isPrivacy,
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
isShareFolder,
|
||||
personal,
|
||||
} = this.props;
|
||||
const { t, getHeaderMenu } = this.props;
|
||||
|
||||
const headerMenu = getHeaderMenu(t);
|
||||
|
||||
let menu = [
|
||||
{
|
||||
@ -379,75 +364,9 @@ class SectionHeaderContent extends React.Component {
|
||||
],
|
||||
onSelect: this.onSelect,
|
||||
},
|
||||
{
|
||||
label: t("Share"),
|
||||
disabled: isFavoritesFolder || isRecentFolder || !isAccessedSelected,
|
||||
onClick: this.onOpenSharingPanel,
|
||||
},
|
||||
{
|
||||
label: t("Common:Download"),
|
||||
disabled: !hasSelection,
|
||||
onClick: this.downloadAction,
|
||||
},
|
||||
{
|
||||
label: t("Translations:DownloadAs"),
|
||||
disabled: !hasSelection || !isWebEditSelected,
|
||||
onClick: this.downloadAsAction,
|
||||
},
|
||||
{
|
||||
label: t("MoveTo"),
|
||||
disabled:
|
||||
isFavoritesFolder ||
|
||||
isRecentFolder ||
|
||||
!isAccessedSelected ||
|
||||
!hasSelection ||
|
||||
isThirdPartySelection,
|
||||
onClick: this.onMoveAction,
|
||||
},
|
||||
{
|
||||
label: t("Translations:Copy"),
|
||||
disabled: !hasSelection,
|
||||
onClick: this.onCopyAction,
|
||||
},
|
||||
{
|
||||
label: t("Common:Delete"),
|
||||
disabled:
|
||||
!hasSelection || !deleteDialogVisible || isThirdPartySelection,
|
||||
onClick: this.onDeleteAction,
|
||||
},
|
||||
];
|
||||
|
||||
if (isRecycleBin) {
|
||||
menu.push({
|
||||
label: t("EmptyRecycleBin"),
|
||||
onClick: this.onEmptyTrashAction,
|
||||
});
|
||||
|
||||
menu.splice(4, 2, {
|
||||
label: t("Translations:Restore"),
|
||||
onClick: this.onMoveAction,
|
||||
});
|
||||
|
||||
menu.splice(1, 1);
|
||||
}
|
||||
|
||||
if (isPrivacy) {
|
||||
menu.splice(1, 1);
|
||||
menu.splice(2, 1);
|
||||
menu.splice(3, 1);
|
||||
}
|
||||
|
||||
if (isShareFolder) {
|
||||
menu.splice(4, 1);
|
||||
}
|
||||
|
||||
if (isRecentFolder || isFavoritesFolder) {
|
||||
menu.splice(1, 1);
|
||||
}
|
||||
|
||||
if (personal && !isWebEditSelected && !isViewedSelected) {
|
||||
menu.splice(1, 1);
|
||||
}
|
||||
menu = [...menu, ...headerMenu];
|
||||
|
||||
return menu;
|
||||
};
|
||||
@ -467,6 +386,7 @@ class SectionHeaderContent extends React.Component {
|
||||
isDesktop,
|
||||
isTabletView,
|
||||
personal,
|
||||
viewAs,
|
||||
} = this.props;
|
||||
|
||||
const menuItems = this.getMenuItems();
|
||||
@ -482,7 +402,7 @@ class SectionHeaderContent extends React.Component {
|
||||
isDesktop={isDesktop}
|
||||
isTabletView={isTabletView}
|
||||
>
|
||||
{isHeaderVisible ? (
|
||||
{isHeaderVisible && viewAs !== "table" ? (
|
||||
<div className="group-button-menu-container">
|
||||
<GroupButtonsMenu
|
||||
checked={isHeaderChecked}
|
||||
@ -579,7 +499,6 @@ export default inject(
|
||||
auth,
|
||||
filesStore,
|
||||
dialogsStore,
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
filesActionsStore,
|
||||
settingsStore,
|
||||
@ -588,38 +507,23 @@ export default inject(
|
||||
setSelected,
|
||||
fileActionStore,
|
||||
fetchFiles,
|
||||
selection,
|
||||
filter,
|
||||
canCreate,
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
userAccess,
|
||||
isAccessedSelected,
|
||||
isThirdPartySelection,
|
||||
isWebEditSelected,
|
||||
setIsLoading,
|
||||
isViewedSelected,
|
||||
hasSelection,
|
||||
viewAs,
|
||||
} = filesStore;
|
||||
const {
|
||||
isRecycleBinFolder,
|
||||
isPrivacyFolder,
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
isShareFolder,
|
||||
} = treeFoldersStore;
|
||||
const { setAction } = fileActionStore;
|
||||
const {
|
||||
setSharingPanelVisible,
|
||||
setMoveToPanelVisible,
|
||||
setCopyPanelVisible,
|
||||
setEmptyTrashDialogVisible,
|
||||
setDownloadDialogVisible,
|
||||
setDeleteDialogVisible,
|
||||
} = dialogsStore;
|
||||
|
||||
const { deleteAction, downloadAction } = filesActionsStore;
|
||||
const { deleteAction, downloadAction, getHeaderMenu } = filesActionsStore;
|
||||
|
||||
return {
|
||||
isDesktop: auth.settingsStore.isDesktopClient,
|
||||
@ -627,25 +531,15 @@ export default inject(
|
||||
title: selectedFolderStore.title,
|
||||
parentId: selectedFolderStore.parentId,
|
||||
currentFolderId: selectedFolderStore.id,
|
||||
isRecycleBin: isRecycleBinFolder,
|
||||
isPrivacy: isPrivacyFolder,
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
isShareFolder,
|
||||
filter,
|
||||
canCreate,
|
||||
hasSelection,
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
deleteDialogVisible: userAccess,
|
||||
isAccessedSelected,
|
||||
isThirdPartySelection,
|
||||
isWebEditSelected,
|
||||
isViewedSelected,
|
||||
isTabletView: auth.settingsStore.isTabletView,
|
||||
confirmDelete: settingsStore.confirmDelete,
|
||||
personal: auth.settingsStore.personal,
|
||||
viewAs,
|
||||
|
||||
setSelected,
|
||||
setAction,
|
||||
@ -654,11 +548,10 @@ export default inject(
|
||||
setSharingPanelVisible,
|
||||
setMoveToPanelVisible,
|
||||
setCopyPanelVisible,
|
||||
setEmptyTrashDialogVisible,
|
||||
deleteAction,
|
||||
setDeleteDialogVisible,
|
||||
setDownloadDialogVisible,
|
||||
downloadAction,
|
||||
getHeaderMenu,
|
||||
};
|
||||
}
|
||||
)(
|
||||
|
@ -245,7 +245,6 @@ class PureHome extends React.Component {
|
||||
uploadFiles
|
||||
onDrop={isRecycleBinFolder || isPrivacyFolder ? null : this.onDrop}
|
||||
setSelections={this.props.setSelections}
|
||||
onMouseMove={this.onMouseMove}
|
||||
showPrimaryProgressBar={primaryProgressDataVisible}
|
||||
primaryProgressBarValue={primaryProgressDataPercent}
|
||||
primaryProgressBarIcon={primaryProgressDataIcon}
|
||||
|
@ -645,6 +645,127 @@ class FilesActionStore {
|
||||
this.uploadDataStore.itemOperationToFolder(operationData);
|
||||
}
|
||||
};
|
||||
|
||||
getHeaderMenu = (t) => {
|
||||
const {
|
||||
isFavoritesFolder,
|
||||
isRecentFolder,
|
||||
isRecycleBinFolder,
|
||||
isPrivacyFolder,
|
||||
isShareFolder,
|
||||
} = this.treeFoldersStore;
|
||||
const {
|
||||
selection,
|
||||
isAccessedSelected,
|
||||
isWebEditSelected,
|
||||
isThirdPartySelection,
|
||||
userAccess,
|
||||
isViewedSelected,
|
||||
hasSelection,
|
||||
} = this.filesStore;
|
||||
|
||||
const {
|
||||
setSharingPanelVisible,
|
||||
setDownloadDialogVisible,
|
||||
setMoveToPanelVisible,
|
||||
setCopyPanelVisible,
|
||||
setDeleteDialogVisible,
|
||||
setEmptyTrashDialogVisible,
|
||||
} = this.dialogsStore;
|
||||
|
||||
const selectionCount = selection.length;
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
label: t("Share"),
|
||||
disabled: isFavoritesFolder || isRecentFolder || !isAccessedSelected,
|
||||
onClick: () => setSharingPanelVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("Common:Download"),
|
||||
disabled: !hasSelection,
|
||||
onClick: () =>
|
||||
this.downloadAction(t("Translations:ArchivingData")).catch((err) =>
|
||||
toastr.error(err)
|
||||
),
|
||||
},
|
||||
{
|
||||
label: t("Translations:DownloadAs"),
|
||||
disabled: !hasSelection || !isWebEditSelected,
|
||||
onClick: () => setDownloadDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("MoveTo"),
|
||||
disabled:
|
||||
isFavoritesFolder ||
|
||||
isRecentFolder ||
|
||||
!isAccessedSelected ||
|
||||
!hasSelection ||
|
||||
isThirdPartySelection,
|
||||
onClick: () => setMoveToPanelVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("Translations:Copy"),
|
||||
disabled: !hasSelection,
|
||||
onClick: () => setCopyPanelVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("Common:Delete"),
|
||||
disabled: !hasSelection || isThirdPartySelection,
|
||||
onClick: () => {
|
||||
if (this.settingsStore.confirmDelete) {
|
||||
setDeleteDialogVisible(true);
|
||||
} else {
|
||||
const translations = {
|
||||
deleteOperation: t("Translations:DeleteOperation"),
|
||||
deleteFromTrash: t("Translations:DeleteFromTrash"),
|
||||
deleteSelectedElem: t("Translations:DeleteSelectedElem"),
|
||||
};
|
||||
|
||||
this.deleteAction(translations).catch((err) => toastr.error(err));
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (isRecycleBinFolder) {
|
||||
headerMenu.push({
|
||||
label: t("EmptyRecycleBin"),
|
||||
onClick: () => setEmptyTrashDialogVisible(true),
|
||||
});
|
||||
|
||||
headerMenu.splice(4, 2, {
|
||||
label: t("Translations:Restore"),
|
||||
onClick: () => setMoveToPanelVisible(true),
|
||||
});
|
||||
|
||||
headerMenu.splice(1, 1);
|
||||
}
|
||||
|
||||
if (isPrivacyFolder) {
|
||||
headerMenu.splice(1, 1);
|
||||
headerMenu.splice(2, 1);
|
||||
headerMenu.splice(3, 1);
|
||||
}
|
||||
|
||||
if (isShareFolder) {
|
||||
headerMenu.splice(4, 1);
|
||||
}
|
||||
|
||||
if (isRecentFolder || isFavoritesFolder) {
|
||||
headerMenu.splice(1, 1);
|
||||
}
|
||||
|
||||
if (
|
||||
this.authStore.settingsStore.personal &&
|
||||
!isWebEditSelected &&
|
||||
!isViewedSelected
|
||||
) {
|
||||
headerMenu.splice(1, 1);
|
||||
}
|
||||
|
||||
return headerMenu;
|
||||
};
|
||||
}
|
||||
|
||||
export default FilesActionStore;
|
||||
|
@ -31,7 +31,7 @@ class FilesStore {
|
||||
|
||||
isLoaded = false;
|
||||
isLoading = false;
|
||||
viewAs = localStorage.getItem("viewAs") || "row";
|
||||
viewAs = localStorage.getItem("viewAs") || "table";
|
||||
dragging = false;
|
||||
privacyInstructions = "https://www.onlyoffice.com/private-rooms.aspx";
|
||||
isInit = false;
|
||||
@ -992,7 +992,7 @@ class FilesStore {
|
||||
}
|
||||
|
||||
get isHeaderVisible() {
|
||||
return this.selection.length > 0 || this.selected !== "close";
|
||||
return this.selection.length > 0;
|
||||
}
|
||||
|
||||
get isHeaderIndeterminate() {
|
||||
@ -1321,7 +1321,13 @@ class FilesStore {
|
||||
}
|
||||
|
||||
//this.selected === "close" && this.setSelected("none");
|
||||
this.setSelection(newSelection);
|
||||
|
||||
//need fo table view
|
||||
const clearSelection = Object.values(
|
||||
newSelection.reduce((item, n) => ((item[n.id] = n), item), {})
|
||||
);
|
||||
|
||||
this.setSelection(clearSelection);
|
||||
//}
|
||||
};
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (Erstellung)",
|
||||
"GroupAction": "Arbeit mit Gruppe",
|
||||
"Members": "Mitglieder",
|
||||
"Name": "Name",
|
||||
"SearchAddedMembers": "Hinzugefügte Mitglieder suchen",
|
||||
"SelectAction": "Auswählen",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' wurde erfolgreich gespeichert"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "Die Haupt-E-Mail-Adresse wird für Benachrichtigungen und Zugriffswiederherstellung benutzt. <1> Sie können in dieser Domain eine neue E-Mail für den Benutzer erstellen und ein Einmalkennwort für die erste Anmeldung festlegen.</1>",
|
||||
"FirstName": "Vorname",
|
||||
"Message": "Chat",
|
||||
"Phone": "Telefon",
|
||||
"ProductsAndInstruments_Products": "Module",
|
||||
"ProfileAction": "Arbeit mit dem Profil",
|
||||
"ProfileTypePopupHelper": "Gäste haben eingeschränkten Zugriff auf einige Funktionen und Module auf dem Portal",
|
||||
|
@ -2,8 +2,7 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (creation)",
|
||||
"GroupAction": "Group action",
|
||||
"Members": "Members",
|
||||
"Name": "Name",
|
||||
"SearchAddedMembers": "Search added members",
|
||||
"SelectAction": "Select",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' has been saved successfully"
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
"EmailPopupHelper": "The main email is used for notifications and restoring access. <1> You can create a new email on this domain for the user and set a one-time password for the first login.</1>",
|
||||
"FirstName": "First Name",
|
||||
"Message": "Talk",
|
||||
"Phone": "Phone",
|
||||
"ProductsAndInstruments_Products": "Modules",
|
||||
"ProfileAction": "Profile action",
|
||||
"ProfileTypePopupHelper": "Guests have limited access to some portal features and modules",
|
||||
|
@ -2,7 +2,6 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (création)",
|
||||
"GroupAction": "Action de groupe",
|
||||
"Members": "Membres",
|
||||
"Name": "Nom",
|
||||
"SearchAddedMembers": "Rechercher les membres ajoutés",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' a été sauvegardé avec succès"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "L'adresse mail principal est utilisée pour les notifications et pour la restauration de l'accès. <1> Vous pouvez créer une nouvelle adresse mail sur ce domaine pour cet utilisateur et définir un mot de passe à usage unique pour la première connexion.",
|
||||
"FirstName": "Prénom",
|
||||
"Message": "Chat",
|
||||
"Phone": "Téléphone",
|
||||
"ProductsAndInstruments_Products": "Modules",
|
||||
"ProfileAction": "Action sur le profil",
|
||||
"ProfileTypePopupHelper": "Les invités ont l'accès limité aux certaines fonctions du portail et modules",
|
||||
|
@ -2,7 +2,6 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (creazione)",
|
||||
"GroupAction": "Azione di gruppo",
|
||||
"Members": "Membri",
|
||||
"Name": "Nome",
|
||||
"SearchAddedMembers": "Cerca membri aggiunti",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' è stato salvato con successo"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "L'email principale viene utilizzata per le notifiche e il ripristino dell'accesso. <1> Puoi creare una nuova email su questo dominio per l'utente e impostare una password monouso per il primo accesso.</1>",
|
||||
"FirstName": "Nome",
|
||||
"Message": "Parla",
|
||||
"Phone": "Telefono",
|
||||
"ProductsAndInstruments_Products": "Moduli",
|
||||
"ProfileAction": "Azione del profilo",
|
||||
"ProfileTypePopupHelper": "Gli ospiti hanno un tipo di accesso limitato a delle funzionalità, e nell'uso dei moduli di questo portale",
|
||||
|
@ -2,7 +2,6 @@
|
||||
"CustomNewDepartment": "{{ກຸ່ມສົນທະນາ}} (ສ້າງ)",
|
||||
"GroupAction": "ກຸ່ມດຳເນີນການ",
|
||||
"Members": "ສະມາຊິກ",
|
||||
"Name": "ຊື່",
|
||||
"SearchAddedMembers": "ຄົ້ນຫາສະມາຊິກເພີ່ມ",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' ໄດ້ຖືກບັນທຶກ ສຳ ເລັດແລ້ວ"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "ອີເມວຫຼັກແມ່ນໃຊ້ ສຳ ລັບການແຈ້ງເຕືອນແລະການກູ້ຄືນການເຂົ້າເຖິງ.<1> ທ່ານສາມາດສ້າງອີເມວໃໝ່ໃນໂດເມນນີ້ສຳລັບຜູ້ໃຊ້ແລະຕັ້ງລະຫັດຜ່ານໜຶ່ງຄັ້ງສຳລັບການເຂົ້າສູ່ລະບົບຄັ້ງທຳອິດ.</1>",
|
||||
"FirstName": "ຊື່ແທ້",
|
||||
"Message": "ເວົ້າ",
|
||||
"Phone": "ໂທລະສັບ",
|
||||
"ProductsAndInstruments_Products": "ໂມດູນ",
|
||||
"ProfileAction": "ການດຳເນີນການໂປຣໄຟລ໌",
|
||||
"ProfileTypePopupHelper": "ບຸກຄົນທົ່ວໄປຖືກຈຳກັດໃຫ້ເຂົ້າເຖິງຄຸນລັກສະນະບາງຢ່າງຂອງ Portal ແລະ ໂມດູນ",
|
||||
|
@ -2,7 +2,6 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (criação)",
|
||||
"GroupAction": "Ação em grupo",
|
||||
"Members": "Membros",
|
||||
"Name": "Nome",
|
||||
"SearchAddedMembers": "Pesquisar membros adicionados",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' foi salvo com sucesso"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "O e-mail principal é utilizado para notificações e restabelecimento do acesso. <1> Você pode criar um novo e-mail neste domínio para o usuário e definir uma senha única para o primeiro login.</1>",
|
||||
"FirstName": "Primeiro nome",
|
||||
"Message": "Bate-papo com outros usuários",
|
||||
"Phone": "Telefone",
|
||||
"ProductsAndInstruments_Products": "Módulos",
|
||||
"ProfileAction": "Ação de perfil",
|
||||
"ProfileTypePopupHelper": "Os hóspedes têm acesso limitado a alguns recursos e módulos do portal",
|
||||
|
@ -2,7 +2,6 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (creare)",
|
||||
"GroupAction": "Acțiune de grup",
|
||||
"Members": "Membrii",
|
||||
"Name": "Numele",
|
||||
"SearchAddedMembers": "Căutare după membrii adăugați",
|
||||
"SuccessSaveGroup": "{{groupCaption}} '{{ groupName }}' a fost salvat cu succes"
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@
|
||||
"EmailPopupHelper": "Adresa de email principală se folosește pentru trimiterea notificărilor și restabilirea accesului. <1> Puteți crea o nouă adresă de e-mail pentru utilizatorul pe acest domeniu și o parolă temporară unică cu care să conectează prima dată.</1>",
|
||||
"FirstName": "Prenume",
|
||||
"Message": "Chat",
|
||||
"Phone": "Telefon",
|
||||
"ProductsAndInstruments_Products": "Module",
|
||||
"ProfileAction": "Acțiuni cu profil",
|
||||
"ProfileTypePopupHelper": "Invitații au acces limitat la unele funcții și module ale portalului",
|
||||
|
@ -2,8 +2,7 @@
|
||||
"CustomNewDepartment": "{{groupCaption}} (создание)",
|
||||
"GroupAction": "Работа с группой",
|
||||
"Members": "Участники",
|
||||
"Name": "Название",
|
||||
"SearchAddedMembers": "Поиск добавленных участников",
|
||||
"SelectAction": "Выбрать",
|
||||
"SuccessSaveGroup": "Успешное сохранение: {{groupCaption, lowercase}} '{{ groupName }}'"
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
"EmailPopupHelper": "Основной email нужен для восстановления доступа к порталу в случае потери пароля, а также для отправки оповещений. <1>Вы можете создать новый email на домене в качестве основного. В этом случае потребуется задать одноразовый пароль, чтобы пользователь смог войти на портал в первый раз.</1> Основной email можно использовать как логин при входе на портал.",
|
||||
"FirstName": "Имя",
|
||||
"Message": "Чат",
|
||||
"Phone": "Телефон",
|
||||
"ProductsAndInstruments_Products": "Модули",
|
||||
"ProfileAction": "Работа с профилем",
|
||||
"ProfileTypePopupHelper": "Гости имеют ограниченный доступ к некоторым функциям и модулям портала",
|
||||
|
152
products/ASC.People/Client/src/HOCs/withContent.js
Normal file
152
products/ASC.People/Client/src/HOCs/withContent.js
Normal file
@ -0,0 +1,152 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import Link from "@appserver/components/link";
|
||||
import LinkWithDropdown from "@appserver/components/link-with-dropdown";
|
||||
import Avatar from "@appserver/components/avatar";
|
||||
|
||||
import config from "../../package.json";
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
import { AppServerConfig } from "@appserver/common/constants";
|
||||
|
||||
export default function withContent(WrappedContent) {
|
||||
const WithContent = (props) => {
|
||||
const {
|
||||
item,
|
||||
selectGroup,
|
||||
fetchProfile,
|
||||
history,
|
||||
checked,
|
||||
selectUser,
|
||||
deselectUser,
|
||||
isAdmin,
|
||||
} = props;
|
||||
const { userName, mobilePhone, email, role, displayName, avatar } = item;
|
||||
|
||||
const onContentRowSelect = (checked, user) =>
|
||||
checked ? selectUser(user) : deselectUser(user);
|
||||
|
||||
const checkedProps = isAdmin ? { checked } : {};
|
||||
|
||||
const element = (
|
||||
<Avatar size="min" role={role} userName={displayName} source={avatar} />
|
||||
);
|
||||
|
||||
const getFormattedGroups = () => {
|
||||
let temp = [];
|
||||
const groups = item.groups;
|
||||
const linkColor = item.statusType === "pending" ? "#D0D5DA" : "#A3A9AE";
|
||||
|
||||
if (!groups) temp.push({ key: 0, label: "" });
|
||||
|
||||
groups &&
|
||||
groups.map((group) =>
|
||||
temp.push({
|
||||
key: group.id,
|
||||
label: group.name,
|
||||
onClick: () => selectGroup(group.id),
|
||||
})
|
||||
);
|
||||
|
||||
if (temp.length <= 1) {
|
||||
return (
|
||||
<Link
|
||||
isTextOverflow
|
||||
containerMinWidth="120px"
|
||||
containerWidth="15%"
|
||||
type="action"
|
||||
title={temp[0].label}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={linkColor}
|
||||
onClick={temp[0].onClick}
|
||||
>
|
||||
{temp[0].label}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LinkWithDropdown
|
||||
isTextOverflow
|
||||
containerMinWidth="120px"
|
||||
containerWidth="15%"
|
||||
title={temp[0].label}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={linkColor}
|
||||
data={temp}
|
||||
>
|
||||
{temp[0].label}
|
||||
</LinkWithDropdown>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const groups = getFormattedGroups();
|
||||
|
||||
const redirectToProfile = () => {
|
||||
history.push(
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/view/${userName}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onUserNameClick = useCallback(
|
||||
(e) => {
|
||||
const timer = setTimeout(() => redirectToProfile(), 500);
|
||||
e.preventDefault();
|
||||
fetchProfile(userName).finally(() => {
|
||||
clearTimeout(timer);
|
||||
if (
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/view/${userName}`
|
||||
) !== window.location.pathname
|
||||
)
|
||||
redirectToProfile();
|
||||
});
|
||||
},
|
||||
[history, userName]
|
||||
);
|
||||
|
||||
const onPhoneClick = () => window.open(`sms:${mobilePhone}`);
|
||||
const onEmailClick = () => window.open(`mailto:${email}`);
|
||||
|
||||
return (
|
||||
<WrappedContent
|
||||
onContentRowSelect={onContentRowSelect}
|
||||
onPhoneClick={onPhoneClick}
|
||||
onEmailClick={onEmailClick}
|
||||
onUserNameClick={onUserNameClick}
|
||||
groups={groups}
|
||||
checkedProps={checkedProps}
|
||||
element={element}
|
||||
isAdmin={isAdmin}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return inject(({ auth, peopleStore }, { item }) => {
|
||||
const { isAdmin, userStore } = auth;
|
||||
|
||||
const { selectGroup } = peopleStore.selectedGroupStore;
|
||||
const { getTargetUser } = peopleStore.targetUserStore;
|
||||
const { selectionStore } = peopleStore;
|
||||
|
||||
const { selection, selectUser, deselectUser } = selectionStore;
|
||||
|
||||
return {
|
||||
isAdmin,
|
||||
currentUserId: userStore.user.id,
|
||||
selectGroup,
|
||||
fetchProfile: getTargetUser,
|
||||
checked: selection.some((el) => el.id === item.id),
|
||||
selectUser,
|
||||
deselectUser,
|
||||
};
|
||||
})(observer(WithContent));
|
||||
}
|
292
products/ASC.People/Client/src/HOCs/withContextOptions.js
Normal file
292
products/ASC.People/Client/src/HOCs/withContextOptions.js
Normal file
@ -0,0 +1,292 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
import { AppServerConfig, EmployeeStatus } from "@appserver/common/constants";
|
||||
import { resendUserInvites } from "@appserver/common/api/people"; //TODO: Move to store action
|
||||
import config from "../../package.json";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import toastr from "studio/toastr";
|
||||
|
||||
export default function withContextOptions(WrappedComponent) {
|
||||
const WithContextOptions = (props) => {
|
||||
const {
|
||||
isAdmin,
|
||||
item,
|
||||
history,
|
||||
setDialogData,
|
||||
closeDialogs,
|
||||
setDeleteSelfProfileDialogVisible,
|
||||
setChangePasswordDialogVisible,
|
||||
setChangeEmailDialogVisible,
|
||||
updateUserStatus,
|
||||
setDeleteProfileDialogVisible,
|
||||
fetchProfile,
|
||||
} = props;
|
||||
const { id, options, userName, email, mobilePhone, currentUserId } = item;
|
||||
|
||||
const isRefetchPeople = true; //TODO: why always true?
|
||||
|
||||
const { t } = useTranslation(["Home", "Translations"]);
|
||||
|
||||
const onEmailSentClick = () => {
|
||||
window.open("mailto:" + email);
|
||||
};
|
||||
|
||||
const onSendMessageClick = () => {
|
||||
window.open(`sms:${mobilePhone}`);
|
||||
};
|
||||
|
||||
const redirectToEdit = () => {
|
||||
history.push(
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/edit/${userName}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onEditClick = () => {
|
||||
const timer = setTimeout(() => redirectToEdit(), 500);
|
||||
fetchProfile(userName).finally(() => {
|
||||
clearTimeout(timer);
|
||||
if (
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/edit/${userName}`
|
||||
) !== window.location.pathname
|
||||
)
|
||||
redirectToEdit();
|
||||
});
|
||||
};
|
||||
|
||||
const toggleChangeEmailDialog = () => {
|
||||
setDialogData({
|
||||
email,
|
||||
id,
|
||||
});
|
||||
|
||||
setChangeEmailDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleChangePasswordDialog = () => {
|
||||
setDialogData({
|
||||
email,
|
||||
});
|
||||
|
||||
setChangePasswordDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleDeleteSelfProfileDialog = () => {
|
||||
closeDialogs();
|
||||
|
||||
setDialogData({
|
||||
email,
|
||||
});
|
||||
|
||||
setDeleteSelfProfileDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleDeleteProfileEverDialog = () => {
|
||||
closeDialogs();
|
||||
|
||||
setDialogData({
|
||||
id,
|
||||
displayName,
|
||||
userName,
|
||||
});
|
||||
|
||||
setDeleteProfileDialogVisible(true);
|
||||
};
|
||||
|
||||
const onDisableClick = (e) => {
|
||||
//onLoading(true);
|
||||
updateUserStatus(EmployeeStatus.Disabled, [id], isRefetchPeople)
|
||||
.then(() => toastr.success(t("Translations:SuccessChangeUserStatus")))
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const onEnableClick = () => {
|
||||
//onLoading(true);
|
||||
updateUserStatus(EmployeeStatus.Active, [id], isRefetchPeople)
|
||||
.then(() => toastr.success(t("Translations:SuccessChangeUserStatus")))
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const onReassignDataClick = () => {
|
||||
history.push(
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/reassign/${userName}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onDeletePersonalDataClick = () => {
|
||||
toastr.success(t("Translations:SuccessDeletePersonalData"));
|
||||
};
|
||||
|
||||
const onInviteAgainClick = () => {
|
||||
//onLoading(true);
|
||||
resendUserInvites([id])
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
<Trans
|
||||
i18nKey="MessageEmailActivationInstuctionsSentOnEmail"
|
||||
ns="Home"
|
||||
t={t}
|
||||
>
|
||||
The email activation instructions have been sent to the
|
||||
<strong>{{ email: email }}</strong> email address
|
||||
</Trans>
|
||||
)
|
||||
)
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const getUserContextOptions = () => {
|
||||
const contextMenu = options.map((option) => {
|
||||
switch (option) {
|
||||
case "send-email":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblSendEmail"),
|
||||
"data-id": id,
|
||||
onClick: onEmailSentClick,
|
||||
};
|
||||
case "send-message":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblSendMessage"),
|
||||
"data-id": id,
|
||||
onClick: onSendMessageClick,
|
||||
};
|
||||
case "separator":
|
||||
return { key: option, isSeparator: true };
|
||||
case "edit":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Common:EditButton"),
|
||||
"data-id": id,
|
||||
onClick: onEditClick,
|
||||
};
|
||||
case "change-password":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:PasswordChangeButton"),
|
||||
"data-id": id,
|
||||
onClick: toggleChangePasswordDialog,
|
||||
};
|
||||
case "change-email":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:EmailChangeButton"),
|
||||
"data-id": id,
|
||||
onClick: toggleChangeEmailDialog,
|
||||
};
|
||||
case "delete-self-profile":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DeleteSelfProfile"),
|
||||
"data-id": id,
|
||||
onClick: toggleDeleteSelfProfileDialog,
|
||||
};
|
||||
case "disable":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DisableUserButton"),
|
||||
"data-id": id,
|
||||
onClick: onDisableClick,
|
||||
};
|
||||
case "enable":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:EnableUserButton"),
|
||||
"data-id": id,
|
||||
onClick: onEnableClick,
|
||||
};
|
||||
case "reassign-data":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:ReassignData"),
|
||||
"data-id": id,
|
||||
onClick: onReassignDataClick,
|
||||
};
|
||||
case "delete-personal-data":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:RemoveData"),
|
||||
"data-id": id,
|
||||
onClick: onDeletePersonalDataClick,
|
||||
};
|
||||
case "delete-profile":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DeleteSelfProfile"),
|
||||
"data-id": id,
|
||||
onClick: toggleDeleteProfileEverDialog,
|
||||
};
|
||||
case "invite-again":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblInviteAgain"),
|
||||
"data-id": id,
|
||||
onClick: onInviteAgainClick,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return contextMenu;
|
||||
};
|
||||
|
||||
const showContextMenu = options && options.length > 0;
|
||||
|
||||
const contextOptionsProps =
|
||||
(isAdmin && showContextMenu) || (showContextMenu && id === currentUserId)
|
||||
? {
|
||||
contextOptions: getUserContextOptions(),
|
||||
}
|
||||
: {};
|
||||
|
||||
return (
|
||||
<WrappedComponent contextOptionsProps={contextOptionsProps} {...props} />
|
||||
);
|
||||
};
|
||||
|
||||
return inject(({ auth, peopleStore }) => {
|
||||
const { isAdmin } = auth;
|
||||
|
||||
const { dialogStore, targetUserStore, usersStore } = peopleStore;
|
||||
const { getTargetUser } = targetUserStore;
|
||||
const { updateUserStatus } = usersStore;
|
||||
const {
|
||||
setDialogData,
|
||||
closeDialogs,
|
||||
setDeleteSelfProfileDialogVisible,
|
||||
setChangePasswordDialogVisible,
|
||||
setChangeEmailDialogVisible,
|
||||
setDeleteProfileDialogVisible,
|
||||
} = dialogStore;
|
||||
|
||||
return {
|
||||
isAdmin,
|
||||
fetchProfile: getTargetUser,
|
||||
setDialogData,
|
||||
closeDialogs,
|
||||
setDeleteSelfProfileDialogVisible,
|
||||
setChangePasswordDialogVisible,
|
||||
setChangeEmailDialogVisible,
|
||||
updateUserStatus,
|
||||
setDeleteProfileDialogVisible,
|
||||
};
|
||||
})(observer(WithContextOptions));
|
||||
}
|
@ -96,7 +96,7 @@ const SectionBodyContent = ({
|
||||
isLoaded,
|
||||
tReady,
|
||||
}) => {
|
||||
const { t, i18n } = useTranslation(["GroupAction", "Translations"]);
|
||||
const { t, i18n } = useTranslation(["GroupAction", "Translations", "Common"]);
|
||||
|
||||
const [inLoading, setInLoading] = useState(false);
|
||||
const [isHeadSelectorOpen, setIsHeadSelectorOpen] = useState(false);
|
||||
@ -242,7 +242,7 @@ const SectionBodyContent = ({
|
||||
hasError={!!nameError}
|
||||
errorMessage={nameError}
|
||||
isVertical={true}
|
||||
labelText={t("Name")}
|
||||
labelText={t("Common:Name")}
|
||||
>
|
||||
<TextInput
|
||||
id="group-name"
|
||||
|
@ -5,7 +5,13 @@ import {
|
||||
ChangePasswordDialog,
|
||||
DeleteSelfProfileDialog,
|
||||
DeleteProfileEverDialog,
|
||||
ChangeUserTypeDialog,
|
||||
ChangeUserStatusDialog,
|
||||
SendInviteDialog,
|
||||
DeleteUsersDialog,
|
||||
InviteDialog,
|
||||
} from "../../../../components/dialogs";
|
||||
import { EmployeeType, EmployeeStatus } from "@appserver/common/constants";
|
||||
|
||||
const Dialogs = ({
|
||||
changeEmail,
|
||||
@ -14,7 +20,13 @@ const Dialogs = ({
|
||||
deleteProfileEver,
|
||||
data,
|
||||
closeDialogs,
|
||||
filter,
|
||||
employeeDialogVisible,
|
||||
guestDialogVisible,
|
||||
activeDialogVisible,
|
||||
disableDialogVisible,
|
||||
sendInviteDialogVisible,
|
||||
deleteDialogVisible,
|
||||
invitationDialogVisible,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
@ -46,15 +58,90 @@ const Dialogs = ({
|
||||
user={data}
|
||||
/>
|
||||
)}
|
||||
{employeeDialogVisible && (
|
||||
<ChangeUserTypeDialog
|
||||
visible={employeeDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
userType={EmployeeType.User}
|
||||
/>
|
||||
)}
|
||||
{guestDialogVisible && (
|
||||
<ChangeUserTypeDialog
|
||||
visible={guestDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
userType={EmployeeType.Guest}
|
||||
/>
|
||||
)}
|
||||
{activeDialogVisible && (
|
||||
<ChangeUserStatusDialog
|
||||
visible={activeDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
userStatus={EmployeeStatus.Active}
|
||||
/>
|
||||
)}
|
||||
{disableDialogVisible && (
|
||||
<ChangeUserStatusDialog
|
||||
visible={disableDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
userStatus={EmployeeStatus.Disabled}
|
||||
/>
|
||||
)}
|
||||
{sendInviteDialogVisible && (
|
||||
<SendInviteDialog
|
||||
visible={sendInviteDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
/>
|
||||
)}
|
||||
|
||||
{deleteDialogVisible && (
|
||||
<DeleteUsersDialog
|
||||
visible={deleteDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
/>
|
||||
)}
|
||||
{invitationDialogVisible && (
|
||||
<InviteDialog
|
||||
visible={invitationDialogVisible}
|
||||
onClose={closeDialogs}
|
||||
onCloseButton={closeDialogs}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ peopleStore }) => ({
|
||||
changeEmail: peopleStore.dialogStore.changeEmail,
|
||||
changePassword: peopleStore.dialogStore.changePassword,
|
||||
deleteSelfProfile: peopleStore.dialogStore.deleteSelfProfile,
|
||||
deleteProfileEver: peopleStore.dialogStore.deleteProfileEver,
|
||||
data: peopleStore.dialogStore.data,
|
||||
closeDialogs: peopleStore.dialogStore.closeDialogs,
|
||||
}))(observer(Dialogs));
|
||||
export default inject(({ peopleStore }) => {
|
||||
const {
|
||||
changeEmail,
|
||||
changePassword,
|
||||
deleteSelfProfile,
|
||||
deleteProfileEver,
|
||||
data,
|
||||
closeDialogs,
|
||||
|
||||
employeeDialogVisible,
|
||||
guestDialogVisible,
|
||||
activeDialogVisible,
|
||||
disableDialogVisible,
|
||||
sendInviteDialogVisible,
|
||||
deleteDialogVisible,
|
||||
invitationDialogVisible,
|
||||
} = peopleStore.dialogStore;
|
||||
|
||||
return {
|
||||
changeEmail,
|
||||
changePassword,
|
||||
deleteSelfProfile,
|
||||
deleteProfileEver,
|
||||
data,
|
||||
closeDialogs,
|
||||
|
||||
employeeDialogVisible,
|
||||
guestDialogVisible,
|
||||
activeDialogVisible,
|
||||
disableDialogVisible,
|
||||
sendInviteDialogVisible,
|
||||
deleteDialogVisible,
|
||||
invitationDialogVisible,
|
||||
};
|
||||
})(observer(Dialogs));
|
||||
|
@ -0,0 +1,43 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import RowContainer from "@appserver/components/row-container";
|
||||
import SimpleUserRow from "./SimpleUserRow";
|
||||
import EmptyScreen from "../EmptyScreen";
|
||||
|
||||
const PeopleRowContainer = ({
|
||||
peopleList,
|
||||
sectionWidth,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if (viewAs !== "table" && viewAs !== "row") return;
|
||||
|
||||
if (sectionWidth < 1025) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
return peopleList.length > 0 ? (
|
||||
<RowContainer className="people-row-container" useReactWindow={false}>
|
||||
{peopleList.map((item) => (
|
||||
<SimpleUserRow key={item.id} item={item} sectionWidth={sectionWidth} />
|
||||
))}
|
||||
</RowContainer>
|
||||
) : (
|
||||
<EmptyScreen />
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ peopleStore }) => {
|
||||
const { usersStore, viewAs, setViewAs } = peopleStore;
|
||||
const { peopleList } = usersStore;
|
||||
|
||||
return {
|
||||
peopleList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
};
|
||||
})(observer(PeopleRowContainer));
|
@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
import Row from "@appserver/components/row";
|
||||
import UserContent from "./userContent";
|
||||
import { withRouter } from "react-router";
|
||||
import withContextOptions from "../../../../../HOCs/withContextOptions";
|
||||
import withContent from "../../../../../HOCs/withContent";
|
||||
|
||||
const SimpleUserRow = (props) => {
|
||||
const {
|
||||
item,
|
||||
sectionWidth,
|
||||
contextOptionsProps,
|
||||
checkedProps,
|
||||
onContentRowSelect,
|
||||
element,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Row
|
||||
key={item.id}
|
||||
data={item}
|
||||
element={element}
|
||||
onSelect={onContentRowSelect}
|
||||
{...checkedProps}
|
||||
{...contextOptionsProps}
|
||||
sectionWidth={sectionWidth}
|
||||
>
|
||||
<UserContent {...props} />
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(withContextOptions(withContent(SimpleUserRow)));
|
@ -0,0 +1,111 @@
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import styled from "styled-components";
|
||||
|
||||
import RowContent from "@appserver/components/row-content";
|
||||
import Link from "@appserver/components/link";
|
||||
import Text from "@appserver/components/text";
|
||||
import Box from "@appserver/components/box";
|
||||
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import SendClockIcon from "../../../../../../public/images/send.clock.react.svg";
|
||||
import CatalogSpamIcon from "../../../../../../public/images/catalog.spam.react.svg";
|
||||
|
||||
const StyledSendClockIcon = styled(SendClockIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: #3b72a7;
|
||||
}
|
||||
`;
|
||||
const StyledCatalogSpamIcon = styled(CatalogSpamIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: #3b72a7;
|
||||
}
|
||||
`;
|
||||
|
||||
const UserContent = ({
|
||||
item,
|
||||
sectionWidth,
|
||||
onPhoneClick,
|
||||
onEmailClick,
|
||||
onUserNameClick,
|
||||
groups,
|
||||
}) => {
|
||||
const { userName, displayName, title, mobilePhone, email, statusType } = item;
|
||||
|
||||
const nameColor = statusType === "pending" ? "#A3A9AE" : "#333333";
|
||||
const sideInfoColor = statusType === "pending" ? "#D0D5DA" : "#A3A9AE";
|
||||
|
||||
return (
|
||||
<RowContent
|
||||
sideColor={sideInfoColor}
|
||||
sectionWidth={sectionWidth}
|
||||
nameColor={nameColor}
|
||||
sideInfoColor={sideInfoColor}
|
||||
>
|
||||
<Link
|
||||
containerWidth="28%"
|
||||
type="page"
|
||||
href={`/products/people/view/${userName}`}
|
||||
title={displayName}
|
||||
fontWeight={600}
|
||||
onClick={onUserNameClick}
|
||||
fontSize="15px"
|
||||
color={nameColor}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{displayName}
|
||||
</Link>
|
||||
<>
|
||||
{statusType === "pending" && <StyledSendClockIcon size="small" />}
|
||||
{statusType === "disabled" && <StyledCatalogSpamIcon size="small" />}
|
||||
</>
|
||||
{title ? (
|
||||
<Text
|
||||
containerMinWidth="120px"
|
||||
containerWidth="20%"
|
||||
as="div"
|
||||
color={sideInfoColor}
|
||||
fontSize="12px"
|
||||
fontWeight={600}
|
||||
title={title}
|
||||
truncate={true}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
) : (
|
||||
<Box containerMinWidth="120px" containerWidth="20%"></Box>
|
||||
)}
|
||||
{groups}
|
||||
<Link
|
||||
containerMinWidth="60px"
|
||||
containerWidth="15%"
|
||||
type="page"
|
||||
title={mobilePhone}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
onClick={onPhoneClick}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{mobilePhone}
|
||||
</Link>
|
||||
<Link
|
||||
containerMinWidth="140px"
|
||||
containerWidth="17%"
|
||||
type="page"
|
||||
title={email}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
onClick={onEmailClick}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{email}
|
||||
</Link>
|
||||
</RowContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(UserContent);
|
@ -1,330 +0,0 @@
|
||||
import React from "react";
|
||||
import Row from "@appserver/components/row";
|
||||
import Avatar from "@appserver/components/avatar";
|
||||
import UserContent from "./userContent";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import toastr from "studio/toastr";
|
||||
import { AppServerConfig, EmployeeStatus } from "@appserver/common/constants";
|
||||
import { resendUserInvites } from "@appserver/common/api/people"; //TODO: Move to store action
|
||||
import { withRouter } from "react-router";
|
||||
import config from "../../../../../package.json";
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
|
||||
const SimpleUserRow = ({
|
||||
person,
|
||||
sectionWidth,
|
||||
checked,
|
||||
isAdmin,
|
||||
isMobile,
|
||||
selectUser,
|
||||
selectGroup,
|
||||
deselectUser,
|
||||
setChangeEmailDialogVisible,
|
||||
setChangePasswordDialogVisible,
|
||||
setDeleteProfileDialogVisible,
|
||||
setDeleteSelfProfileDialogVisible,
|
||||
setDialogData,
|
||||
closeDialogs,
|
||||
updateUserStatus,
|
||||
history,
|
||||
fetchProfile,
|
||||
}) => {
|
||||
const { t } = useTranslation(["Home", "Translations"]);
|
||||
const isRefetchPeople = true;
|
||||
|
||||
const {
|
||||
email,
|
||||
role,
|
||||
displayName,
|
||||
avatar,
|
||||
id,
|
||||
status,
|
||||
mobilePhone,
|
||||
options,
|
||||
userName,
|
||||
currentUserId,
|
||||
} = person;
|
||||
|
||||
const onContentRowSelect = (checked, user) =>
|
||||
checked ? selectUser(user) : deselectUser(user);
|
||||
|
||||
const onEmailSentClick = () => {
|
||||
window.open("mailto:" + email);
|
||||
};
|
||||
|
||||
const onSendMessageClick = () => {
|
||||
window.open(`sms:${mobilePhone}`);
|
||||
};
|
||||
const redirectToEdit = () => {
|
||||
history.push(
|
||||
combineUrl(AppServerConfig.proxyURL, config.homepage, `/edit/${userName}`)
|
||||
);
|
||||
};
|
||||
const onEditClick = () => {
|
||||
const timer = setTimeout(() => redirectToEdit(), 500);
|
||||
fetchProfile(userName).finally(() => {
|
||||
clearTimeout(timer);
|
||||
if (
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/edit/${userName}`
|
||||
) !== window.location.pathname
|
||||
)
|
||||
redirectToEdit();
|
||||
});
|
||||
};
|
||||
|
||||
const toggleChangeEmailDialog = () => {
|
||||
setDialogData({
|
||||
email,
|
||||
id,
|
||||
});
|
||||
|
||||
setChangeEmailDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleChangePasswordDialog = () => {
|
||||
setDialogData({
|
||||
email,
|
||||
});
|
||||
|
||||
setChangePasswordDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleDeleteSelfProfileDialog = (e) => {
|
||||
closeDialogs();
|
||||
|
||||
setDialogData({
|
||||
email,
|
||||
});
|
||||
|
||||
setDeleteSelfProfileDialogVisible(true);
|
||||
};
|
||||
|
||||
const toggleDeleteProfileEverDialog = (e) => {
|
||||
closeDialogs();
|
||||
|
||||
setDialogData({
|
||||
id,
|
||||
displayName,
|
||||
userName,
|
||||
});
|
||||
|
||||
setDeleteProfileDialogVisible(true);
|
||||
};
|
||||
|
||||
const onDisableClick = (e) => {
|
||||
//onLoading(true);
|
||||
updateUserStatus(EmployeeStatus.Disabled, [id], isRefetchPeople)
|
||||
.then(() => toastr.success(t("Translations:SuccessChangeUserStatus")))
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const onEnableClick = (e) => {
|
||||
//onLoading(true);
|
||||
updateUserStatus(EmployeeStatus.Active, [id], isRefetchPeople)
|
||||
.then(() => toastr.success(t("Translations:SuccessChangeUserStatus")))
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const onReassignDataClick = (e) => {
|
||||
history.push(
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/reassign/${userName}`
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const onDeletePersonalDataClick = (e) => {
|
||||
toastr.success(t("Translations:SuccessDeletePersonalData"));
|
||||
};
|
||||
|
||||
const onInviteAgainClick = () => {
|
||||
//onLoading(true);
|
||||
resendUserInvites([id])
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
<Trans
|
||||
i18nKey="MessageEmailActivationInstuctionsSentOnEmail"
|
||||
ns="Home"
|
||||
t={t}
|
||||
>
|
||||
The email activation instructions have been sent to the
|
||||
<strong>{{ email: email }}</strong> email address
|
||||
</Trans>
|
||||
)
|
||||
)
|
||||
.catch((error) => toastr.error(error));
|
||||
//.finally(() => onLoading(false));
|
||||
};
|
||||
|
||||
const getUserContextOptions = (options, id) => {
|
||||
const contextMenu = options.map((option) => {
|
||||
switch (option) {
|
||||
case "send-email":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblSendEmail"),
|
||||
"data-id": id,
|
||||
onClick: onEmailSentClick,
|
||||
};
|
||||
case "send-message":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblSendMessage"),
|
||||
"data-id": id,
|
||||
onClick: onSendMessageClick,
|
||||
};
|
||||
case "separator":
|
||||
return { key: option, isSeparator: true };
|
||||
case "edit":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Common:EditButton"),
|
||||
"data-id": id,
|
||||
onClick: onEditClick,
|
||||
};
|
||||
case "change-password":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:PasswordChangeButton"),
|
||||
"data-id": id,
|
||||
onClick: toggleChangePasswordDialog,
|
||||
};
|
||||
case "change-email":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:EmailChangeButton"),
|
||||
"data-id": id,
|
||||
onClick: toggleChangeEmailDialog,
|
||||
};
|
||||
case "delete-self-profile":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DeleteSelfProfile"),
|
||||
"data-id": id,
|
||||
onClick: toggleDeleteSelfProfileDialog,
|
||||
};
|
||||
case "disable":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DisableUserButton"),
|
||||
"data-id": id,
|
||||
onClick: onDisableClick,
|
||||
};
|
||||
case "enable":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:EnableUserButton"),
|
||||
"data-id": id,
|
||||
onClick: onEnableClick,
|
||||
};
|
||||
case "reassign-data":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:ReassignData"),
|
||||
"data-id": id,
|
||||
onClick: onReassignDataClick,
|
||||
};
|
||||
case "delete-personal-data":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:RemoveData"),
|
||||
"data-id": id,
|
||||
onClick: onDeletePersonalDataClick,
|
||||
};
|
||||
case "delete-profile":
|
||||
return {
|
||||
key: option,
|
||||
label: t("Translations:DeleteSelfProfile"),
|
||||
"data-id": id,
|
||||
onClick: toggleDeleteProfileEverDialog,
|
||||
};
|
||||
case "invite-again":
|
||||
return {
|
||||
key: option,
|
||||
label: t("LblInviteAgain"),
|
||||
"data-id": id,
|
||||
onClick: onInviteAgainClick,
|
||||
};
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
});
|
||||
|
||||
return contextMenu;
|
||||
};
|
||||
|
||||
const showContextMenu = options && options.length > 0;
|
||||
const checkedProps = isAdmin ? { checked } : {};
|
||||
|
||||
const element = (
|
||||
<Avatar size="min" role={role} userName={displayName} source={avatar} />
|
||||
);
|
||||
|
||||
const contextOptionsProps =
|
||||
(isAdmin && showContextMenu) || (showContextMenu && id === currentUserId)
|
||||
? {
|
||||
contextOptions: getUserContextOptions(options, id),
|
||||
}
|
||||
: {};
|
||||
|
||||
return (
|
||||
<Row
|
||||
key={id}
|
||||
status={status}
|
||||
data={person}
|
||||
element={element}
|
||||
onSelect={onContentRowSelect}
|
||||
{...checkedProps}
|
||||
{...contextOptionsProps}
|
||||
//needForUpdate={this.needForUpdate}
|
||||
sectionWidth={sectionWidth}
|
||||
>
|
||||
<UserContent
|
||||
isMobile={isMobile}
|
||||
//widthProp={widthProp}
|
||||
user={person}
|
||||
history={history}
|
||||
selectGroup={selectGroup}
|
||||
sectionWidth={sectionWidth}
|
||||
fetchProfile={fetchProfile}
|
||||
/>
|
||||
</Row>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
inject(({ auth, peopleStore }, { person }) => {
|
||||
return {
|
||||
isAdmin: auth.isAdmin,
|
||||
currentUserId: auth.userStore.user.id,
|
||||
checked: peopleStore.selectionStore.selection.some(
|
||||
(el) => el.id === person.id
|
||||
),
|
||||
selectUser: peopleStore.selectionStore.selectUser,
|
||||
deselectUser: peopleStore.selectionStore.deselectUser,
|
||||
selectGroup: peopleStore.selectedGroupStore.selectGroup,
|
||||
setChangeEmailDialogVisible:
|
||||
peopleStore.dialogStore.setChangeEmailDialogVisible,
|
||||
setChangePasswordDialogVisible:
|
||||
peopleStore.dialogStore.setChangePasswordDialogVisible,
|
||||
setDeleteSelfProfileDialogVisible:
|
||||
peopleStore.dialogStore.setDeleteSelfProfileDialogVisible,
|
||||
setDeleteProfileDialogVisible:
|
||||
peopleStore.dialogStore.setDeleteProfileDialogVisible,
|
||||
setDialogData: peopleStore.dialogStore.setDialogData,
|
||||
closeDialogs: peopleStore.dialogStore.closeDialogs,
|
||||
updateUserStatus: peopleStore.usersStore.updateUserStatus,
|
||||
fetchProfile: peopleStore.targetUserStore.getTargetUser,
|
||||
};
|
||||
})(observer(SimpleUserRow))
|
||||
);
|
@ -0,0 +1,43 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import TableContainer from "@appserver/components/table-container";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import TableRow from "./TableRow";
|
||||
import TableHeader from "./TableHeader";
|
||||
import TableBody from "@appserver/components/table-container/TableBody";
|
||||
import EmptyScreen from "../EmptyScreen";
|
||||
|
||||
const Table = ({ peopleList, sectionWidth, viewAs, setViewAs }) => {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (sectionWidth < 1025) {
|
||||
viewAs !== "row" && setViewAs("row");
|
||||
} else {
|
||||
viewAs !== "table" && setViewAs("table");
|
||||
}
|
||||
}, [sectionWidth]);
|
||||
|
||||
return peopleList.length > 0 ? (
|
||||
<TableContainer forwardedRef={ref}>
|
||||
<TableHeader sectionWidth={sectionWidth} containerRef={ref} />
|
||||
<TableBody>
|
||||
{peopleList.map((item) => (
|
||||
<TableRow key={item.id} item={item} />
|
||||
))}
|
||||
</TableBody>
|
||||
</TableContainer>
|
||||
) : (
|
||||
<EmptyScreen />
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ peopleStore }) => {
|
||||
const { usersStore, viewAs, setViewAs } = peopleStore;
|
||||
const { peopleList } = usersStore;
|
||||
|
||||
return {
|
||||
peopleList,
|
||||
viewAs,
|
||||
setViewAs,
|
||||
};
|
||||
})(observer(Table));
|
@ -0,0 +1,231 @@
|
||||
import React from "react";
|
||||
import TableHeader from "@appserver/components/table-container/TableHeader";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import DropDownItem from "@appserver/components/drop-down-item";
|
||||
|
||||
const TABLE_COLUMNS = "peopleTableColumns";
|
||||
|
||||
class PeopleTableHeader extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { t } = props;
|
||||
|
||||
const defaultColumns = [
|
||||
{
|
||||
key: "Name",
|
||||
title: t("Common:Name"),
|
||||
resizable: true,
|
||||
enable: true,
|
||||
default: true,
|
||||
sortBy: "AZ",
|
||||
active: true,
|
||||
minWidth: 180,
|
||||
onClick: this.onFilter,
|
||||
onIconClick: this.onIconClick,
|
||||
},
|
||||
{
|
||||
key: "Department",
|
||||
title: t("Common:Department"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Role",
|
||||
title: t("Common:Role"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Phone",
|
||||
title: t("Common:Phone"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
{
|
||||
key: "Mail",
|
||||
title: t("Common:Mail"),
|
||||
enable: true,
|
||||
resizable: true,
|
||||
onChange: this.onColumnChange,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = this.getColumns(defaultColumns);
|
||||
|
||||
this.state = { columns };
|
||||
}
|
||||
|
||||
getColumns = (defaultColumns) => {
|
||||
const storageColumns = localStorage.getItem(TABLE_COLUMNS);
|
||||
const columns = [];
|
||||
|
||||
if (storageColumns) {
|
||||
const splitColumns = storageColumns.split(",");
|
||||
|
||||
for (let col of defaultColumns) {
|
||||
const column = splitColumns.find((key) => key === col.key);
|
||||
column ? (col.enable = true) : (col.enable = false);
|
||||
|
||||
columns.push(col);
|
||||
}
|
||||
return columns;
|
||||
} else {
|
||||
return defaultColumns;
|
||||
}
|
||||
};
|
||||
|
||||
onColumnChange = (key, e) => {
|
||||
const { columns } = this.state;
|
||||
const columnIndex = columns.findIndex((c) => c.key === key);
|
||||
|
||||
if (columnIndex === -1) return;
|
||||
|
||||
columns[columnIndex].enable = !columns[columnIndex].enable;
|
||||
this.setState({ columns });
|
||||
|
||||
const tableColumns = columns.map((c) => c.enable && c.key);
|
||||
localStorage.setItem(TABLE_COLUMNS, tableColumns);
|
||||
};
|
||||
|
||||
onFilter = () => {
|
||||
const { filter, setIsLoading, fetchPeople } = this.props;
|
||||
const newFilter = filter.clone();
|
||||
|
||||
if (newFilter.sortBy === "lastname") {
|
||||
newFilter.sortBy = "firstname";
|
||||
} else {
|
||||
newFilter.sortBy = "lastname";
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
fetchPeople(newFilter).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
onIconClick = () => {
|
||||
const { filter, setIsLoading, fetchPeople } = this.props;
|
||||
const newFilter = filter.clone();
|
||||
|
||||
if (newFilter.sortOrder === "ascending") {
|
||||
newFilter.sortOrder = "descending";
|
||||
} else {
|
||||
newFilter.sortOrder = "ascending";
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
fetchPeople(newFilter).finally(() => setIsLoading(false));
|
||||
};
|
||||
|
||||
onChange = (checked) => {
|
||||
this.props.setSelected(checked ? "all" : "none");
|
||||
};
|
||||
|
||||
onSelect = (e) => {
|
||||
const key = e.currentTarget.dataset.key;
|
||||
this.props.setSelected(key);
|
||||
};
|
||||
|
||||
setSelected = (checked) => {
|
||||
const { isAdmin, setSelected } = this.props;
|
||||
if (isAdmin) {
|
||||
setSelected && setSelected(checked ? "all" : "none");
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { columns } = this.state;
|
||||
const {
|
||||
t,
|
||||
containerRef,
|
||||
isHeaderVisible,
|
||||
isHeaderChecked,
|
||||
isHeaderIndeterminate,
|
||||
getHeaderMenu,
|
||||
filter,
|
||||
sectionWidth,
|
||||
isAdmin,
|
||||
} = this.props;
|
||||
const { sortOrder } = filter;
|
||||
|
||||
const checkboxOptions = (
|
||||
<>
|
||||
<DropDownItem
|
||||
label={t("Common:Active")}
|
||||
data-key="active"
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("Translations:DisabledEmployeeStatus")}
|
||||
data-key="disabled"
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
<DropDownItem
|
||||
label={t("LblInvited")}
|
||||
data-key="invited"
|
||||
onClick={this.onSelect}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<TableHeader
|
||||
hasAccess={isAdmin}
|
||||
checkboxSize="48px"
|
||||
sorted={sortOrder === "descending"}
|
||||
setSelected={this.setSelected}
|
||||
containerRef={containerRef}
|
||||
columns={columns}
|
||||
columnStorageName="peopleColumnsSize"
|
||||
sectionWidth={sectionWidth}
|
||||
isHeaderVisible={isHeaderVisible}
|
||||
checkboxOptions={checkboxOptions}
|
||||
onChange={this.onChange}
|
||||
isChecked={isHeaderChecked}
|
||||
isIndeterminate={isHeaderIndeterminate}
|
||||
headerMenu={getHeaderMenu(t)}
|
||||
checkboxMargin="12px"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default inject(({ auth, peopleStore }) => {
|
||||
const {
|
||||
selectionStore,
|
||||
headerMenuStore,
|
||||
filterStore,
|
||||
usersStore,
|
||||
loadingStore,
|
||||
getHeaderMenu,
|
||||
} = peopleStore;
|
||||
|
||||
const { setSelected } = selectionStore;
|
||||
const {
|
||||
isHeaderVisible,
|
||||
isHeaderChecked,
|
||||
isHeaderIndeterminate,
|
||||
} = headerMenuStore;
|
||||
const { filter } = filterStore;
|
||||
const { getUsersList } = usersStore;
|
||||
const { setIsLoading } = loadingStore;
|
||||
|
||||
return {
|
||||
isAdmin: auth.isAdmin,
|
||||
setSelected,
|
||||
isHeaderVisible,
|
||||
filter,
|
||||
fetchPeople: getUsersList,
|
||||
setIsLoading,
|
||||
getHeaderMenu,
|
||||
isHeaderChecked,
|
||||
isHeaderIndeterminate,
|
||||
};
|
||||
})(
|
||||
withTranslation(["Home", "Common", "Translations"])(
|
||||
observer(PeopleTableHeader)
|
||||
)
|
||||
);
|
@ -0,0 +1,110 @@
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import TableRow from "@appserver/components/table-container/TableRow";
|
||||
import TableCell from "@appserver/components/table-container/TableCell";
|
||||
import withContextOptions from "../../../../../HOCs/withContextOptions";
|
||||
import withContent from "../../../../../HOCs/withContent";
|
||||
import Link from "@appserver/components/link";
|
||||
import Text from "@appserver/components/text";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledPeopleRow = styled(TableRow)`
|
||||
.table-container_cell {
|
||||
height: 46px;
|
||||
max-height: 46px;
|
||||
}
|
||||
|
||||
.table-container_row-checkbox-wrapper {
|
||||
padding-left: 4px;
|
||||
|
||||
.table-container_row-checkbox {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const PeopleTableRow = (props) => {
|
||||
const {
|
||||
item,
|
||||
contextOptionsProps,
|
||||
element,
|
||||
checkedProps,
|
||||
onContentRowSelect,
|
||||
groups,
|
||||
onEmailClick,
|
||||
onUserNameClick,
|
||||
isAdmin,
|
||||
} = props;
|
||||
const { displayName, email, role, statusType, userName } = item;
|
||||
|
||||
const nameColor = statusType === "pending" ? "#A3A9AE" : "#333333";
|
||||
const sideInfoColor = statusType === "pending" ? "#D0D5DA" : "#A3A9AE";
|
||||
|
||||
return (
|
||||
<StyledPeopleRow
|
||||
key={item.id}
|
||||
item={item}
|
||||
element={element}
|
||||
onContentSelect={onContentRowSelect}
|
||||
hasAccess={isAdmin}
|
||||
{...contextOptionsProps}
|
||||
{...checkedProps}
|
||||
>
|
||||
<TableCell>
|
||||
<Link
|
||||
type="page"
|
||||
title={displayName}
|
||||
fontWeight="600"
|
||||
fontSize="15px"
|
||||
color={nameColor}
|
||||
isTextOverflow
|
||||
href={`/products/people/view/${userName}`}
|
||||
onClick={onUserNameClick}
|
||||
>
|
||||
{displayName}
|
||||
</Link>
|
||||
</TableCell>
|
||||
<TableCell>{groups}</TableCell>
|
||||
<TableCell>
|
||||
<Text
|
||||
type="page"
|
||||
title={role}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
truncate
|
||||
>
|
||||
{role}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text
|
||||
style={{ display: "none" }} //TODO:
|
||||
type="page"
|
||||
title={role}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
truncate
|
||||
>
|
||||
Phone
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Link
|
||||
type="page"
|
||||
title={email}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
onClick={onEmailClick}
|
||||
isTextOverflow
|
||||
>
|
||||
{email}
|
||||
</Link>
|
||||
</TableCell>
|
||||
</StyledPeopleRow>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(withContextOptions(withContent(PeopleTableRow)));
|
@ -1,53 +1,39 @@
|
||||
import React from "react";
|
||||
import RowContainer from "@appserver/components/row-container";
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
import { withTranslation } from "react-i18next";
|
||||
//import toastr from "studio/toastr";
|
||||
|
||||
import EmptyScreen from "./EmptyScreen";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import SimpleUserRow from "./SimpleUserRow";
|
||||
import Dialogs from "./Dialogs";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
import withLoader from "../../../../HOCs/withLoader";
|
||||
import Loaders from "@appserver/common/components/Loaders";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import withLoader from "../../../../HOCs/withLoader";
|
||||
import PeopleRowContainer from "./RowView/PeopleRowContainer";
|
||||
import TableView from "./TableView/TableContainer";
|
||||
import { Consumer } from "@appserver/components/utils/context";
|
||||
|
||||
const SectionBodyContent = ({ peopleList, tReady }) => {
|
||||
return peopleList.length > 0 ? (
|
||||
<>
|
||||
class SectionBodyContent extends React.Component {
|
||||
render() {
|
||||
const { tReady, viewAs } = this.props;
|
||||
|
||||
return (
|
||||
<Consumer>
|
||||
{(context) => (
|
||||
<RowContainer
|
||||
className="people-row-container"
|
||||
useReactWindow={false}
|
||||
tReady={tReady}
|
||||
>
|
||||
{peopleList.map((person) => (
|
||||
<SimpleUserRow
|
||||
key={person.id}
|
||||
person={person}
|
||||
sectionWidth={context.sectionWidth}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
))}
|
||||
</RowContainer>
|
||||
)}
|
||||
{(context) =>
|
||||
viewAs === "table" ? (
|
||||
<TableView sectionWidth={context.sectionWidth} tReady={tReady} />
|
||||
) : (
|
||||
<PeopleRowContainer
|
||||
sectionWidth={context.sectionWidth}
|
||||
tReady={tReady}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Consumer>
|
||||
<Dialogs />
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreen />
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default inject(({ auth, peopleStore }) => ({
|
||||
isLoaded: auth.isLoaded,
|
||||
isRefresh: peopleStore.isRefresh,
|
||||
peopleList: peopleStore.usersStore.peopleList,
|
||||
isLoading: peopleStore.isLoading,
|
||||
}))(
|
||||
withTranslation("Home")(
|
||||
export default inject(({ peopleStore }) => {
|
||||
const { viewAs, setViewAs } = peopleStore;
|
||||
|
||||
return { viewAs, setViewAs };
|
||||
})(
|
||||
withTranslation(["Home", "Common", "Translations"])(
|
||||
withLoader(observer(SectionBodyContent))(
|
||||
<Loaders.Rows isRectangle={false} />
|
||||
)
|
||||
|
@ -1,199 +0,0 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import styled from "styled-components";
|
||||
|
||||
import RowContent from "@appserver/components/row-content";
|
||||
import Link from "@appserver/components/link";
|
||||
import LinkWithDropdown from "@appserver/components/link-with-dropdown";
|
||||
import Text from "@appserver/components/text";
|
||||
import Box from "@appserver/components/box";
|
||||
|
||||
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
|
||||
import SendClockIcon from "../../../../../public/images/send.clock.react.svg";
|
||||
import CatalogSpamIcon from "../../../../../public/images/catalog.spam.react.svg";
|
||||
import config from "../../../../../package.json";
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
import { AppServerConfig } from "@appserver/common/constants";
|
||||
|
||||
const StyledSendClockIcon = styled(SendClockIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: #3b72a7;
|
||||
}
|
||||
`;
|
||||
const StyledCatalogSpamIcon = styled(CatalogSpamIcon)`
|
||||
${commonIconsStyles}
|
||||
path {
|
||||
fill: #3b72a7;
|
||||
}
|
||||
`;
|
||||
const getFormattedGroups = (user, selectGroup) => {
|
||||
let temp = [];
|
||||
const groups = user.groups;
|
||||
const linkColor = user.statusType === "pending" ? "#D0D5DA" : "#A3A9AE";
|
||||
|
||||
if (!groups) temp.push({ key: 0, label: "" });
|
||||
|
||||
groups &&
|
||||
groups.map((group) =>
|
||||
temp.push({
|
||||
key: group.id,
|
||||
label: group.name,
|
||||
onClick: () => selectGroup(group.id),
|
||||
})
|
||||
);
|
||||
|
||||
if (temp.length <= 1) {
|
||||
return (
|
||||
<Link
|
||||
isTextOverflow={true}
|
||||
containerMinWidth="120px"
|
||||
containerWidth="15%"
|
||||
type="action"
|
||||
title={temp[0].label}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={linkColor}
|
||||
onClick={temp[0].onClick}
|
||||
>
|
||||
{temp[0].label}
|
||||
</Link>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LinkWithDropdown
|
||||
isTextOverflow={true}
|
||||
containerMinWidth="120px"
|
||||
containerWidth="15%"
|
||||
title={temp[0].label}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={linkColor}
|
||||
data={temp}
|
||||
>
|
||||
{temp[0].label}
|
||||
</LinkWithDropdown>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const UserContent = ({
|
||||
user,
|
||||
history,
|
||||
selectGroup,
|
||||
//widthProp,
|
||||
isMobile,
|
||||
sectionWidth,
|
||||
fetchProfile,
|
||||
}) => {
|
||||
const { userName, displayName, title, mobilePhone, email, statusType } = user;
|
||||
const groups = getFormattedGroups(user, selectGroup);
|
||||
|
||||
const redirectToProfile = () => {
|
||||
history.push(
|
||||
combineUrl(AppServerConfig.proxyURL, config.homepage, `/view/${userName}`)
|
||||
);
|
||||
};
|
||||
|
||||
const onUserNameClick = useCallback(
|
||||
(e) => {
|
||||
const timer = setTimeout(() => redirectToProfile(), 500);
|
||||
e.preventDefault();
|
||||
fetchProfile(userName).finally(() => {
|
||||
clearTimeout(timer);
|
||||
if (
|
||||
combineUrl(
|
||||
AppServerConfig.proxyURL,
|
||||
config.homepage,
|
||||
`/view/${userName}`
|
||||
) !== window.location.pathname
|
||||
)
|
||||
redirectToProfile();
|
||||
});
|
||||
},
|
||||
[history, userName]
|
||||
);
|
||||
|
||||
const onPhoneClick = useCallback(() => window.open(`sms:${mobilePhone}`), [
|
||||
mobilePhone,
|
||||
]);
|
||||
|
||||
const onEmailClick = useCallback(() => window.open(`mailto:${email}`), [
|
||||
email,
|
||||
]);
|
||||
|
||||
const nameColor = statusType === "pending" ? "#A3A9AE" : "#333333";
|
||||
const sideInfoColor = statusType === "pending" ? "#D0D5DA" : "#A3A9AE";
|
||||
|
||||
return (
|
||||
<RowContent
|
||||
//widthProp={widthProp}
|
||||
isMobile={isMobile}
|
||||
sideColor={sideInfoColor}
|
||||
sectionWidth={sectionWidth}
|
||||
>
|
||||
<Link
|
||||
containerWidth="28%"
|
||||
type="page"
|
||||
href={`/products/people/view/${userName}`}
|
||||
title={displayName}
|
||||
fontWeight={600}
|
||||
onClick={onUserNameClick}
|
||||
fontSize="15px"
|
||||
color={nameColor}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{displayName}
|
||||
</Link>
|
||||
<>
|
||||
{statusType === "pending" && <StyledSendClockIcon size="small" />}
|
||||
{statusType === "disabled" && <StyledCatalogSpamIcon size="small" />}
|
||||
</>
|
||||
{title ? (
|
||||
<Text
|
||||
containerMinWidth="120px"
|
||||
containerWidth="20%"
|
||||
as="div"
|
||||
color={sideInfoColor}
|
||||
fontSize="12px"
|
||||
fontWeight={600}
|
||||
title={title}
|
||||
truncate={true}
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
) : (
|
||||
<Box containerMinWidth="120px" containerWidth="20%"></Box>
|
||||
)}
|
||||
{groups}
|
||||
<Link
|
||||
containerMinWidth="60px"
|
||||
containerWidth="15%"
|
||||
type="page"
|
||||
title={mobilePhone}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
onClick={onPhoneClick}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{mobilePhone}
|
||||
</Link>
|
||||
<Link
|
||||
containerMinWidth="140px"
|
||||
containerWidth="17%"
|
||||
type="page"
|
||||
title={email}
|
||||
fontSize="12px"
|
||||
fontWeight={400}
|
||||
color={sideInfoColor}
|
||||
onClick={onEmailClick}
|
||||
isTextOverflow={true}
|
||||
>
|
||||
{email}
|
||||
</Link>
|
||||
</RowContent>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(UserContent);
|
@ -227,7 +227,7 @@ class SectionFilterContent extends React.Component {
|
||||
|
||||
render() {
|
||||
const selectedFilterData = this.getSelectedFilterData();
|
||||
const { t, isLoaded, sectionWidth, tReady } = this.props;
|
||||
const { t, isLoaded, sectionWidth, tReady, viewAs } = this.props;
|
||||
|
||||
return isLoaded && tReady ? (
|
||||
<FilterInput
|
||||
@ -241,6 +241,8 @@ class SectionFilterContent extends React.Component {
|
||||
placeholder={t("Common:Search")}
|
||||
contextMenuHeader={t("Common:AddFilter")}
|
||||
isMobile={isMobileOnly}
|
||||
viewAs={viewAs}
|
||||
viewSelectorVisible={false}
|
||||
/>
|
||||
) : (
|
||||
<Loaders.Filter />
|
||||
@ -250,7 +252,13 @@ class SectionFilterContent extends React.Component {
|
||||
|
||||
export default withRouter(
|
||||
inject(({ auth, peopleStore }) => {
|
||||
const { loadingStore, filterStore, usersStore, groupsStore } = peopleStore;
|
||||
const {
|
||||
loadingStore,
|
||||
filterStore,
|
||||
usersStore,
|
||||
groupsStore,
|
||||
viewAs,
|
||||
} = peopleStore;
|
||||
const { settingsStore, userStore, isLoaded, isAdmin } = auth;
|
||||
const { customNames } = settingsStore;
|
||||
const { user } = userStore;
|
||||
@ -268,6 +276,7 @@ export default withRouter(
|
||||
fetchPeople,
|
||||
filter,
|
||||
setIsLoading,
|
||||
viewAs,
|
||||
};
|
||||
})(
|
||||
observer(
|
||||
|
@ -12,19 +12,8 @@ import Headline from "@appserver/common/components/Headline";
|
||||
import toastr from "studio/toastr";
|
||||
import Loaders from "@appserver/common/components/Loaders";
|
||||
import withLoader from "../../../../HOCs/withLoader";
|
||||
import {
|
||||
EmployeeType,
|
||||
EmployeeStatus,
|
||||
AppServerConfig,
|
||||
} from "@appserver/common/constants";
|
||||
import { AppServerConfig } from "@appserver/common/constants";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import {
|
||||
InviteDialog,
|
||||
DeleteUsersDialog,
|
||||
SendInviteDialog,
|
||||
ChangeUserStatusDialog,
|
||||
ChangeUserTypeDialog,
|
||||
} from "../../../../components/dialogs";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import config from "../../../../../package.json";
|
||||
@ -109,43 +98,24 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
const SectionHeaderContent = (props) => {
|
||||
const [invitationDialogVisible, setInvitationDialogVisible] = useState(false);
|
||||
const [deleteDialogVisible, setDeleteDialogVisible] = useState(false);
|
||||
const [sendInviteDialogVisible, setSendInviteDialogVisible] = useState(false);
|
||||
const [disableDialogVisible, setDisableDialogVisible] = useState(false);
|
||||
const [activeDialogVisible, setActiveDialogVisible] = useState(false);
|
||||
const [guestDialogVisible, setGuestDialogVisible] = useState(false);
|
||||
const [employeeDialogVisible, setEmployeeDialogVisible] = useState(false);
|
||||
|
||||
const {
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
//onCheck,
|
||||
//onSelect,
|
||||
//clearSelection,
|
||||
group,
|
||||
isAdmin,
|
||||
t,
|
||||
tReady,
|
||||
history,
|
||||
customNames,
|
||||
homepage,
|
||||
deleteGroup,
|
||||
selection,
|
||||
hasAnybodySelected,
|
||||
hasUsersToMakeEmployees,
|
||||
hasUsersToMakeGuests,
|
||||
hasUsersToActivate,
|
||||
hasUsersToDisable,
|
||||
hasUsersToInvite,
|
||||
hasUsersToRemove,
|
||||
isLoaded,
|
||||
isTabletView,
|
||||
//selectAll,
|
||||
setSelected,
|
||||
resetFilter,
|
||||
//selectByStatus,
|
||||
getHeaderMenu,
|
||||
setInvitationDialogVisible,
|
||||
viewAs,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
@ -164,47 +134,7 @@ const SectionHeaderContent = (props) => {
|
||||
setSelected,
|
||||
]);
|
||||
|
||||
const onClose = () => {
|
||||
setSelected("none");
|
||||
};
|
||||
|
||||
const toggleEmployeeDialog = useCallback(
|
||||
() => setEmployeeDialogVisible(!employeeDialogVisible),
|
||||
[employeeDialogVisible]
|
||||
);
|
||||
|
||||
const toggleGuestDialog = useCallback(
|
||||
() => setGuestDialogVisible(!guestDialogVisible),
|
||||
[guestDialogVisible]
|
||||
);
|
||||
|
||||
const toggleActiveDialog = useCallback(
|
||||
() => setActiveDialogVisible(!activeDialogVisible),
|
||||
[activeDialogVisible]
|
||||
);
|
||||
|
||||
const toggleDisableDialog = useCallback(
|
||||
() => setDisableDialogVisible(!disableDialogVisible),
|
||||
[disableDialogVisible]
|
||||
);
|
||||
|
||||
const toggleSendInviteDialog = useCallback(
|
||||
() => setSendInviteDialogVisible(!sendInviteDialogVisible),
|
||||
[sendInviteDialogVisible]
|
||||
);
|
||||
|
||||
const toggleDeleteDialog = useCallback(
|
||||
() => setDeleteDialogVisible(!deleteDialogVisible),
|
||||
[deleteDialogVisible]
|
||||
);
|
||||
|
||||
const onSendEmail = useCallback(() => {
|
||||
let str = "";
|
||||
for (let item of selection) {
|
||||
str += `${item.email},`;
|
||||
}
|
||||
window.open(`mailto: ${str}`, "_self");
|
||||
}, [selection]);
|
||||
const onClose = () => setSelected("none");
|
||||
|
||||
const onSelectorSelect = useCallback(
|
||||
(item) => {
|
||||
@ -213,91 +143,42 @@ const SectionHeaderContent = (props) => {
|
||||
[onSelect]
|
||||
);
|
||||
|
||||
const menuItems = useMemo(
|
||||
let menuItems = useMemo(
|
||||
() => [
|
||||
{
|
||||
label: t("Common:Select"),
|
||||
isDropdown: true,
|
||||
isSeparator: true,
|
||||
isSelect: true,
|
||||
fontWeight: "bold",
|
||||
children: [
|
||||
<DropDownItem
|
||||
key="active"
|
||||
label={t("Common:Active")}
|
||||
data-index={0}
|
||||
/>,
|
||||
<DropDownItem
|
||||
key="disabled"
|
||||
label={t("Translations:DisabledEmployeeStatus")}
|
||||
data-index={1}
|
||||
/>,
|
||||
<DropDownItem key="invited" label={t("LblInvited")} data-index={2} />,
|
||||
],
|
||||
onSelect: onSelectorSelect,
|
||||
},
|
||||
{
|
||||
label: t("ChangeToUser", {
|
||||
userCaption,
|
||||
}),
|
||||
disabled: !hasUsersToMakeEmployees,
|
||||
onClick: toggleEmployeeDialog,
|
||||
},
|
||||
{
|
||||
label: t("ChangeToGuest", {
|
||||
guestCaption,
|
||||
}),
|
||||
disabled: !hasUsersToMakeGuests,
|
||||
onClick: toggleGuestDialog,
|
||||
},
|
||||
{
|
||||
label: t("LblSetActive"),
|
||||
disabled: !hasUsersToActivate,
|
||||
onClick: toggleActiveDialog,
|
||||
},
|
||||
{
|
||||
label: t("LblSetDisabled"),
|
||||
disabled: !hasUsersToDisable,
|
||||
onClick: toggleDisableDialog,
|
||||
},
|
||||
{
|
||||
label: t("LblInviteAgain"),
|
||||
disabled: !hasUsersToInvite,
|
||||
onClick: toggleSendInviteDialog,
|
||||
},
|
||||
{
|
||||
label: t("LblSendEmail"),
|
||||
disabled: !hasAnybodySelected,
|
||||
onClick: onSendEmail,
|
||||
},
|
||||
{
|
||||
label: t("Common:Delete"),
|
||||
disabled: !hasUsersToRemove,
|
||||
onClick: toggleDeleteDialog,
|
||||
},
|
||||
...[
|
||||
{
|
||||
label: t("Common:Select"),
|
||||
isDropdown: true,
|
||||
isSeparator: true,
|
||||
isSelect: true,
|
||||
fontWeight: "bold",
|
||||
children: [
|
||||
<DropDownItem
|
||||
key="active"
|
||||
label={t("Common:Active")}
|
||||
data-index={0}
|
||||
/>,
|
||||
<DropDownItem
|
||||
key="disabled"
|
||||
label={t("Translations:DisabledEmployeeStatus")}
|
||||
data-index={1}
|
||||
/>,
|
||||
<DropDownItem
|
||||
key="invited"
|
||||
label={t("LblInvited")}
|
||||
data-index={2}
|
||||
/>,
|
||||
],
|
||||
onSelect: onSelectorSelect,
|
||||
},
|
||||
],
|
||||
],
|
||||
[
|
||||
t,
|
||||
userCaption,
|
||||
guestCaption,
|
||||
onSelectorSelect,
|
||||
toggleEmployeeDialog,
|
||||
toggleGuestDialog,
|
||||
toggleActiveDialog,
|
||||
toggleDisableDialog,
|
||||
toggleSendInviteDialog,
|
||||
onSendEmail,
|
||||
toggleDeleteDialog,
|
||||
hasAnybodySelected,
|
||||
hasUsersToMakeEmployees,
|
||||
hasUsersToMakeGuests,
|
||||
hasUsersToActivate,
|
||||
hasUsersToDisable,
|
||||
hasUsersToInvite,
|
||||
hasUsersToRemove,
|
||||
]
|
||||
[t, onSelectorSelect]
|
||||
);
|
||||
|
||||
const headerMenu = getHeaderMenu(t);
|
||||
menuItems = [...menuItems, ...headerMenu];
|
||||
|
||||
const onEditGroup = useCallback(
|
||||
() =>
|
||||
history.push(
|
||||
@ -349,10 +230,7 @@ const SectionHeaderContent = (props) => {
|
||||
);
|
||||
}, [history, homepage]);
|
||||
|
||||
const onInvitationDialogClick = useCallback(
|
||||
() => setInvitationDialogVisible(!invitationDialogVisible),
|
||||
[invitationDialogVisible]
|
||||
);
|
||||
const onInvitationDialogClick = () => setInvitationDialogVisible(true);
|
||||
|
||||
const getContextOptionsPlus = useCallback(() => {
|
||||
return [
|
||||
@ -391,7 +269,8 @@ const SectionHeaderContent = (props) => {
|
||||
goToEmployeeCreate,
|
||||
goToGuestCreate,
|
||||
goToGroupCreate,
|
||||
onInvitationDialogClick /* , onSentInviteAgain */,
|
||||
/* , onSentInviteAgain */
|
||||
,
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -403,51 +282,7 @@ const SectionHeaderContent = (props) => {
|
||||
width={context.sectionWidth}
|
||||
isTabletView={isTabletView}
|
||||
>
|
||||
{employeeDialogVisible && (
|
||||
<ChangeUserTypeDialog
|
||||
visible={employeeDialogVisible}
|
||||
onClose={toggleEmployeeDialog}
|
||||
userType={EmployeeType.User}
|
||||
/>
|
||||
)}
|
||||
|
||||
{guestDialogVisible && (
|
||||
<ChangeUserTypeDialog
|
||||
visible={guestDialogVisible}
|
||||
onClose={toggleGuestDialog}
|
||||
userType={EmployeeType.Guest}
|
||||
/>
|
||||
)}
|
||||
{activeDialogVisible && (
|
||||
<ChangeUserStatusDialog
|
||||
visible={activeDialogVisible}
|
||||
onClose={toggleActiveDialog}
|
||||
userStatus={EmployeeStatus.Active}
|
||||
/>
|
||||
)}
|
||||
{disableDialogVisible && (
|
||||
<ChangeUserStatusDialog
|
||||
visible={disableDialogVisible}
|
||||
onClose={toggleDisableDialog}
|
||||
userStatus={EmployeeStatus.Disabled}
|
||||
/>
|
||||
)}
|
||||
|
||||
{sendInviteDialogVisible && (
|
||||
<SendInviteDialog
|
||||
visible={sendInviteDialogVisible}
|
||||
onClose={toggleSendInviteDialog}
|
||||
/>
|
||||
)}
|
||||
|
||||
{deleteDialogVisible && (
|
||||
<DeleteUsersDialog
|
||||
visible={deleteDialogVisible}
|
||||
onClose={toggleDeleteDialog}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isHeaderVisible ? (
|
||||
{isHeaderVisible && viewAs !== "table" ? (
|
||||
<div className="group-button-menu-container">
|
||||
<GroupButtonsMenu
|
||||
checked={isHeaderChecked}
|
||||
@ -508,13 +343,6 @@ const SectionHeaderContent = (props) => {
|
||||
getData={getContextOptionsPlus}
|
||||
isDisabled={false}
|
||||
/>
|
||||
{invitationDialogVisible && (
|
||||
<InviteDialog
|
||||
visible={invitationDialogVisible}
|
||||
onClose={onInvitationDialogClick}
|
||||
onCloseButton={onInvitationDialogClick}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
@ -528,34 +356,62 @@ const SectionHeaderContent = (props) => {
|
||||
};
|
||||
|
||||
export default withRouter(
|
||||
inject(({ auth, peopleStore }) => ({
|
||||
resetFilter: peopleStore.resetFilter,
|
||||
customNames: auth.settingsStore.customNames,
|
||||
homepage: config.homepage,
|
||||
isLoaded: auth.isLoaded,
|
||||
isAdmin: auth.isAdmin,
|
||||
fetchPeople: peopleStore.usersStore.getUsersList,
|
||||
selection: peopleStore.selectionStore.selection,
|
||||
setSelected: peopleStore.selectionStore.setSelected,
|
||||
selectByStatus: peopleStore.selectionStore.selectByStatus,
|
||||
isHeaderVisible: peopleStore.headerMenuStore.isHeaderVisible,
|
||||
isHeaderIndeterminate: peopleStore.headerMenuStore.isHeaderIndeterminate,
|
||||
isHeaderChecked: peopleStore.headerMenuStore.isHeaderChecked,
|
||||
clearSelection: peopleStore.selectionStore.clearSelection,
|
||||
selectAll: peopleStore.selectionStore.selectAll,
|
||||
hasAnybodySelected: peopleStore.selectionStore.hasAnybodySelected,
|
||||
hasUsersToMakeEmployees: peopleStore.selectionStore.hasUsersToMakeEmployees,
|
||||
hasUsersToMakeGuests: peopleStore.selectionStore.hasUsersToMakeGuests,
|
||||
hasUsersToActivate: peopleStore.selectionStore.hasUsersToActivate,
|
||||
hasUsersToDisable: peopleStore.selectionStore.hasUsersToDisable,
|
||||
hasUsersToInvite: peopleStore.selectionStore.hasUsersToInvite,
|
||||
hasUsersToRemove: peopleStore.selectionStore.hasUsersToRemove,
|
||||
deleteGroup: peopleStore.groupsStore.deleteGroup,
|
||||
removeUser: peopleStore.usersStore.removeUser,
|
||||
updateUserStatus: peopleStore.usersStore.updateUserStatus,
|
||||
group: peopleStore.selectedGroupStore.group,
|
||||
isTabletView: auth.settingsStore.isTabletView,
|
||||
}))(
|
||||
inject(({ auth, peopleStore }) => {
|
||||
const { settingsStore, isLoaded, isAdmin } = auth;
|
||||
const { customNames, isTabletView } = settingsStore;
|
||||
|
||||
const {
|
||||
resetFilter,
|
||||
usersStore,
|
||||
selectionStore,
|
||||
headerMenuStore,
|
||||
groupsStore,
|
||||
selectedGroupStore,
|
||||
getHeaderMenu,
|
||||
dialogStore,
|
||||
viewAs,
|
||||
} = peopleStore;
|
||||
const { getUsersList, removeUser, updateUserStatus } = usersStore;
|
||||
const {
|
||||
setSelected,
|
||||
selectByStatus,
|
||||
clearSelection,
|
||||
selectAll,
|
||||
} = selectionStore;
|
||||
|
||||
const {
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
} = headerMenuStore;
|
||||
const { deleteGroup } = groupsStore;
|
||||
const { group } = selectedGroupStore;
|
||||
const { setInvitationDialogVisible } = dialogStore;
|
||||
|
||||
return {
|
||||
resetFilter,
|
||||
customNames,
|
||||
homepage: config.homepage,
|
||||
isLoaded,
|
||||
isAdmin,
|
||||
fetchPeople: getUsersList,
|
||||
setSelected,
|
||||
selectByStatus,
|
||||
isHeaderVisible,
|
||||
isHeaderIndeterminate,
|
||||
isHeaderChecked,
|
||||
clearSelection,
|
||||
selectAll,
|
||||
deleteGroup,
|
||||
removeUser,
|
||||
updateUserStatus,
|
||||
group,
|
||||
isTabletView,
|
||||
getHeaderMenu,
|
||||
setInvitationDialogVisible,
|
||||
viewAs,
|
||||
};
|
||||
})(
|
||||
withTranslation(["Home", "Common", "Translations"])(
|
||||
withLoader(observer(SectionHeaderContent))(<Loaders.SectionHeader />)
|
||||
)
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
} from "./Section";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { isMobile } from "react-device-detect";
|
||||
import Dialogs from "./Section/Body/Dialogs"; //TODO: Move dialogs to another folder
|
||||
|
||||
const Home = ({
|
||||
isLoading,
|
||||
@ -56,39 +57,43 @@ const Home = ({
|
||||
}, [isLoading]);
|
||||
|
||||
return (
|
||||
<PageLayout
|
||||
withBodyScroll
|
||||
withBodyAutoFocus={!isMobile}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<PageLayout.ArticleHeader>
|
||||
<ArticleHeaderContent />
|
||||
</PageLayout.ArticleHeader>
|
||||
<>
|
||||
<PageLayout
|
||||
withBodyScroll
|
||||
withBodyAutoFocus={!isMobile}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<PageLayout.ArticleHeader>
|
||||
<ArticleHeaderContent />
|
||||
</PageLayout.ArticleHeader>
|
||||
|
||||
<PageLayout.ArticleMainButton>
|
||||
<ArticleMainButtonContent />
|
||||
</PageLayout.ArticleMainButton>
|
||||
<PageLayout.ArticleMainButton>
|
||||
<ArticleMainButtonContent />
|
||||
</PageLayout.ArticleMainButton>
|
||||
|
||||
<PageLayout.ArticleBody>
|
||||
<ArticleBodyContent />
|
||||
</PageLayout.ArticleBody>
|
||||
<PageLayout.ArticleBody>
|
||||
<ArticleBodyContent />
|
||||
</PageLayout.ArticleBody>
|
||||
|
||||
<PageLayout.SectionHeader>
|
||||
<SectionHeaderContent />
|
||||
</PageLayout.SectionHeader>
|
||||
<PageLayout.SectionHeader>
|
||||
<SectionHeaderContent />
|
||||
</PageLayout.SectionHeader>
|
||||
|
||||
<PageLayout.SectionFilter>
|
||||
<SectionFilterContent />
|
||||
</PageLayout.SectionFilter>
|
||||
<PageLayout.SectionFilter>
|
||||
<SectionFilterContent />
|
||||
</PageLayout.SectionFilter>
|
||||
|
||||
<PageLayout.SectionBody>
|
||||
<SectionBodyContent />
|
||||
</PageLayout.SectionBody>
|
||||
<PageLayout.SectionBody>
|
||||
<SectionBodyContent />
|
||||
</PageLayout.SectionBody>
|
||||
|
||||
<PageLayout.SectionPaging>
|
||||
<SectionPagingContent />
|
||||
</PageLayout.SectionPaging>
|
||||
</PageLayout>
|
||||
<PageLayout.SectionPaging>
|
||||
<SectionPagingContent />
|
||||
</PageLayout.SectionPaging>
|
||||
</PageLayout>
|
||||
|
||||
<Dialogs />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -745,7 +745,7 @@ class UpdateUserForm extends React.Component {
|
||||
/>
|
||||
{/*TODO: uncomment this after added phone form */}
|
||||
{/* <TextChangeField
|
||||
labelText={`${t("Phone")}:`}
|
||||
labelText={`${t("Common:Phone")}:`}
|
||||
inputName="phone"
|
||||
inputValue={profile.mobilePhone}
|
||||
buttonText={t("ChangeButton")}
|
||||
|
@ -7,6 +7,13 @@ class DialogStore {
|
||||
deleteProfileEver = false;
|
||||
data = {};
|
||||
|
||||
employeeDialogVisible = false;
|
||||
guestDialogVisible = false;
|
||||
activeDialogVisible = false;
|
||||
disableDialogVisible = false;
|
||||
sendInviteDialogVisible = false;
|
||||
invitationDialogVisible = false;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
@ -31,12 +38,48 @@ class DialogStore {
|
||||
this.data = data;
|
||||
};
|
||||
|
||||
setEmployeeDialogVisible = (visible) => {
|
||||
this.employeeDialogVisible = visible;
|
||||
};
|
||||
|
||||
setGuestDialogVisible = (visible) => {
|
||||
this.guestDialogVisible = visible;
|
||||
};
|
||||
|
||||
setActiveDialogVisible = (visible) => {
|
||||
this.activeDialogVisible = visible;
|
||||
};
|
||||
|
||||
setDisableDialogVisible = (visible) => {
|
||||
this.disableDialogVisible = visible;
|
||||
};
|
||||
|
||||
setSendInviteDialogVisible = (visible) => {
|
||||
this.sendInviteDialogVisible = visible;
|
||||
};
|
||||
|
||||
setDeleteDialogVisible = (visible) => {
|
||||
this.deleteDialogVisible = visible;
|
||||
};
|
||||
|
||||
setInvitationDialogVisible = (visible) => {
|
||||
this.invitationDialogVisible = visible;
|
||||
};
|
||||
|
||||
closeDialogs = () => {
|
||||
this.setChangeEmailDialogVisible(false);
|
||||
this.setChangePasswordDialogVisible(false);
|
||||
this.setDeleteSelfProfileDialogVisible(false);
|
||||
this.setDeleteProfileDialogVisible(false);
|
||||
this.setDialogData({});
|
||||
|
||||
this.setEmployeeDialogVisible(false);
|
||||
this.setGuestDialogVisible(false);
|
||||
this.setActiveDialogVisible(false);
|
||||
this.setDisableDialogVisible(false);
|
||||
this.setSendInviteDialogVisible(false);
|
||||
this.setDeleteDialogVisible(false);
|
||||
this.setInvitationDialogVisible(false);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { action, computed, makeObservable, observable } from "mobx";
|
||||
import { makeAutoObservable } from "mobx";
|
||||
import GroupsStore from "./GroupsStore";
|
||||
import UsersStore from "./UsersStore";
|
||||
import config from "../../package.json";
|
||||
@ -29,6 +29,7 @@ class PeopleStore {
|
||||
dialogStore = null;
|
||||
loadingStore = null;
|
||||
isInit = false;
|
||||
viewAs = "table";
|
||||
|
||||
constructor() {
|
||||
this.groupsStore = new GroupsStore(this);
|
||||
@ -44,11 +45,7 @@ class PeopleStore {
|
||||
this.dialogStore = new DialogStore();
|
||||
this.loadingStore = new LoadingStore();
|
||||
|
||||
makeObservable(this, {
|
||||
init: action,
|
||||
isPeoplesAdmin: computed,
|
||||
resetFilter: action,
|
||||
});
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
get isPeoplesAdmin() {
|
||||
@ -84,6 +81,82 @@ class PeopleStore {
|
||||
|
||||
return getUsersList(newFilter);
|
||||
};
|
||||
|
||||
getHeaderMenu = (t) => {
|
||||
const { userCaption, guestCaption } = authStore.settingsStore.customNames;
|
||||
const {
|
||||
hasUsersToMakeEmployees,
|
||||
hasUsersToMakeGuests,
|
||||
hasUsersToActivate,
|
||||
hasUsersToDisable,
|
||||
hasUsersToInvite,
|
||||
hasAnybodySelected,
|
||||
hasUsersToRemove,
|
||||
selection,
|
||||
} = this.selectionStore;
|
||||
const {
|
||||
setEmployeeDialogVisible,
|
||||
setGuestDialogVisible,
|
||||
setActiveDialogVisible,
|
||||
setDisableDialogVisible,
|
||||
setSendInviteDialogVisible,
|
||||
setDeleteDialogVisible,
|
||||
} = this.dialogStore;
|
||||
|
||||
const headerMenu = [
|
||||
{
|
||||
label: t("ChangeToUser", {
|
||||
userCaption,
|
||||
}),
|
||||
disabled: !hasUsersToMakeEmployees,
|
||||
onClick: () => setEmployeeDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("ChangeToGuest", {
|
||||
guestCaption,
|
||||
}),
|
||||
disabled: !hasUsersToMakeGuests,
|
||||
onClick: () => setGuestDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("LblSetActive"),
|
||||
disabled: !hasUsersToActivate,
|
||||
onClick: () => setActiveDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("LblSetDisabled"),
|
||||
disabled: !hasUsersToDisable,
|
||||
onClick: () => setDisableDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("LblInviteAgain"),
|
||||
disabled: !hasUsersToInvite,
|
||||
onClick: () => setSendInviteDialogVisible(true),
|
||||
},
|
||||
{
|
||||
label: t("LblSendEmail"),
|
||||
disabled: !hasAnybodySelected,
|
||||
onClick: () => {
|
||||
let str = "";
|
||||
for (let item of selection) {
|
||||
str += `${item.email},`;
|
||||
}
|
||||
window.open(`mailto: ${str}`, "_self");
|
||||
},
|
||||
},
|
||||
{
|
||||
label: t("Common:Delete"),
|
||||
disabled: !hasUsersToRemove,
|
||||
onClick: () => setDeleteDialogVisible(true),
|
||||
},
|
||||
];
|
||||
|
||||
return headerMenu;
|
||||
};
|
||||
|
||||
setViewAs = (viewAs) => {
|
||||
this.viewAs = viewAs;
|
||||
};
|
||||
}
|
||||
|
||||
export default PeopleStore;
|
||||
|
@ -44,8 +44,7 @@ class SelectionStore {
|
||||
};
|
||||
|
||||
selectUser = (user) => {
|
||||
const u = user;
|
||||
return this.selection.push(u);
|
||||
return this.selection.push(user);
|
||||
};
|
||||
|
||||
deselectUser = (user) => {
|
||||
|
3
public/images/folder arrow.react.svg
Normal file
3
public/images/folder arrow.react.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.646447 2.14645C0.841709 1.95118 1.15829 1.95118 1.35355 2.14645L4 4.79289L6.64645 2.14645C6.84171 1.95118 7.15829 1.95118 7.35355 2.14645C7.54882 2.34171 7.54882 2.65829 7.35355 2.85355L4.35355 5.85355C4.15829 6.04882 3.84171 6.04882 3.64645 5.85355L0.646447 2.85355C0.451184 2.65829 0.451184 2.34171 0.646447 2.14645Z" fill="#657077"/>
|
||||
</svg>
|
After Width: | Height: | Size: 488 B |
@ -81,5 +81,7 @@
|
||||
"Version": "Version",
|
||||
"View": "Anzeigen",
|
||||
"ViewWeb": "Web-Version öffnen",
|
||||
"Warning": "Warnung"
|
||||
"Warning": "Warnung",
|
||||
"Name": "Name",
|
||||
"Phone": "Telefon"
|
||||
}
|
||||
|
@ -95,5 +95,14 @@
|
||||
"Warning": "Warning",
|
||||
"ResetApplication": "Reset application",
|
||||
"NewVersionAvailable": "A new version of the website available",
|
||||
"Load": "Load"
|
||||
"Load": "Load",
|
||||
"Name": "Name",
|
||||
"Image": "Image",
|
||||
"Unknown": "Unknown",
|
||||
"Archive": "Archive",
|
||||
"Video": "Video",
|
||||
"Audio": "Audio",
|
||||
"Department": "Department",
|
||||
"Role": "Role",
|
||||
"Phone": "Phone"
|
||||
}
|
||||
|
@ -71,5 +71,7 @@
|
||||
"Version": "Version",
|
||||
"View": "Afficher",
|
||||
"ViewWeb": "Voir la version web",
|
||||
"Warning": "Attention"
|
||||
}
|
||||
"Warning": "Attention",
|
||||
"Name": "Nom",
|
||||
"Phone": "Téléphone"
|
||||
}
|
||||
|
@ -73,5 +73,7 @@
|
||||
"Version": "Versione",
|
||||
"View": "Visualizza",
|
||||
"ViewWeb": "Visualizza la versione web",
|
||||
"Warning": "Avviso"
|
||||
"Warning": "Avviso",
|
||||
"Name": "Nome",
|
||||
"Phone": "Telefono"
|
||||
}
|
||||
|
@ -73,5 +73,7 @@
|
||||
"Version": "ລຸ້ນ",
|
||||
"View": "ມຸມມອງ",
|
||||
"ViewWeb": "ເປີດຜ່ານຫນ້າເວັບ",
|
||||
"Warning": "ແຈ້ງເຕືອນ"
|
||||
"Warning": "ແຈ້ງເຕືອນ",
|
||||
"Name": "ຊື່",
|
||||
"Phone": "ໂທລະສັບ"
|
||||
}
|
||||
|
@ -73,5 +73,7 @@
|
||||
"Version": "Versão",
|
||||
"View": "Ver",
|
||||
"ViewWeb": "Veja a versão web",
|
||||
"Warning": "Aviso"
|
||||
"Warning": "Aviso",
|
||||
"Name": "Nome",
|
||||
"Phone": "Telefone"
|
||||
}
|
||||
|
@ -73,5 +73,7 @@
|
||||
"Version": "Versiune",
|
||||
"View": "Vizualizare",
|
||||
"ViewWeb": "Accesați versiunea online",
|
||||
"Warning": "Avertisment"
|
||||
"Warning": "Avertisment",
|
||||
"Name": "Numele",
|
||||
"Phone": "Telefon"
|
||||
}
|
||||
|
@ -95,5 +95,14 @@
|
||||
"Warning": "Внимание",
|
||||
"ResetApplication": "Сбросить приложение",
|
||||
"NewVersionAvailable": "Доступна новая версия веб-сайта",
|
||||
"Load": "Загрузить"
|
||||
"Load": "Загрузить",
|
||||
"Name": "Название",
|
||||
"Image": "Изображение",
|
||||
"Unknown": "Неизвестный",
|
||||
"Archive": "Архив",
|
||||
"Video": "Видео",
|
||||
"Audio": "Аудио",
|
||||
"Department": "Отдел",
|
||||
"Role": "Позиция",
|
||||
"Phone": "Телефон"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user