Таблицы, добавление, редактирование, удаление, удаление выделенного.
This commit is contained in:
parent
1e6688e4cf
commit
123a6fde18
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,6 +4,8 @@
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.idea
|
||||
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
24
.idea/codeStyles/Project.xml
generated
24
.idea/codeStyles/Project.xml
generated
@ -24,5 +24,29 @@
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
<DBN-PSQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false" />
|
||||
</DBN-PSQL>
|
||||
<DBN-SQL>
|
||||
<case-options enabled="true">
|
||||
<option name="KEYWORD_CASE" value="lower" />
|
||||
<option name="FUNCTION_CASE" value="lower" />
|
||||
<option name="PARAMETER_CASE" value="lower" />
|
||||
<option name="DATATYPE_CASE" value="lower" />
|
||||
<option name="OBJECT_CASE" value="preserve" />
|
||||
</case-options>
|
||||
<formatting-settings enabled="false">
|
||||
<option name="STATEMENT_SPACING" value="one_line" />
|
||||
<option name="CLAUSE_CHOP_DOWN" value="chop_down_if_statement_long" />
|
||||
<option name="ITERATION_ELEMENTS_WRAPPING" value="chop_down_if_not_single" />
|
||||
</formatting-settings>
|
||||
</DBN-SQL>
|
||||
</code_scheme>
|
||||
</component>
|
159
.idea/workspace.xml
generated
159
.idea/workspace.xml
generated
@ -1,12 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="3c1f5459-12ab-4427-89ae-be4c0e341eb6" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Button/Button.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Button/button.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Checkbox/Checkbox.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Checkbox/checkbox.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Checkbox/selected.svg" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Input/Input.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Input/input.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Loader/Loader.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Loader/loader.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Select/Select.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Common/Select/select.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Form/Form.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Form/form.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Modal/Modal.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Modal/modal.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Sidebar/Sidebar.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Sidebar/sidebar.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/Table.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableCell/TableCell.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableCell/tableCell.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableEmpty/TableEmpty.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableEmpty/tableEmpty.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableHead/TableHead.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableHead/tableHead.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableHeadItem/TableHeadItem.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableHeadItem/tableHeadItem.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableRow/TableRow.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/TableRow/tableRow.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Table/table.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Template/TableCompany/TableCompany.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Template/TableCompany/tableCompany.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Template/TableWorkers/TableWorkers.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/Components/Template/TableWorkers/tableWorkers.scss" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/helpers/array.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/mock/mockTable.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/store/store.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/store/tableCompanySlice.js" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/src/store/tableWorkersSlice.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/codeStyles/Project.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/codeStyles/Project.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package-lock.json" beforeDir="false" afterPath="$PROJECT_DIR$/package-lock.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/package.json" beforeDir="false" afterPath="$PROJECT_DIR$/package.json" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/App.css" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.scss" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/App.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/App.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/index.js" beforeDir="false" afterPath="$PROJECT_DIR$/src/index.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/logo.svg" beforeDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="JavaScript File" />
|
||||
<option value="SCSS File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="2J35rc74r3VojkFeH4ngbLhp6ge" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showExcludedFiles" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../IRI" />
|
||||
<property name="list.type.of.created.stylesheet" value="SCSS" />
|
||||
<property name="node.js.detected.package.eslint" value="true" />
|
||||
<property name="node.js.detected.package.standard" value="true" />
|
||||
<property name="node.js.path.for.package.eslint" value="project" />
|
||||
<property name="node.js.path.for.package.standard" value="project" />
|
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
||||
<property name="node.js.selected.package.standard" value="" />
|
||||
<property name="nodejs_package_manager_path" value="npm" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/Components/Table" />
|
||||
</key>
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/Components/Common" />
|
||||
<recent name="$PROJECT_DIR$/src/Components/Table" />
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
<recent name="$PROJECT_DIR$/src/Components/Common/Checkbox" />
|
||||
<recent name="$PROJECT_DIR$/src/Components" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="3c1f5459-12ab-4427-89ae-be4c0e341eb6" name="Default Changelist" comment="" />
|
||||
<created>1671294125199</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1671294125199</updated>
|
||||
<workItem from="1671294127387" duration="5598000" />
|
||||
<workItem from="1671299757432" duration="6341000" />
|
||||
<workItem from="1671365091146" duration="14875000" />
|
||||
<workItem from="1671388144305" duration="15952000" />
|
||||
<workItem from="1671448137631" duration="41117000" />
|
||||
<workItem from="1671524387598" duration="275000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="1" />
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state x="555" y="181" width="800" height="671" key="#ESLint" timestamp="1671294406686">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="555" y="181" width="800" height="671" key="#ESLint/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671294406686" />
|
||||
<state x="627" y="325" width="655" height="382" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1671390741373">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="627" y="325" width="655" height="382" key="#com.intellij.fileTypes.FileTypeChooser/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671390741373" />
|
||||
<state x="693" y="269" width="524" height="494" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1671375945131">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="693" y="269" width="524" height="494" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671375945131" />
|
||||
<state x="561" y="118" width="788" height="796" key="CommitChangelistDialog2" timestamp="1671524816158">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="561" y="118" width="788" height="796" key="CommitChangelistDialog2/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671524816158" />
|
||||
<state x="743" y="277" width="424" height="478" key="FileChooserDialogImpl" timestamp="1671486080118">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="743" y="277" width="424" height="478" key="FileChooserDialogImpl/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671486080118" />
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.bottom" timestamp="1671299466029">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.bottom/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671299466029" />
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.center" timestamp="1671299466029">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.center/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671299466029" />
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.left" timestamp="1671299466029">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.left/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671299466029" />
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.right" timestamp="1671299466029">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state width="1899" height="276" key="GridCell.Tab.0.right/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671299466029" />
|
||||
<state x="624" y="238" key="run.anything.popup" timestamp="1671472733814">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="624" y="238" key="run.anything.popup/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671472733814" />
|
||||
<state x="623" y="228" width="672" height="678" key="search.everywhere.popup" timestamp="1671389871175">
|
||||
<screen x="0" y="0" width="1920" height="1040" />
|
||||
</state>
|
||||
<state x="623" y="228" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040/0.0.1920.1040@0.0.1920.1040" timestamp="1671389871175" />
|
||||
</component>
|
||||
</project>
|
3020
package-lock.json
generated
3020
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -6,9 +6,12 @@
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"node-sass": "^7.0.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
@ -34,5 +37,9 @@
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.1",
|
||||
"react-redux": "^8.0.5"
|
||||
}
|
||||
}
|
||||
|
38
src/App.css
38
src/App.css
@ -1,38 +0,0 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
85
src/App.js
85
src/App.js
@ -1,23 +1,76 @@
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import React, {useEffect, useState} from "react";
|
||||
import {useDispatch, useSelector} from 'react-redux'
|
||||
|
||||
import TableWorkers from "./Components/Template/TableWorkers/TableWorkers";
|
||||
import TableCompany from "./Components/Template/TableCompany/TableCompany";
|
||||
|
||||
import {companyRecords} from "./mock/mockTable";
|
||||
import {companyWorkers} from "./mock/mockTable";
|
||||
import {tableCompanySlice} from "./store/tableCompanySlice";
|
||||
|
||||
|
||||
import {tableWorkersSlice} from "./store/tableWorkersSlice";
|
||||
import './App.scss'
|
||||
|
||||
function App() {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const tableCompanyData = useSelector((state) => state.table);
|
||||
const tableWorkersData = useSelector((state) => state.tableWorkers);
|
||||
|
||||
|
||||
const {
|
||||
setInitialCompany, calculateWorkersInCompany
|
||||
} = tableCompanySlice.actions;
|
||||
|
||||
|
||||
const {setInitialWorkers, showCompanyWorkers, setColorActiveWorker,} = tableWorkersSlice.actions;
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
dispatch(setInitialCompany(companyRecords));
|
||||
setIsLoading(false);
|
||||
dispatch(setInitialWorkers(companyWorkers))
|
||||
|
||||
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (tableCompanyData.data.data) {
|
||||
dispatch(showCompanyWorkers(tableCompanyData.activeCompanyId))
|
||||
}
|
||||
|
||||
}, [tableCompanyData.activeCompanyId]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
dispatch(setColorActiveWorker(tableWorkersData.activeWorkersId));
|
||||
|
||||
}, [tableWorkersData.activeWorkersId]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(tableWorkersData.workers)
|
||||
dispatch(calculateWorkersInCompany(tableWorkersData.workers))
|
||||
|
||||
}, [tableWorkersData.workers]);
|
||||
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
<TableCompany
|
||||
tableData={tableCompanyData}
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
<TableWorkers
|
||||
companyData={tableCompanyData.data.data}
|
||||
tableData={tableWorkersData}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
10
src/App.scss
Normal file
10
src/App.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
background: #131826;
|
||||
}
|
||||
body {
|
||||
background: #131826;
|
||||
height: 100vh;
|
||||
}
|
||||
|
13
src/Components/Common/Button/Button.js
Normal file
13
src/Components/Common/Button/Button.js
Normal file
@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import './button.scss';
|
||||
|
||||
export const Button = ({text, onClick, styles = {}, containerStyles = {}, children, disabled = false}) => {
|
||||
return (
|
||||
<div className='button' style={containerStyles}>
|
||||
<button onClick={onClick} style={styles} disabled={!!disabled}>
|
||||
{text || children}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
};
|
61
src/Components/Common/Button/button.scss
Normal file
61
src/Components/Common/Button/button.scss
Normal file
@ -0,0 +1,61 @@
|
||||
.button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #219FE6;
|
||||
border-radius: 4px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
color: #FFFFFF;
|
||||
outline: none;
|
||||
border: none;
|
||||
transition: all ease 0.3s;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
background: #0F8ACF;
|
||||
box-shadow: 0 0 33px #276B92;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #3F3F3F;
|
||||
color: #ACACAC;
|
||||
box-shadow: none;
|
||||
cursor: alias;
|
||||
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 33px #276B92, inset 0 0 10px rgba(14, 89, 131, 0.43);
|
||||
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #0F8ACF;
|
||||
|
||||
&:before {
|
||||
background: none;
|
||||
border: 1px solid #0F8ACF;
|
||||
content: "";
|
||||
display: block;
|
||||
border-radius: 6px;
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
bottom: -4px;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
src/Components/Common/Checkbox/Checkbox.js
Normal file
18
src/Components/Common/Checkbox/Checkbox.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
import selectedIcon from './selected.svg'
|
||||
|
||||
import './checkbox.scss'
|
||||
|
||||
export const Checkbox = ({ isActive, onChange = () => {} }) => {
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<div onClick={onChange} className='checkbox__input'>
|
||||
{isActive && <img src={selectedIcon} alt="iconChecked"/>}
|
||||
</div>
|
||||
|
||||
|
||||
)
|
||||
};
|
10
src/Components/Common/Checkbox/checkbox.scss
Normal file
10
src/Components/Common/Checkbox/checkbox.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.checkbox__input {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid #898A98;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
3
src/Components/Common/Checkbox/selected.svg
Normal file
3
src/Components/Common/Checkbox/selected.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.66659 0.333328C2.78253 0.333328 1.93468 0.684518 1.30956 1.30964C0.684441 1.93476 0.333252 2.78261 0.333252 3.66666V10.3333C0.333252 11.2174 0.684441 12.0652 1.30956 12.6904C1.93468 13.3155 2.78253 13.6667 3.66659 13.6667H10.3333C11.2173 13.6667 12.0652 13.3155 12.6903 12.6904C13.3154 12.0652 13.6666 11.2174 13.6666 10.3333V3.66666C13.6666 2.78261 13.3154 1.93476 12.6903 1.30964C12.0652 0.684518 11.2173 0.333328 10.3333 0.333328H3.66659ZM9.48659 6.12266C9.54647 6.05875 9.59318 5.98367 9.62404 5.90171C9.65491 5.81975 9.66933 5.73251 9.66648 5.64498C9.66364 5.55744 9.64358 5.47133 9.60745 5.39154C9.57132 5.31176 9.51983 5.23988 9.45592 5.18C9.39201 5.12011 9.31693 5.0734 9.23497 5.04254C9.15301 5.01167 9.06577 4.99725 8.97823 5.0001C8.8907 5.00294 8.80458 5.023 8.7248 5.05913C8.64502 5.09526 8.57314 5.14675 8.51325 5.21066L6.45792 7.404L5.44258 6.502C5.30956 6.39141 5.13875 6.33686 4.96625 6.34987C4.79375 6.36289 4.63306 6.44245 4.51813 6.57174C4.4032 6.70104 4.34303 6.86994 4.35033 7.04278C4.35763 7.21562 4.43183 7.37885 4.55725 7.49799L6.05725 8.83133C6.18704 8.94662 6.35669 9.00674 6.53011 8.99889C6.70354 8.99105 6.86706 8.91587 6.98592 8.78933L9.48592 6.12266H9.48659Z" fill="#219FE6"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
39
src/Components/Common/Input/Input.js
Normal file
39
src/Components/Common/Input/Input.js
Normal file
@ -0,0 +1,39 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
|
||||
import './input.scss';
|
||||
|
||||
export const Input = ({ required, name, type = 'text', value, placeholder, isNotChangeable=false, onChange=()=>{}, onChangeValue=(value)=>value, fieldData={},}) => {
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
useEffect( ()=>{
|
||||
setInputValue(value)
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className={`customInput ${required && !inputValue ? ' customInput--required':''}`}>
|
||||
<div className='customInput__field'>
|
||||
<input
|
||||
name={name}
|
||||
className='customInput__input'
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
disabled={isNotChangeable}
|
||||
value={inputValue}
|
||||
{...fieldData}
|
||||
onChange={e => {
|
||||
const res = onChangeValue(e.target.value);
|
||||
setInputValue(res);
|
||||
fieldData.onChange && fieldData.onChange({...e, target: {...e.target, value: res}})
|
||||
}}
|
||||
onBlur={(e)=>{
|
||||
onChange(inputValue);
|
||||
fieldData.onBlur && fieldData.onBlur(e)
|
||||
}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
50
src/Components/Common/Input/input.scss
Normal file
50
src/Components/Common/Input/input.scss
Normal file
@ -0,0 +1,50 @@
|
||||
.customInput {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
&--required {
|
||||
border: 1px solid #b21;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&__field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #2D313D;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: #2D313D;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
border: 1px solid #2D313D;
|
||||
color: #898A98;
|
||||
transition: all ease 0.3s;
|
||||
|
||||
&:hover {
|
||||
background: #3A3F4F;
|
||||
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
border: 1px solid #219FE6
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: #3F3F3F;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
9
src/Components/Common/Loader/Loader.js
Normal file
9
src/Components/Common/Loader/Loader.js
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import './loader.scss';
|
||||
|
||||
export const Loader = () => {
|
||||
return (
|
||||
<div className='loader'/>
|
||||
)
|
||||
};
|
49
src/Components/Common/Loader/loader.scss
Normal file
49
src/Components/Common/Loader/loader.scss
Normal file
@ -0,0 +1,49 @@
|
||||
.loader {
|
||||
--clock-color: #007affbb;
|
||||
--white-color: #fff;
|
||||
--clock-width: 2.5rem;
|
||||
--clock-radius: calc(var(--clock-width) / 2);
|
||||
--clock-minute-length: calc(var(--clock-width) * 0.4);
|
||||
--clock-hour-length: calc(var(--clock-width) * 0.2);
|
||||
--clock-thickness: 0.2rem;
|
||||
|
||||
margin-left: 12px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: var(--clock-width);
|
||||
height: var(--clock-width);
|
||||
border: 2px solid var(--clock-color);
|
||||
border-radius: 50%;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
top: calc(var(--clock-radius) * 0.25);
|
||||
width: var(--clock-thickness);
|
||||
background: var(--clock-color);
|
||||
border-radius: 10px;
|
||||
transform-origin: center calc(100% - calc(var(--clock-thickness) / 2));
|
||||
animation: spin infinite linear;
|
||||
}
|
||||
|
||||
&::before {
|
||||
height: var(--clock-minute-length);
|
||||
animation-duration: 2s;
|
||||
}
|
||||
|
||||
&::after {
|
||||
top: calc(var(--clock-radius) * 0.25 + var(--clock-hour-length));
|
||||
height: var(--clock-hour-length);
|
||||
animation-duration: 15s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
20
src/Components/Common/Select/Select.js
Normal file
20
src/Components/Common/Select/Select.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import './select.scss'
|
||||
|
||||
export const Select = ({options, onChange}) => {
|
||||
|
||||
return (
|
||||
<select className='select' defaultValue={'DEFAULT'} onChange={(e) => {
|
||||
|
||||
onChange(e.target.value)
|
||||
}}>
|
||||
<option disabled value='DEFAULT'>Выберите компанию</option>
|
||||
{
|
||||
options.map((item, key) => {
|
||||
return <option key={key} value={item[Object.keys(item)[0]]}>{item[Object.keys(item)[1]]}</option>
|
||||
})
|
||||
}
|
||||
|
||||
</select>
|
||||
)
|
||||
};
|
10
src/Components/Common/Select/select.scss
Normal file
10
src/Components/Common/Select/select.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.select {
|
||||
background: #2D313D;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
color: white;
|
||||
outline: none;
|
||||
}
|
68
src/Components/Form/Form.js
Normal file
68
src/Components/Form/Form.js
Normal file
@ -0,0 +1,68 @@
|
||||
import React, {useState} from "react";
|
||||
|
||||
import {Input} from "../Common/Input/Input";
|
||||
import {Button} from "../Common/Button/Button";
|
||||
|
||||
import './form.scss'
|
||||
import {Select} from "../Common/Select/Select";
|
||||
|
||||
|
||||
export const Form = ({
|
||||
data, config, onClick = () => {
|
||||
}
|
||||
}) => {
|
||||
|
||||
const [formData, setFormData] = useState(data);
|
||||
|
||||
const inputs = config.filter((item) => item.editable === true);
|
||||
const inputsIdRender = inputs.map((item) => item.columnId);
|
||||
|
||||
return (
|
||||
<form className='form'>
|
||||
{
|
||||
Object.entries(formData || {}).map((item, key) => {
|
||||
|
||||
const rowName = inputs.find((i) =>
|
||||
i.columnId === item[0]
|
||||
);
|
||||
|
||||
if (inputsIdRender.indexOf(item[0]) + 1) {
|
||||
if (Array.isArray(item[1])) {
|
||||
return <div key={key} className='form__row'>
|
||||
<label>{rowName.columnName}</label>
|
||||
<Select options={item[1]}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
[item[0]]: [...item[1]].map((i) => {
|
||||
if (i.companyId === parseInt(e)) {
|
||||
return {...i, active: true}
|
||||
} else {
|
||||
return {...i, active: false}
|
||||
}
|
||||
})
|
||||
})}/>
|
||||
</div>
|
||||
}
|
||||
return (
|
||||
<div className='form__row' key={key}>
|
||||
<label>{rowName.columnName}</label>
|
||||
<Input name={item[0]}
|
||||
onChange={(e) => setFormData({...formData, [item[0]]: e})} type="text"
|
||||
isNotChangeable={!rowName.editable}
|
||||
value={item[1]}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
)}
|
||||
|
||||
<div className='form__buttons'>
|
||||
<Button onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onClick(formData);
|
||||
}}>Сохранить</Button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
};
|
18
src/Components/Form/form.scss
Normal file
18
src/Components/Form/form.scss
Normal file
@ -0,0 +1,18 @@
|
||||
.form {
|
||||
background: #1c2a41;
|
||||
|
||||
&__row {
|
||||
label {
|
||||
color: white;
|
||||
}
|
||||
|
||||
margin-bottom: 10px;
|
||||
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
}
|
18
src/Components/Modal/Modal.js
Normal file
18
src/Components/Modal/Modal.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import ReactDom from 'react-dom'
|
||||
|
||||
import './modal.scss'
|
||||
|
||||
export default React.memo(function Modal({isOpen, children, onClose = ()=> {}}) {
|
||||
|
||||
const body = document.querySelector('body');
|
||||
|
||||
return ReactDom.createPortal(
|
||||
<div className={`modal ${isOpen ? ' active' : ''}`} onClick={() => onClose()}>
|
||||
<div className='modal__content' onClick={(e) => e.stopPropagation()}>
|
||||
<button className='modal__close' onClick={() => onClose()}>Х</button>
|
||||
{children}
|
||||
</div>
|
||||
</div>, body)
|
||||
|
||||
});
|
29
src/Components/Modal/modal.scss
Normal file
29
src/Components/Modal/modal.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.modal {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: scale(0);
|
||||
|
||||
&__content {
|
||||
background-color: #1c2a41;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal.active {
|
||||
transform: scale(1)
|
||||
}
|
15
src/Components/Sidebar/Sidebar.js
Normal file
15
src/Components/Sidebar/Sidebar.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
|
||||
|
||||
import './sidebar.scss'
|
||||
|
||||
export const Sidebar = () => {
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
</div>
|
||||
)
|
||||
|
||||
};
|
0
src/Components/Sidebar/sidebar.scss
Normal file
0
src/Components/Sidebar/sidebar.scss
Normal file
57
src/Components/Table/Table.js
Normal file
57
src/Components/Table/Table.js
Normal file
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
|
||||
import {TableRow} from "./TableRow/TableRow";
|
||||
import {TableHead} from "./TableHead/TableHead";
|
||||
import {TableEmpty} from "./TableEmpty/TableEmpty";
|
||||
import {Button} from "../Common/Button/Button";
|
||||
import {Loader} from "../Common/Loader/Loader";
|
||||
|
||||
import './table.scss'
|
||||
|
||||
export const Table = ({groups, tableConfig, data, emptyTable, fetchMore, isFetching, hasMore, isLoading, onChange}) => {
|
||||
|
||||
if(isLoading) {
|
||||
return (
|
||||
<div className='table'>
|
||||
<TableEmpty {...emptyTable} text='Загрузка' loader={Loader} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='table'>
|
||||
{
|
||||
data && data.length > 0 ?
|
||||
|
||||
<div className='table__data'>
|
||||
|
||||
<TableHead tableConfig={tableConfig} groups={groups}/>
|
||||
|
||||
<div className='table__rowList'>
|
||||
|
||||
{
|
||||
data.map((row, key) => {
|
||||
return (
|
||||
<TableRow row={row} onChange={onChange} key={`TableRow${key}`} tableConfig={tableConfig}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
{ hasMore && <div className='table__load'>
|
||||
{
|
||||
isFetching
|
||||
? <Button><Loader /></Button>
|
||||
: <Button onClick={()=>fetchMore()}>Load more</Button>
|
||||
}
|
||||
</div> }
|
||||
</div>
|
||||
: <TableEmpty {...emptyTable}/>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
};
|
14
src/Components/Table/TableCell/TableCell.js
Normal file
14
src/Components/Table/TableCell/TableCell.js
Normal file
@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import './tableCell.scss';
|
||||
|
||||
export const TableCell = ({ row, tableConfig, onChange, item, columnWidth, renderRowItem = (item => item), isGroupEnd, onClick = ()=> {}}) => {
|
||||
return (
|
||||
<div onClick={onClick} className={`tableCell${isGroupEnd ? " tableCell--last" : ""}`}
|
||||
style={{minWidth: columnWidth}}>
|
||||
{
|
||||
renderRowItem(item, row, tableConfig, onChange)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
};
|
36
src/Components/Table/TableCell/tableCell.scss
Normal file
36
src/Components/Table/TableCell/tableCell.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.tableCell {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #FFFFFF;
|
||||
height: 100%;
|
||||
|
||||
|
||||
&--last {
|
||||
border-right: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
&>p {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
}
|
26
src/Components/Table/TableEmpty/TableEmpty.js
Normal file
26
src/Components/Table/TableEmpty/TableEmpty.js
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import './tableEmpty.scss';
|
||||
|
||||
export const TableEmpty = ({icon, text, button, link, loader: Loader}) => {
|
||||
return (
|
||||
<div className='tableEmpty'>
|
||||
{ icon && <div className='tableEmpty__icon'>
|
||||
<img src={icon} alt={'icon'}/>
|
||||
</div> }
|
||||
<div className='tableEmpty__text'>
|
||||
<p>{text}</p>
|
||||
{ Loader && <Loader /> }
|
||||
</div>
|
||||
|
||||
{/*{button && <div className='tableEmpty__button'>*/}
|
||||
{/* {*/}
|
||||
{/* button.link*/}
|
||||
{/* ? <Link to={button.link}>{button.text}</Link>*/}
|
||||
{/* : <Button {...button} />*/}
|
||||
{/* }*/}
|
||||
|
||||
{/*</div>}*/}
|
||||
</div>
|
||||
)
|
||||
};
|
64
src/Components/Table/TableEmpty/tableEmpty.scss
Normal file
64
src/Components/Table/TableEmpty/tableEmpty.scss
Normal file
@ -0,0 +1,64 @@
|
||||
.tableEmpty {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 25px 0;
|
||||
background: #131826;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-top: 4px;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
margin-top: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 446px;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
color: #FFFFFF;
|
||||
|
||||
@media (max-width: 480px) {
|
||||
margin-top: 27px;
|
||||
width: 258px;
|
||||
}
|
||||
}
|
||||
|
||||
&__button {
|
||||
margin-top: 30px;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
|
||||
width: 135px;
|
||||
height: 42px;
|
||||
|
||||
background: #219FE6;
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
37
src/Components/Table/TableHead/TableHead.js
Normal file
37
src/Components/Table/TableHead/TableHead.js
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
import {TableHeadItem} from "../TableHeadItem/TableHeadItem";
|
||||
|
||||
import './tableHead.scss';
|
||||
|
||||
export const TableHead = ({groups, tableConfig, info}) => {
|
||||
return (
|
||||
<div className='tableHead'>
|
||||
{
|
||||
groups
|
||||
? groups.map((groupItem, key) => {
|
||||
return (
|
||||
<div key={key} className='tableHead__group'
|
||||
style={{minWidth: groupItem.width, flex: groupItem.columnCount}}>
|
||||
<TableHeadItem item={{columnName: groupItem.id}} info={groupItem.info} isGroup={true}/>
|
||||
<div className='tableHead__groupItems'>
|
||||
{
|
||||
tableConfig.filter(headItem => headItem.groupId === groupItem.id).map((headItem, key) => {
|
||||
return (
|
||||
<TableHeadItem item={headItem} key={key} info={info && info[headItem.columnId]}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
: tableConfig.map((headItem, key) => {
|
||||
return (
|
||||
<TableHeadItem key={`tableHeadItem${key}`} item={headItem}/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
};
|
44
src/Components/Table/TableHead/tableHead.scss
Normal file
44
src/Components/Table/TableHead/tableHead.scss
Normal file
@ -0,0 +1,44 @@
|
||||
.tableHead {
|
||||
display: flex;
|
||||
background-color: #131826;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
// &__item {
|
||||
// height: 38px;
|
||||
// position: relative;
|
||||
// flex: 1;
|
||||
|
||||
// &:after {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// width: 100%;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// border-bottom: 1px solid #282D3A;
|
||||
// }
|
||||
// }
|
||||
|
||||
&__groupItems {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
&__group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-right: 1px solid #282D3A;
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
29
src/Components/Table/TableHeadItem/TableHeadItem.js
Normal file
29
src/Components/Table/TableHeadItem/TableHeadItem.js
Normal file
@ -0,0 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import './tableHeadItem.scss';
|
||||
|
||||
export const TableHeadItem = ({item, isGroup}) => {
|
||||
|
||||
return (
|
||||
<div className={`tableHeadItem${isGroup ? ' tableHeadItem--group' : ''}`}
|
||||
style={{minWidth: item.columnWidth, width: '100%'}}>
|
||||
<div className='tableHeadItem__text'>
|
||||
{
|
||||
typeof item.columnName === "function" ?
|
||||
item.columnName() : item.columnName
|
||||
}
|
||||
</div>
|
||||
{
|
||||
item.info
|
||||
&& (
|
||||
<div className='tableHeadItem__info'>
|
||||
<div className='tableHeadItem__message'>
|
||||
{item.info.message}
|
||||
</div>
|
||||
{item.info.message ? 'i' : ''}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
};
|
116
src/Components/Table/TableHeadItem/tableHeadItem.scss
Normal file
116
src/Components/Table/TableHeadItem/tableHeadItem.scss
Normal file
@ -0,0 +1,116 @@
|
||||
.tableHeadItem {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 38px;
|
||||
flex: 1;
|
||||
//padding: 10px 0;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
|
||||
&--group {
|
||||
height: 50px;
|
||||
padding: 18px 27px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
//border-left: 1px solid #282D3A;
|
||||
|
||||
.tableHeadItem__text {
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tableHeadItem__info {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 8px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
font-size: 8px;
|
||||
|
||||
&--highlighted {
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 0 20px #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
line-height: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #898A98;
|
||||
}
|
||||
|
||||
&__info {
|
||||
margin-left: 5px;
|
||||
position: relative;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background-color: #898A98;
|
||||
border-radius: 50%;
|
||||
font-size: 8px;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 0 20px #FFFFFF;
|
||||
.tableHeadItem__message {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__message {
|
||||
position: absolute;
|
||||
display: none;
|
||||
width: max-content;
|
||||
max-width: 420px;
|
||||
padding: 10px;
|
||||
background-color: #131826;
|
||||
color: #FFFFFF;
|
||||
font-size: 12px;
|
||||
z-index: 2;
|
||||
top: initial;
|
||||
bottom: 16px;
|
||||
left: initial;
|
||||
right: -50%;
|
||||
transform: translate(20%, 0);
|
||||
}
|
||||
}
|
28
src/Components/Table/TableRow/TableRow.js
Normal file
28
src/Components/Table/TableRow/TableRow.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
|
||||
import {TableCell} from "../TableCell/TableCell";
|
||||
|
||||
import './tableRow.scss';
|
||||
|
||||
export const TableRow = ({tableConfig, row, onChange}) => {
|
||||
return (
|
||||
<div className={`tableRow ${row.active ? "tableRow--active" : ""}`}>
|
||||
{
|
||||
tableConfig.map(({columnId, columnWidth, renderRowItem, isGroupEnd}, key) => {
|
||||
return (
|
||||
<TableCell
|
||||
key={`cell${row.id}${key}`}
|
||||
row={row}
|
||||
onChange={onChange}
|
||||
item={row[columnId]}
|
||||
columnWidth={columnWidth}
|
||||
renderRowItem={renderRowItem}
|
||||
isGroupEnd={isGroupEnd}
|
||||
tableConfig={tableConfig}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
};
|
28
src/Components/Table/TableRow/tableRow.scss
Normal file
28
src/Components/Table/TableRow/tableRow.scss
Normal file
@ -0,0 +1,28 @@
|
||||
.tableRow {
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
background-color: #131826;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
// &__cell {
|
||||
// height: 100%;
|
||||
// border-bottom: 1px solid #282D3A;
|
||||
// flex: 1;
|
||||
// }
|
||||
|
||||
}
|
||||
.tableRow--active {
|
||||
background-color: #23324f;
|
||||
}
|
||||
|
22
src/Components/Table/table.scss
Normal file
22
src/Components/Table/table.scss
Normal file
@ -0,0 +1,22 @@
|
||||
.table {
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
&__data {
|
||||
background-color: #131826;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
&__head {
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
&__row {
|
||||
border-bottom: 1px solid #282D3A;
|
||||
}
|
||||
|
||||
&__load {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
169
src/Components/Template/TableCompany/TableCompany.js
Normal file
169
src/Components/Template/TableCompany/TableCompany.js
Normal file
@ -0,0 +1,169 @@
|
||||
import React, {useState} from "react";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
|
||||
import {Checkbox} from "../../Common/Checkbox/Checkbox";
|
||||
import {Button} from "../../Common/Button/Button";
|
||||
import {Table} from "../../Table/Table";
|
||||
import {Form} from "../../Form/Form";
|
||||
import Modal from "../../Modal/Modal";
|
||||
|
||||
import {tableCompanySlice} from "../../../store/tableCompanySlice";
|
||||
import {tableWorkersSlice} from "../../../store/tableWorkersSlice";
|
||||
|
||||
import './tableCompany.scss'
|
||||
|
||||
export default React.memo(function TableCompany({isLoading, tableData}) {
|
||||
|
||||
const [modalData, setModalData] = useState({});
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const [modalCreateOpen, setModalCreateOpen] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
setActiveCompany, setActiveAllCompany, removeSelectedCompany,
|
||||
setUnActiveWorkers, removeCompany, editCompanyData, createCompany
|
||||
} = tableCompanySlice.actions;
|
||||
|
||||
const {removeWorkersByIdCompany} = tableWorkersSlice.actions;
|
||||
|
||||
const companyTableConfig = [
|
||||
{
|
||||
columnId: 'check',
|
||||
columnName: () =>
|
||||
<Checkbox
|
||||
isActive={tableData.data.data.length === tableData.activeCompanyId.length}
|
||||
onChange={() => {
|
||||
dispatch(setUnActiveWorkers());
|
||||
dispatch(setActiveAllCompany());
|
||||
}}
|
||||
/>,
|
||||
columnWidth: '40px',
|
||||
renderRowItem: (item, row) =>
|
||||
<Checkbox
|
||||
id={row.id}
|
||||
isActive={tableData.activeCompanyId.includes(row.id)}
|
||||
onChange={() => {
|
||||
dispatch(setUnActiveWorkers());
|
||||
dispatch(setActiveCompany(row.id))
|
||||
}}
|
||||
/>,
|
||||
},
|
||||
{
|
||||
columnId: 'company_name',
|
||||
columnName: 'Название компании',
|
||||
columnWidth: '110px',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'number_of_employees',
|
||||
columnName: 'Количество сотрудников',
|
||||
columnWidth: '90px',
|
||||
editable: false,
|
||||
},
|
||||
{
|
||||
columnId: 'address',
|
||||
columnName: 'Адрес',
|
||||
columnWidth: '110px',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: "handlers",
|
||||
columnName: () => <Button
|
||||
styles={{padding: '1px', background: '#881818', maxWidth: '98px'}}
|
||||
onClick={() => {
|
||||
dispatch(removeSelectedCompany(tableData.activeCompanyId))
|
||||
}}
|
||||
>Удалить выбранные</Button>,
|
||||
renderRowItem: (item, row) =>
|
||||
<div>
|
||||
<Button styles={{padding: '3px', marginBottom: '3px'}} onClick={() => {
|
||||
dispatch(removeCompany(row.id));
|
||||
dispatch(removeWorkersByIdCompany(row.id))
|
||||
}
|
||||
}> Удалить
|
||||
</Button>
|
||||
<Button styles={{padding: '3px'}} onClick={
|
||||
() => {
|
||||
setModalData({...row});
|
||||
setModalOpen(true);
|
||||
}
|
||||
}>Редактировать
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
}
|
||||
];
|
||||
const createCompanyConfig = [
|
||||
{
|
||||
columnId: 'id',
|
||||
columnName: 'Фамилия',
|
||||
},
|
||||
{
|
||||
columnId: 'company_name',
|
||||
columnName: 'Название компании',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'address',
|
||||
columnName: 'Адрес',
|
||||
editable: true,
|
||||
},
|
||||
|
||||
];
|
||||
return (
|
||||
<div className='tableWrapper'>
|
||||
<div className='tableWrapper__title'>
|
||||
<Button styles={{background: "#3dda0c87", maxWidth: '200px'}} onClick={() => {
|
||||
setModalCreateOpen(true)
|
||||
}}>
|
||||
Добавить компанию
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
// hasMore={tableData.has_more_pages}
|
||||
// fetchMore={() => {
|
||||
// setCursor(tableData.next_page_cursor_param);
|
||||
// setPushTableData(true)
|
||||
// }}
|
||||
isLoading={isLoading}
|
||||
tableConfig={companyTableConfig}
|
||||
emptyTable={{
|
||||
text: "Пусто...",
|
||||
}}
|
||||
data={tableData.data.data}
|
||||
|
||||
/>
|
||||
{modalOpen &&
|
||||
<Modal isOpen={modalOpen} onClose={() => setModalOpen(false)}>
|
||||
<Form data={modalData} onClick={(formData) => {
|
||||
dispatch(editCompanyData(formData));
|
||||
setModalOpen(false)
|
||||
}} config={companyTableConfig}/>
|
||||
</Modal>}
|
||||
|
||||
{modalCreateOpen &&
|
||||
<Modal isOpen={modalCreateOpen} onClose={() => {
|
||||
setModalCreateOpen(false)
|
||||
}}>
|
||||
<Form data={{
|
||||
company_name: '',
|
||||
address: "",
|
||||
|
||||
}} onClick={(formData) => {
|
||||
|
||||
const newFormData = {
|
||||
...formData,
|
||||
id: Math.floor(Math.random() * 10000),
|
||||
};
|
||||
dispatch(createCompany(newFormData));
|
||||
setModalCreateOpen(false)
|
||||
}} config={createCompanyConfig}/>
|
||||
</Modal>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
173
src/Components/Template/TableWorkers/TableWorkers.js
Normal file
173
src/Components/Template/TableWorkers/TableWorkers.js
Normal file
@ -0,0 +1,173 @@
|
||||
import React, {useState} from "react";
|
||||
import {useDispatch} from "react-redux";
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
import {Table} from "../../Table/Table";
|
||||
import {Button} from "../../Common/Button/Button";
|
||||
import {Checkbox} from "../../Common/Checkbox/Checkbox";
|
||||
import {Form} from "../../Form/Form";
|
||||
import Modal from "../../Modal/Modal";
|
||||
|
||||
import {tableWorkersSlice} from "../../../store/tableWorkersSlice";
|
||||
|
||||
import './tableWorkers.scss'
|
||||
|
||||
export default React.memo(function TableWorkers({tableData, companyData}) {
|
||||
|
||||
const [modalData, setModalData] = useState({});
|
||||
const [modalEditOpen, setModalEditOpen] = useState(false);
|
||||
const [modalCreateOpen, setModalCreateOpen] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {removeWorker, editWorkersData, removeSelectedWorkers, setActiveWorker, setActiveAllWorkers, createWorker} = tableWorkersSlice.actions;
|
||||
const arrCompany = companyData?.map((item) => {
|
||||
return {companyId: item.id, company_name: item.company_name}
|
||||
});
|
||||
|
||||
const companyWorkersTableConfig = [
|
||||
{
|
||||
columnId: 'checkWorkers',
|
||||
columnName: () =>
|
||||
<Checkbox
|
||||
isActive={tableData.visibleWorkers.length === tableData.activeWorkersId.length}
|
||||
onChange={() => {
|
||||
dispatch(setActiveAllWorkers());
|
||||
}}
|
||||
/>,
|
||||
columnWidth: '40px',
|
||||
renderRowItem: (item, row) =>
|
||||
<Checkbox
|
||||
id={row.id}
|
||||
isActive={tableData.activeWorkersId.includes(row.id)}
|
||||
onChange={() => {
|
||||
dispatch(setActiveWorker(row.id))
|
||||
}}
|
||||
/>,
|
||||
},
|
||||
{
|
||||
columnId: 'last_name',
|
||||
columnName: 'Фамилия',
|
||||
columnWidth: '100px',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'first_name',
|
||||
columnName: 'Имя',
|
||||
columnWidth: '100px',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'position',
|
||||
columnName: 'Должность',
|
||||
columnWidth: '110px',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: "handlers",
|
||||
columnName: () => <Button
|
||||
onClick={() => {
|
||||
dispatch(removeSelectedWorkers(tableData.activeWorkersId))
|
||||
}}
|
||||
styles={{padding: '1px', background: '#881818', maxWidth: '98px'}}
|
||||
>Удалить выбранные</Button>,
|
||||
renderRowItem: (item, row) =>
|
||||
<div>
|
||||
<Button styles={{padding: '3px', marginBottom: '3px'}} onClick={() =>
|
||||
dispatch(removeWorker(row.id))
|
||||
}> Удалить
|
||||
</Button>
|
||||
<Button styles={{padding: '3px'}} onClick={
|
||||
() => {
|
||||
setModalData({...row});
|
||||
setModalEditOpen(true);
|
||||
}
|
||||
}>Редактировать
|
||||
</Button>
|
||||
|
||||
</div>
|
||||
},
|
||||
];
|
||||
|
||||
const createWorkerConfig = [
|
||||
{
|
||||
columnId: 'last_name',
|
||||
columnName: 'Фамилия',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'first_name',
|
||||
columnName: 'Имя',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'position',
|
||||
columnName: 'Должность',
|
||||
editable: true,
|
||||
},
|
||||
{
|
||||
columnId: 'companyId',
|
||||
columnName: 'Компания',
|
||||
editable: true,
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className='tableWrapper'>
|
||||
<div className='tableWrapper__title'>
|
||||
<Button styles={{background: "#3dda0c87", maxWidth: '200px'}} onClick={() => {
|
||||
setModalCreateOpen(true)
|
||||
}}>
|
||||
Добавить работника
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
// hasMore={tableData.has_more_pages}
|
||||
// fetchMore={() => {
|
||||
// setCursor(tableData.next_page_cursor_param);
|
||||
// setPushTableData(true)
|
||||
// }}
|
||||
|
||||
tableConfig={companyWorkersTableConfig}
|
||||
emptyTable={{
|
||||
text: "Пусто...",
|
||||
}}
|
||||
data={tableData.visibleWorkers}
|
||||
onChange={editWorkersData}
|
||||
/>
|
||||
{modalEditOpen &&
|
||||
<Modal isOpen={modalEditOpen} onClose={() => {
|
||||
setModalEditOpen(false)
|
||||
}}>
|
||||
<Form data={modalData} onClick={(formData) => {
|
||||
dispatch(editWorkersData(formData));
|
||||
setModalEditOpen(false)
|
||||
}} config={companyWorkersTableConfig}/>
|
||||
</Modal>
|
||||
}
|
||||
|
||||
{modalCreateOpen &&
|
||||
<Modal isOpen={modalCreateOpen} onClose={() => {
|
||||
setModalCreateOpen(false)
|
||||
}}>
|
||||
<Form data={{
|
||||
last_name: '',
|
||||
first_name: "",
|
||||
position: "",
|
||||
companyId: arrCompany
|
||||
}} onClick={(formData) => {
|
||||
|
||||
const newFormData = {
|
||||
...formData,
|
||||
id: uuidv4(),
|
||||
companyId: formData.companyId.find((item) => item.active).companyId
|
||||
|
||||
};
|
||||
dispatch(createWorker(newFormData));
|
||||
setModalCreateOpen(false)
|
||||
}} config={createWorkerConfig}/>
|
||||
</Modal>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
9
src/Components/Template/TableWorkers/tableWorkers.scss
Normal file
9
src/Components/Template/TableWorkers/tableWorkers.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.tableWorkers {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__title {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
8
src/helpers/array.js
Normal file
8
src/helpers/array.js
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
export function flat(arr) {
|
||||
const newArr = [];
|
||||
arr.forEach(i =>
|
||||
Array.isArray(i) ? newArr.push(...flat(i)) : newArr.push(i)
|
||||
);
|
||||
return newArr;
|
||||
}
|
13
src/index.js
13
src/index.js
@ -1,17 +1,22 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import {Provider} from "react-redux";
|
||||
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
import store from "./store/store";
|
||||
|
||||
import './index.css';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<App/>
|
||||
</Provider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
|
||||
reportWebVitals();
|
||||
|
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB |
73
src/mock/mockTable.js
Normal file
73
src/mock/mockTable.js
Normal file
@ -0,0 +1,73 @@
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
const tableData = [
|
||||
{
|
||||
id: 1111,
|
||||
company_name: "Sber Bank",
|
||||
address: 'asfd asd',
|
||||
},
|
||||
{
|
||||
id: 2222,
|
||||
company_name: "alfa Bank",
|
||||
address: 'asfdasd',
|
||||
|
||||
},
|
||||
{
|
||||
id: 3333,
|
||||
company_name: "Tinkoff Bank",
|
||||
address: 'asfd ',
|
||||
},
|
||||
|
||||
];
|
||||
const workers= [
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 1111,
|
||||
last_name: "Karpenko",
|
||||
first_name: "sergey",
|
||||
position: "Front End developer",
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 1111,
|
||||
last_name: "Ne karp",
|
||||
first_name: "Ne",
|
||||
position: "Ne Front End developer",
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 1111,
|
||||
last_name: "Sasuke",
|
||||
first_name: "Kakashi",
|
||||
position: "Коллектор",
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 2222,
|
||||
last_name: "Кактов",
|
||||
first_name: "ктото",
|
||||
position: "буздельник",
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 3333,
|
||||
last_name: "Savenko",
|
||||
first_name: "Dima",
|
||||
position: "Front End developer",
|
||||
},
|
||||
{
|
||||
id: uuidv4(),
|
||||
companyId: 3333,
|
||||
last_name: "Ne Savenko",
|
||||
first_name: "Ne Dima",
|
||||
position: "Ne Front End developer",
|
||||
},
|
||||
];
|
||||
|
||||
export const companyRecords = {
|
||||
data: tableData,
|
||||
totalRecords: tableData.length,
|
||||
pages: (tableData.length + 1) / 10
|
||||
};
|
||||
|
||||
export const companyWorkers = workers
|
18
src/store/store.js
Normal file
18
src/store/store.js
Normal file
@ -0,0 +1,18 @@
|
||||
import {configureStore, combineReducers} from "@reduxjs/toolkit";
|
||||
|
||||
import tableCompanySlice from './tableCompanySlice'
|
||||
import tableWorkersSlice from "./tableWorkersSlice";
|
||||
|
||||
import {composeWithDevTools} from "redux-devtools-extension";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
table: tableCompanySlice,
|
||||
tableWorkers: tableWorkersSlice,
|
||||
|
||||
});
|
||||
|
||||
const store = configureStore({
|
||||
reducer: rootReducer,
|
||||
}, composeWithDevTools());
|
||||
|
||||
export default store
|
89
src/store/tableCompanySlice.js
Normal file
89
src/store/tableCompanySlice.js
Normal file
@ -0,0 +1,89 @@
|
||||
import {createSlice} from "@reduxjs/toolkit";
|
||||
|
||||
|
||||
export const tableCompanySlice = createSlice({
|
||||
name: "tableCompany",
|
||||
initialState: {
|
||||
data: [],
|
||||
activeCompanyId: [],
|
||||
|
||||
},
|
||||
|
||||
reducers: {
|
||||
setInitialCompany(state, {payload}) {
|
||||
|
||||
state.data = payload
|
||||
},
|
||||
|
||||
calculateWorkersInCompany(state, {payload}) {
|
||||
const countWorkersInCompany = payload.reduce((acc, el) => {
|
||||
acc[el.companyId] = (acc[el.companyId] || 0) + 1;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
||||
if (Array.isArray(state.data.data)) {
|
||||
state.data.data = state.data?.data.map((i) => {
|
||||
return {
|
||||
...i,
|
||||
number_of_employees: countWorkersInCompany[i.id]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
setActiveCompany(state, {payload}) {
|
||||
|
||||
const index = state.activeCompanyId.indexOf(payload);
|
||||
if (index > -1) {
|
||||
state.activeCompanyId.splice(index, 1)
|
||||
} else {
|
||||
state.activeCompanyId.push(payload)
|
||||
}
|
||||
},
|
||||
|
||||
setActiveAllCompany(state) {
|
||||
if (state.activeCompanyId.length && state.activeCompanyId.length === state.data.data.length) {
|
||||
state.activeCompanyId = []
|
||||
} else {
|
||||
state.activeCompanyId = state.data.data.map(item => item.id)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
setUnActiveWorkers(state) {
|
||||
state.activeWorkersId = []
|
||||
},
|
||||
|
||||
removeCompany(state, {payload}) {
|
||||
state.data.data = state.data.data.filter((item) => item.id !== payload)
|
||||
},
|
||||
|
||||
removeSelectedCompany(state, {payload}) {
|
||||
|
||||
state.data.data = state.data.data.filter((item) => !payload.includes(item.id))
|
||||
},
|
||||
|
||||
editCompanyData(state, action) {
|
||||
state.data.data = state.data.data.map((i) => {
|
||||
if (i.id === action.payload.id) {
|
||||
return action.payload
|
||||
}
|
||||
return i
|
||||
})
|
||||
},
|
||||
createCompany(state, {payload}) {
|
||||
state.data.data = [...state.data.data, payload]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default tableCompanySlice.reducer
|
||||
|
||||
export const {
|
||||
setInitialCompany, setActiveCompany, setActiveAllCompany, createCompany,
|
||||
setUnActiveWorkers, removeCompany, editCompanyData, calculateWorkersInCompany
|
||||
} = tableCompanySlice.actions;
|
93
src/store/tableWorkersSlice.js
Normal file
93
src/store/tableWorkersSlice.js
Normal file
@ -0,0 +1,93 @@
|
||||
import {createSlice} from "@reduxjs/toolkit";
|
||||
|
||||
|
||||
export const tableWorkersSlice = createSlice({
|
||||
name: "tableWorkers",
|
||||
initialState: {
|
||||
workers: [],
|
||||
activeWorkersId: [],
|
||||
visibleWorkers: []
|
||||
},
|
||||
|
||||
reducers: {
|
||||
setInitialWorkers(state, {payload}) {
|
||||
state.workers = payload
|
||||
},
|
||||
|
||||
showCompanyWorkers(state, {payload}) {
|
||||
|
||||
state.visibleWorkers = state.workers.filter((item) => payload.includes(item.companyId))
|
||||
},
|
||||
|
||||
setColorActiveWorker(state) {
|
||||
|
||||
state.visibleWorkers = state.visibleWorkers.map((worker) => {
|
||||
if (state.activeWorkersId.includes(worker.id)) {
|
||||
return {...worker, active: true}
|
||||
} else {
|
||||
return {...worker, active: false}
|
||||
}
|
||||
})
|
||||
},
|
||||
setActiveWorker(state, action) {
|
||||
const index = state.activeWorkersId.indexOf(action.payload);
|
||||
if (index > -1) {
|
||||
state.activeWorkersId.splice(index, 1)
|
||||
} else {
|
||||
state.activeWorkersId.push(action.payload)
|
||||
}
|
||||
},
|
||||
|
||||
setActiveAllWorkers(state) {
|
||||
if (state.activeWorkersId.length > 0 && state.activeWorkersId.length === state.visibleWorkers.length) {
|
||||
state.activeWorkersId = []
|
||||
} else {
|
||||
state.activeWorkersId = state.visibleWorkers.map(item => item.id)
|
||||
}
|
||||
},
|
||||
removeWorkersByIdCompany(state, {payload}) {
|
||||
|
||||
state.workers = state.workers.filter((i) => i.companyId !== payload);
|
||||
state.visibleWorkers = state.visibleWorkers.filter((i) => i.companyId !== payload)
|
||||
|
||||
},
|
||||
|
||||
removeWorker(state, action) {
|
||||
state.workers = state.workers.filter((item) => item.id !== action.payload);
|
||||
state.visibleWorkers = state.visibleWorkers.filter((item) => item.id !== action.payload)
|
||||
},
|
||||
|
||||
removeSelectedWorkers(state, {payload}) {
|
||||
state.workers = state.workers.filter((item) => !payload.includes(item.id));
|
||||
state.visibleWorkers = state.visibleWorkers.filter((item) => !payload.includes(item.id))
|
||||
},
|
||||
|
||||
editWorkersData(state, {payload}) {
|
||||
state.workers = state.workers.map((i) => {
|
||||
if (i.id === payload.id) {
|
||||
return payload
|
||||
}
|
||||
return i
|
||||
});
|
||||
state.visibleWorkers = state.visibleWorkers.map((i) => {
|
||||
if (i.id === payload.id) {
|
||||
return payload
|
||||
}
|
||||
return i
|
||||
});
|
||||
},
|
||||
createWorker(state, {payload}) {
|
||||
state.workers = [...state.workers, payload]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default tableWorkersSlice.reducer
|
||||
|
||||
export const {
|
||||
setInitialWorkers, showCompanyWorkers, setColorActiveWorker, removeWorkersByIdCompany, removeWorker,
|
||||
editWorkersData, removeSelectedWorkers, setActiveWorker, createWorker
|
||||
|
||||
} = tableWorkersSlice.actions;
|
Loading…
Reference in New Issue
Block a user