Compare commits
No commits in common. "155888f7fcb5fa938a04f4ee7a26256ec934253f" and "047cabe14b1b0462d786b69d0b0fea91a04f9f64" have entirely different histories.
155888f7fc
...
047cabe14b
@ -21,7 +21,7 @@ export const DictVspApi = {
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
getDropdownVsp(params) {
|
||||
return api.get(`/dict/info/dropdown`, { params }).then((r) => r.data);
|
||||
},
|
||||
getDropdownVsp(params){
|
||||
return api.get(`/dict/info/dropdown`, params).then((r) => r.data);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
import api from "./client";
|
||||
|
||||
export const ProjectsApi = {
|
||||
list: (params) => {
|
||||
return api.get(`/projects`, { params }).then((r) => r.data);
|
||||
},
|
||||
|
||||
get: (id) => {
|
||||
return api.get(`/projects/${id}`).then((r) => r.data);
|
||||
},
|
||||
|
||||
create: (data) => {
|
||||
return api.post("/projects", data).then((r) => r.data);
|
||||
},
|
||||
|
||||
update: (id, data) => {
|
||||
return api.patch(`/projects/${id}`, data).then((r) => r.data);
|
||||
},
|
||||
|
||||
delete: (id) => {
|
||||
return api.delete(`/projects/${id}`).then((r) => r.data);
|
||||
},
|
||||
|
||||
getTableData: (id, sheetName, year) => {
|
||||
return api
|
||||
.get(`/projects/${id}/report/${year}/${sheetName}`)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
export: (id, year, sheetName) => {
|
||||
return api.get(`/export/project/${id}/report/${year}/${sheetName}`, {
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
};
|
||||
@ -17,7 +17,6 @@ import { CircularProgress } from '@mui/material';
|
||||
import NewTablePage from '../pages/NewTablePage.jsx';
|
||||
import TablesTest from '../pages/TablesTest.jsx';
|
||||
import NewFormTablePage from '../pages/NewFormTablePage.jsx'
|
||||
import ProjectsPage from '../pages/ProjectsPage/ProjectsPage.jsx';
|
||||
|
||||
export const PrivateRoute = () => {
|
||||
const { isAuthenticated, loading } = useAuth();
|
||||
@ -38,10 +37,8 @@ export const AppRoutes = () => {
|
||||
<Route element={<PrivateRoute />}>
|
||||
<Route path="/" element={<TasksPage />} />
|
||||
<Route path="/tasks" element={<TasksPage />} />
|
||||
<Route path="/projects" element={<ProjectsPage />}/>
|
||||
<Route path="/forms" element={<FormsPage />} />
|
||||
<Route path="/task/:taskId" element={<TaskPage />} />
|
||||
<Route path="/project/:projectId" element={<TaskPage />} />
|
||||
<Route path="/table/:tableId" element={<TablePage />} />
|
||||
<Route path="/table-temp/:tableId" element={<TableTempPage />} />
|
||||
<Route path="/admin_panel/users" element={<UsersPage />} />
|
||||
@ -52,7 +49,7 @@ export const AppRoutes = () => {
|
||||
<Route path="/new-table" element={<NewTablePage />} />
|
||||
<Route path="/tables-new" element={<TablesTest />} />
|
||||
<Route path="/tables/:form/:sheetName" element={<NewFormTablePage />} />
|
||||
<Route path="/table/form/:formId/form-type/:formType/:sheetName/:direction/:year" element={<NewFormTablePage />} />
|
||||
<Route path="/table/form/:formId/form-type/:formType/:sheetName/:direction" element={<NewFormTablePage />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
|
||||
@ -4,7 +4,7 @@ import { NavLink } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { TasksSvg, AdminPanelSvg, DictSvg } from '../common/icons/icons';
|
||||
import { useAuth } from '../../app/context/AuthProvider';
|
||||
import { ROLES_NAME_ID } from '../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../constants';
|
||||
|
||||
const Switch = styled.div`
|
||||
margin-left: 1rem;
|
||||
|
||||
@ -31,13 +31,12 @@ const LockBadge = styled.div`
|
||||
`;
|
||||
|
||||
// Функция для подсветки текста
|
||||
const highlightText = (text, searchQueries) => {
|
||||
if (!text) return text;
|
||||
const cleanSearchQueries = searchQueries.filter(q => (q !== undefined && q !== ''));
|
||||
if (cleanSearchQueries.length == 0) return text;
|
||||
const highlightText = (text, searchQuery) => {
|
||||
if (!searchQuery || !text) return text;
|
||||
|
||||
const escapedQuery = searchQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
||||
|
||||
const escapedQueries = cleanSearchQueries.map(query => query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
||||
const regex = new RegExp(`(${ escapedQueries.join('|')})`, 'gi');
|
||||
const parts = text.split(regex);
|
||||
|
||||
return parts.map((part, index) =>
|
||||
@ -90,7 +89,7 @@ const formatNumber = (value, asInteger = false) => {
|
||||
});
|
||||
};
|
||||
|
||||
const Cell = React.memo(({ cell, row, column, onClick, globalFilter, columnFilter, backgroundColor, color, isEditable }) => {
|
||||
const Cell = React.memo(({ cell, row, column, onClick, globalFilter, backgroundColor, color, isEditable }) => {
|
||||
const { lockedCells } = useRealtime();
|
||||
const cellKey = `${row.id}_${column.id}`;
|
||||
|
||||
@ -106,10 +105,9 @@ const Cell = React.memo(({ cell, row, column, onClick, globalFilter, columnFilte
|
||||
}
|
||||
|
||||
const highlightedContent = useMemo(() => {
|
||||
return highlightText(textValue, [globalFilter, columnFilter]);
|
||||
return highlightText(textValue, globalFilter);
|
||||
}, [textValue, globalFilter]);
|
||||
|
||||
|
||||
// Базовые стили для заблокированной ячейки
|
||||
const lockedStyles = isLocked ? {
|
||||
border: '2px solid #e0e0e0',
|
||||
|
||||
@ -29,8 +29,8 @@ import { additionVspRowTable } from './constants/addingRowConfig';
|
||||
import { SelectVspModal } from './Modals/SelectVspModal';
|
||||
import { getRowId } from './utils/rowUtils';
|
||||
|
||||
const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction, formType, year);
|
||||
const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
||||
const { data, setData, isLoading: isTableLoading, editingCells: dataEditingCells } = useRealtimeData(formId, sheetName, direction);
|
||||
const [globalFilter, setGlobalFilter] = useState('');
|
||||
const [showColumnFilters, setShowColumnFilters] = useState(false);
|
||||
const [selectedColumnId, setSelectedColumnId] = useState();
|
||||
@ -229,12 +229,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
minHeight: '1px',
|
||||
maxHeight: '1px',
|
||||
visibility: 'hidden',
|
||||
'& svg': {
|
||||
height: '1px',
|
||||
minHeight: '1px',
|
||||
maxHeight: '1px',
|
||||
visibility: 'hidden',
|
||||
}
|
||||
},
|
||||
},
|
||||
muiTablePaperProps: getTablePaperStyles(),
|
||||
@ -366,8 +360,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
formId={formId}
|
||||
sheetName={sheetName}
|
||||
direction={direction}
|
||||
year={year}
|
||||
isProject={formType == 'PROJECT'}
|
||||
/>
|
||||
|
||||
<div style={tableWrapperStyle}>
|
||||
@ -428,6 +420,8 @@ const RealtimeTable = ({ formType, formId, sheetName, direction, year }) => {
|
||||
onClose={() => setIsOpenModalSelectVsp(false)}
|
||||
formId={formId}
|
||||
onSelect={handleAddVspRow}
|
||||
// vspOptions={}
|
||||
// selectedVSP={}
|
||||
title="Выбор ВСП"
|
||||
/>
|
||||
</>
|
||||
|
||||
@ -18,7 +18,7 @@ import CollapsibleTree from '../../common/CollapsibleTree';
|
||||
import ZoomSlider from './ZoomSlider';
|
||||
import SearchComponent from '../../common/SearchComponent';
|
||||
import { ExportDefaultButton } from '../../common/Buttons/ButtonsActions';
|
||||
import { exportSheet, exportSheetProject } from '../../../utils/exportFile';
|
||||
import { exportSheet } from '../../../utils/exportForm';
|
||||
|
||||
const SettingPanel = ({
|
||||
selectedColumnId,
|
||||
@ -39,8 +39,6 @@ const SettingPanel = ({
|
||||
formId,
|
||||
sheetName,
|
||||
direction,
|
||||
year,
|
||||
isProject,
|
||||
}) => {
|
||||
const [isPinned, setIsPinned] = useState(false);
|
||||
const [isExporting, setIsExporting] = useState(false);
|
||||
@ -78,16 +76,7 @@ const SettingPanel = ({
|
||||
const handleClickDeleteFormula = () => { };
|
||||
|
||||
const handleExport = async () => {
|
||||
if (isExporting) return;
|
||||
if (isProject) {
|
||||
if (!formId || !sheetName || !year) return;
|
||||
setIsExporting(true);
|
||||
await exportSheetProject(formId, sheetName, year);
|
||||
setIsExporting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formId || !sheetName) return;
|
||||
if (!formId || !sheetName || isExporting) return;
|
||||
setIsExporting(true);
|
||||
await exportSheet(formId, sheetName, direction);
|
||||
setIsExporting(false);
|
||||
@ -126,27 +115,40 @@ const SettingPanel = ({
|
||||
</Tooltip>
|
||||
</GroupByObject>
|
||||
|
||||
<Divider orientation="vertical" flexItem />
|
||||
|
||||
{!isProject ??
|
||||
<>
|
||||
<Divider orientation="vertical" flexItem />
|
||||
<GroupByObject title="Строки">
|
||||
<Tooltip title="Добавить строку">
|
||||
<IconButton onClick={onAddRow} variant="outlined">
|
||||
<Plus />
|
||||
<AddLine />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<GroupByObject title="Строки">
|
||||
<Tooltip title="Добавить строку">
|
||||
<IconButton onClick={onAddRow} variant="outlined">
|
||||
<Plus />
|
||||
<AddLine />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Удалить строку">
|
||||
<IconButton onClick={onDeleteRow} variant="outlined">
|
||||
<Minus />
|
||||
<RemoveLine />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</GroupByObject>
|
||||
</>
|
||||
}
|
||||
<Tooltip title="Удалить строку">
|
||||
<IconButton onClick={onDeleteRow} variant="outlined">
|
||||
<Minus />
|
||||
<RemoveLine />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</GroupByObject>
|
||||
|
||||
{/* <Divider orientation="vertical" flexItem />
|
||||
|
||||
<GroupByObject title="Формулы">
|
||||
<Tooltip title="Добавить формулу">
|
||||
<IconButton onClick={handleClickAddFormula} variant="outlined">
|
||||
<FormulaSettingPanel />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="Удалить формулу">
|
||||
<IconButton onClick={handleClickDeleteFormula} variant="outlined">
|
||||
<Minus />
|
||||
<FormulaSettingPanel />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</GroupByObject> */}
|
||||
<Divider orientation="vertical" flexItem />
|
||||
<GroupByObject title="Столбцы">
|
||||
{columns ? (
|
||||
|
||||
@ -313,10 +313,10 @@ const FilterCell = ({ column, table }) => {
|
||||
onChange={(e) => handleFilterChange(e.target.value)}
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '0.25rem 0.375rem',
|
||||
fontSize: '0.6875rem',
|
||||
padding: '4px 6px',
|
||||
fontSize: '11px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '0.25rem',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
>
|
||||
@ -330,30 +330,23 @@ const FilterCell = ({ column, table }) => {
|
||||
);
|
||||
|
||||
case 'range': {
|
||||
// Для диапазона можно реализовать два поля
|
||||
const [min, max] = column.getFilterValue() || ['', ''];
|
||||
return (
|
||||
<div style={{ display: 'flex', gap: '0.25rem' }}> {/* 4px */}
|
||||
<div style={{ display: 'flex', gap: '4px' }}>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="От"
|
||||
value={min}
|
||||
onChange={(e) => column.setFilterValue([e.target.value, max])}
|
||||
style={{
|
||||
width: '50%',
|
||||
padding: '0.25rem',
|
||||
fontSize: '0.6875rem'
|
||||
}}
|
||||
style={{ width: '50%', padding: '4px', fontSize: '11px' }}
|
||||
/>
|
||||
<input
|
||||
type="number"
|
||||
placeholder="До"
|
||||
value={max}
|
||||
onChange={(e) => column.setFilterValue([min, e.target.value])}
|
||||
style={{
|
||||
width: '50%',
|
||||
padding: '0.25rem',
|
||||
fontSize: '0.6875rem'
|
||||
}}
|
||||
style={{ width: '50%', padding: '4px', fontSize: '11px' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -368,10 +361,10 @@ const FilterCell = ({ column, table }) => {
|
||||
placeholder="Поиск..."
|
||||
style={{
|
||||
width: '100%',
|
||||
padding: '0.25rem 0.375rem',
|
||||
fontSize: '0.6875rem',
|
||||
padding: '4px 6px',
|
||||
fontSize: '11px',
|
||||
border: '1px solid #ddd',
|
||||
borderRadius: '0.25rem',
|
||||
borderRadius: '4px',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
/>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
555
web/src/components/RealtimeTable/constants/FORM_3/LP.js
Normal file
555
web/src/components/RealtimeTable/constants/FORM_3/LP.js
Normal file
@ -0,0 +1,555 @@
|
||||
|
||||
import {
|
||||
greenColumn,
|
||||
whiteColumn,
|
||||
orangeColumn,
|
||||
yellowColumn,
|
||||
redColumn,
|
||||
blueColumn,
|
||||
} from '../columnColors';
|
||||
export const config = {
|
||||
"config":{
|
||||
"colors":{
|
||||
"kod_razdela_kod_razdela":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"kod_razdela_kod_razdela"
|
||||
},
|
||||
"id_stati_id_stati":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_stati_id_stati"
|
||||
},
|
||||
"id_gruppy_nomenklatury_id_gruppy_nomenklatury":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury"
|
||||
},
|
||||
"naimenovanie_naimenovanie":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"naimenovanie_naimenovanie"
|
||||
},
|
||||
"korrektirovka_limita_po_statyam_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"korrektirovka_limita_po_statyam_smety"
|
||||
},
|
||||
"field_uvelichenie_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_uvelichenie_smety"
|
||||
},
|
||||
"field_za_yanvar":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_yanvar"
|
||||
},
|
||||
"field_za_fevral":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_fevral"
|
||||
},
|
||||
"field_itogo_i_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_i_kvartal"
|
||||
},
|
||||
"field_za_aprel":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_aprel"
|
||||
},
|
||||
"field_za_may":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_may"
|
||||
},
|
||||
"field_itogo_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_ii_kvartal"
|
||||
},
|
||||
"field_za_iyul":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_iyul"
|
||||
},
|
||||
"field_za_avgust":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_avgust"
|
||||
},
|
||||
"field_itogo_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iii_kvartal"
|
||||
},
|
||||
"field_za_oktyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_oktyabr"
|
||||
},
|
||||
"field_za_noyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_noyabr"
|
||||
},
|
||||
"field_spod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_spod"
|
||||
},
|
||||
"field_itogo_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iv_kvartal"
|
||||
},
|
||||
"itogo_itogo":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"itogo_itogo"
|
||||
},
|
||||
"fakt_zamart":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zamart"
|
||||
},
|
||||
"fakt_zaiyun":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zaiyun"
|
||||
},
|
||||
"fakt_zasentyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zasentyabr"
|
||||
},
|
||||
"fakt_zadekabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zadekabr"
|
||||
},
|
||||
"ekonomiya_pereraskhod_ekonomiya_pereraskhod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal"
|
||||
}
|
||||
},
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela_kod_razdela",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati_id_stati",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie_naimenovanie",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Корректировка лимита",
|
||||
"accessorKey":"korrektirovka_limita",
|
||||
"columns":[
|
||||
{
|
||||
"header":"по статьям сметы *",
|
||||
"accessorKey":"korrektirovka_limita_po_statyam_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"",
|
||||
"accessorKey":"field",
|
||||
"columns":[
|
||||
{
|
||||
"header":"увеличение сметы **",
|
||||
"accessorKey":"field_uvelichenie_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ЯНВАРЬ",
|
||||
"accessorKey":"field_za_yanvar",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ФЕВРАЛЬ",
|
||||
"accessorKey":"field_za_fevral",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого I квартал",
|
||||
"accessorKey":"field_itogo_i_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АПРЕЛЬ",
|
||||
"accessorKey":"field_za_aprel",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за МАЙ",
|
||||
"accessorKey":"field_za_may",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого II квартал",
|
||||
"accessorKey":"field_itogo_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ИЮЛЬ",
|
||||
"accessorKey":"field_za_iyul",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АВГУСТ",
|
||||
"accessorKey":"field_za_avgust",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого III квартал",
|
||||
"accessorKey":"field_itogo_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ОКТЯБРЬ",
|
||||
"accessorKey":"field_za_oktyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за НОЯБРЬ",
|
||||
"accessorKey":"field_za_noyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"СПОД",
|
||||
"accessorKey":"field_spod",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого IV квартал",
|
||||
"accessorKey":"field_itogo_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого",
|
||||
"accessorKey":"itogo",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Итого",
|
||||
"accessorKey":"itogo_itogo",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Факт",
|
||||
"accessorKey":"fakt",
|
||||
"columns":[
|
||||
{
|
||||
"header":"заМАРТ",
|
||||
"accessorKey":"fakt_zamart",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заИЮНЬ",
|
||||
"accessorKey":"fakt_zaiyun",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заСЕНТЯБРЬ",
|
||||
"accessorKey":"fakt_zasentyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заДЕКАБРЬ",
|
||||
"accessorKey":"fakt_zadekabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod",
|
||||
"columns":[
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на II квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на II квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на III квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на III квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на IV квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на IV квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
577
web/src/components/RealtimeTable/constants/FORM_3/PROJECT.js
Normal file
577
web/src/components/RealtimeTable/constants/FORM_3/PROJECT.js
Normal file
@ -0,0 +1,577 @@
|
||||
|
||||
import {
|
||||
greenColumn,
|
||||
whiteColumn,
|
||||
orangeColumn,
|
||||
yellowColumn,
|
||||
redColumn,
|
||||
blueColumn,
|
||||
} from '../columnColors';
|
||||
export const config = {
|
||||
"config":{
|
||||
"colors":{
|
||||
"kod_razdela_kod_razdela":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"kod_razdela_kod_razdela"
|
||||
},
|
||||
"id_stati_id_stati":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_stati_id_stati"
|
||||
},
|
||||
"id_gruppy_nomenklatury_id_gruppy_nomenklatury":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury"
|
||||
},
|
||||
"naimenovanie_naimenovanie":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"naimenovanie_naimenovanie"
|
||||
},
|
||||
"korrektirovka_plana_po_statyam_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"korrektirovka_plana_po_statyam_smety"
|
||||
},
|
||||
"field_uvelichenie_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_uvelichenie_smety"
|
||||
},
|
||||
"field_za_yanvar":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_yanvar"
|
||||
},
|
||||
"field_za_fevral":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_fevral"
|
||||
},
|
||||
"field_itogo_i_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_i_kvartal"
|
||||
},
|
||||
"field_za_aprel":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_aprel"
|
||||
},
|
||||
"field_za_may":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_may"
|
||||
},
|
||||
"field_itogo_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_ii_kvartal"
|
||||
},
|
||||
"field_za_iyul":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_iyul"
|
||||
},
|
||||
"field_za_avgust":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_avgust"
|
||||
},
|
||||
"field_itogo_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iii_kvartal"
|
||||
},
|
||||
"field_za_oktyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_oktyabr"
|
||||
},
|
||||
"field_za_noyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_noyabr"
|
||||
},
|
||||
"field_spod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_spod"
|
||||
},
|
||||
"field_itogo_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iv_kvartal"
|
||||
},
|
||||
"itogo_korrektirovka_itogo_korrektirovka":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"itogo_korrektirovka_itogo_korrektirovka"
|
||||
},
|
||||
"fakt_zamart":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zamart"
|
||||
},
|
||||
"fakt_zaiyun":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zaiyun"
|
||||
},
|
||||
"fakt_zasentyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zasentyabr"
|
||||
},
|
||||
"fakt_zadekabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zadekabr"
|
||||
},
|
||||
"ekonomiya_pereraskhod_ekonomiya_pereraskhod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod"
|
||||
},
|
||||
"smeta_raskhodov_po_razvitiyu_na_ii_kvartal_smeta_raskhodov_po_razvitiyu_na_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_ii_kvartal_smeta_raskhodov_po_razvitiyu_na_ii_kvartal"
|
||||
},
|
||||
"itogo_skorrek_smeta_itogo_skorrek_smeta":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"itogo_skorrek_smeta_itogo_skorrek_smeta"
|
||||
},
|
||||
"smeta_raskhodov_po_razvitiyu_na_iii_kvartal_smeta_raskhodov_po_razvitiyu_na_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iii_kvartal_smeta_raskhodov_po_razvitiyu_na_iii_kvartal"
|
||||
},
|
||||
"smeta_raskhodov_po_razvitiyu_na_iv_kvartal_smeta_raskhodov_po_razvitiyu_na_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iv_kvartal_smeta_raskhodov_po_razvitiyu_na_iv_kvartal"
|
||||
}
|
||||
},
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela_kod_razdela",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati_id_stati",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie_naimenovanie",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Корректировка плана",
|
||||
"accessorKey":"korrektirovka_plana",
|
||||
"columns":[
|
||||
{
|
||||
"header":"по статьям сметы *",
|
||||
"accessorKey":"korrektirovka_plana_po_statyam_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"",
|
||||
"accessorKey":"field",
|
||||
"columns":[
|
||||
{
|
||||
"header":"увеличение сметы **",
|
||||
"accessorKey":"field_uvelichenie_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ЯНВАРЬ",
|
||||
"accessorKey":"field_za_yanvar",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ФЕВРАЛЬ",
|
||||
"accessorKey":"field_za_fevral",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого I квартал",
|
||||
"accessorKey":"field_itogo_i_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АПРЕЛЬ",
|
||||
"accessorKey":"field_za_aprel",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за МАЙ",
|
||||
"accessorKey":"field_za_may",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого II квартал",
|
||||
"accessorKey":"field_itogo_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ИЮЛЬ",
|
||||
"accessorKey":"field_za_iyul",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АВГУСТ",
|
||||
"accessorKey":"field_za_avgust",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого III квартал",
|
||||
"accessorKey":"field_itogo_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ОКТЯБРЬ",
|
||||
"accessorKey":"field_za_oktyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за НОЯБРЬ",
|
||||
"accessorKey":"field_za_noyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"СПОД",
|
||||
"accessorKey":"field_spod",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого IV квартал",
|
||||
"accessorKey":"field_itogo_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого корректировка",
|
||||
"accessorKey":"itogo_korrektirovka",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Итого корректировка",
|
||||
"accessorKey":"itogo_korrektirovka_itogo_korrektirovka",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Факт",
|
||||
"accessorKey":"fakt",
|
||||
"columns":[
|
||||
{
|
||||
"header":"заМАРТ",
|
||||
"accessorKey":"fakt_zamart",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заИЮНЬ",
|
||||
"accessorKey":"fakt_zaiyun",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заСЕНТЯБРЬ",
|
||||
"accessorKey":"fakt_zasentyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заДЕКАБРЬ",
|
||||
"accessorKey":"fakt_zadekabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod",
|
||||
"columns":[
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Смета расходов по развитию на II квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_ii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Смета расходов по развитию на II квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_ii_kvartal_smeta_raskhodov_po_razvitiyu_na_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого скоррек. смета",
|
||||
"accessorKey":"itogo_skorrek_smeta",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Итого скоррек. смета",
|
||||
"accessorKey":"itogo_skorrek_smeta_itogo_skorrek_smeta",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Смета расходов по развитию на III квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Смета расходов по развитию на III квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iii_kvartal_smeta_raskhodov_po_razvitiyu_na_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Смета расходов по развитию на IV квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iv_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Смета расходов по развитию на IV квартал",
|
||||
"accessorKey":"smeta_raskhodov_po_razvitiyu_na_iv_kvartal_smeta_raskhodov_po_razvitiyu_na_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
555
web/src/components/RealtimeTable/constants/FORM_3/TR.js
Normal file
555
web/src/components/RealtimeTable/constants/FORM_3/TR.js
Normal file
@ -0,0 +1,555 @@
|
||||
|
||||
import {
|
||||
greenColumn,
|
||||
whiteColumn,
|
||||
orangeColumn,
|
||||
yellowColumn,
|
||||
redColumn,
|
||||
blueColumn,
|
||||
} from '../columnColors';
|
||||
export const config = {
|
||||
"config":{
|
||||
"colors":{
|
||||
"kod_razdela_kod_razdela":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"kod_razdela_kod_razdela"
|
||||
},
|
||||
"id_stati_id_stati":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_stati_id_stati"
|
||||
},
|
||||
"id_gruppy_nomenklatury_id_gruppy_nomenklatury":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury"
|
||||
},
|
||||
"naimenovanie_naimenovanie":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"naimenovanie_naimenovanie"
|
||||
},
|
||||
"korrektirovka_limita_po_statyam_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"korrektirovka_limita_po_statyam_smety"
|
||||
},
|
||||
"field_uvelichenie_smety":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_uvelichenie_smety"
|
||||
},
|
||||
"field_za_yanvar":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_yanvar"
|
||||
},
|
||||
"field_za_fevral":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_fevral"
|
||||
},
|
||||
"field_itogo_i_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_i_kvartal"
|
||||
},
|
||||
"field_za_aprel":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_aprel"
|
||||
},
|
||||
"field_za_may":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_may"
|
||||
},
|
||||
"field_itogo_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_ii_kvartal"
|
||||
},
|
||||
"field_za_iyul":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_iyul"
|
||||
},
|
||||
"field_za_avgust":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_avgust"
|
||||
},
|
||||
"field_itogo_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iii_kvartal"
|
||||
},
|
||||
"field_za_oktyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_oktyabr"
|
||||
},
|
||||
"field_za_noyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_za_noyabr"
|
||||
},
|
||||
"field_spod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_spod"
|
||||
},
|
||||
"field_itogo_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"field_itogo_iv_kvartal"
|
||||
},
|
||||
"itogo_itogo":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"itogo_itogo"
|
||||
},
|
||||
"fakt_zamart":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zamart"
|
||||
},
|
||||
"fakt_zaiyun":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zaiyun"
|
||||
},
|
||||
"fakt_zasentyabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zasentyabr"
|
||||
},
|
||||
"fakt_zadekabr":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"fakt_zadekabr"
|
||||
},
|
||||
"ekonomiya_pereraskhod_ekonomiya_pereraskhod":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal"
|
||||
},
|
||||
"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal":{
|
||||
"color_type":whiteColumn,
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal"
|
||||
}
|
||||
},
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Код раздела",
|
||||
"accessorKey":"kod_razdela_kod_razdela",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID статьи",
|
||||
"accessorKey":"id_stati_id_stati",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury",
|
||||
"columns":[
|
||||
{
|
||||
"header":"ID группы номенклатуры",
|
||||
"accessorKey":"id_gruppy_nomenklatury_id_gruppy_nomenklatury",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Наименование",
|
||||
"accessorKey":"naimenovanie_naimenovanie",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Корректировка лимита",
|
||||
"accessorKey":"korrektirovka_limita",
|
||||
"columns":[
|
||||
{
|
||||
"header":"по статьям сметы *",
|
||||
"accessorKey":"korrektirovka_limita_po_statyam_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"",
|
||||
"accessorKey":"field",
|
||||
"columns":[
|
||||
{
|
||||
"header":"увеличение сметы **",
|
||||
"accessorKey":"field_uvelichenie_smety",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ЯНВАРЬ",
|
||||
"accessorKey":"field_za_yanvar",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ФЕВРАЛЬ",
|
||||
"accessorKey":"field_za_fevral",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого I квартал",
|
||||
"accessorKey":"field_itogo_i_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АПРЕЛЬ",
|
||||
"accessorKey":"field_za_aprel",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за МАЙ",
|
||||
"accessorKey":"field_za_may",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого II квартал",
|
||||
"accessorKey":"field_itogo_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ИЮЛЬ",
|
||||
"accessorKey":"field_za_iyul",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за АВГУСТ",
|
||||
"accessorKey":"field_za_avgust",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого III квартал",
|
||||
"accessorKey":"field_itogo_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за ОКТЯБРЬ",
|
||||
"accessorKey":"field_za_oktyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"за НОЯБРЬ",
|
||||
"accessorKey":"field_za_noyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"СПОД",
|
||||
"accessorKey":"field_spod",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого IV квартал",
|
||||
"accessorKey":"field_itogo_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Итого",
|
||||
"accessorKey":"itogo",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Итого",
|
||||
"accessorKey":"itogo_itogo",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Факт",
|
||||
"accessorKey":"fakt",
|
||||
"columns":[
|
||||
{
|
||||
"header":"заМАРТ",
|
||||
"accessorKey":"fakt_zamart",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заИЮНЬ",
|
||||
"accessorKey":"fakt_zaiyun",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заСЕНТЯБРЬ",
|
||||
"accessorKey":"fakt_zasentyabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"заДЕКАБРЬ",
|
||||
"accessorKey":"fakt_zadekabr",
|
||||
"size":150,
|
||||
"filterFn":"contains",
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod",
|
||||
"columns":[
|
||||
{
|
||||
"header":"экономия (+) перерасход (-)",
|
||||
"accessorKey":"ekonomiya_pereraskhod_ekonomiya_pereraskhod",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на II квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на II квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_ii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на III квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на III квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iii_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на IV квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal",
|
||||
"columns":[
|
||||
{
|
||||
"header":"Лимит затрат / остаток лимита затрат на IV квартал",
|
||||
"accessorKey":"limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal_limit_zatrat_ostatok_limita_zatrat_na_iv_kvartal",
|
||||
"size":150,
|
||||
"filterFn":"contains"
|
||||
}
|
||||
],
|
||||
"muiTableHeadCellProps":{
|
||||
"sx":{
|
||||
"backgroundColor":"#C2D69B",
|
||||
"color":"#000000"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -25,29 +25,17 @@ export const RealtimeProvider = ({
|
||||
formId,
|
||||
sheetName,
|
||||
direction,
|
||||
year,
|
||||
userId,
|
||||
isProject = false,
|
||||
}) => {
|
||||
// Формируем URL (кастомные секреты на фронте пока недоступны, делаем через REACT_APP_ROOT_PATH)
|
||||
const wsUrl =
|
||||
`${(
|
||||
process.env.REACT_APP_API_URL ||
|
||||
process.env.REACT_APP_API_URL ||
|
||||
process.env.REACT_APP_ROOT_PATH ||
|
||||
"ws://localhost:8000"
|
||||
).replace(/^http/, "ws")}` +
|
||||
(!isProject
|
||||
? `/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`
|
||||
: `/api/v1/ws/projects/${formId}/report/${year}/${sheetName}`);
|
||||
|
||||
const wsUrl = `${(process.env.REACT_APP_API_URL || process.env.REACT_APP_API_URL || process.env.REACT_APP_ROOT_PATH || "ws://localhost:8000").replace(/^http/, "ws")}/api/v1/ws/form/${formId}/sheet/${sheetName}${direction ? `?direction=${direction}` : ""}`;
|
||||
//для ячеек которые редактируются другими пользователями
|
||||
const [lockedCells, setLockedCells] = useState([]);
|
||||
const handleMessage = useCallback((data) => {
|
||||
// Обработка ошибок
|
||||
if (data.error) {
|
||||
console.log(data.error.message);
|
||||
toast.error("Server error:" + data.error.message || "");
|
||||
console.log(data.error.message)
|
||||
toast.error("Server error:" + data.error.message || '');
|
||||
onErrorRef.current?.(data.error);
|
||||
return;
|
||||
}
|
||||
@ -93,7 +81,7 @@ export const RealtimeProvider = ({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const { isConnectedRef, isConnected, sendMessage, disconnect, lastError } = useWebSocket(
|
||||
const { isConnected, sendMessage, disconnect, lastError } = useWebSocket(
|
||||
wsUrl,
|
||||
handleMessage,
|
||||
);
|
||||
@ -157,26 +145,13 @@ export const RealtimeProvider = ({
|
||||
}
|
||||
}, [isConnected]);
|
||||
|
||||
const addCommonField = (message) => {
|
||||
if (isProject) {
|
||||
message.report_type = sheetName;
|
||||
message.project_id = formId;
|
||||
message.year = year;
|
||||
} else {
|
||||
message.sheet = sheetName;
|
||||
message.form_id = formId;
|
||||
message.direction = direction;
|
||||
}
|
||||
return message;
|
||||
};
|
||||
|
||||
// Функция для начала редактирования ячейки
|
||||
const startEditing = useCallback(
|
||||
async (row, column) => {
|
||||
const columnId = column.id;
|
||||
const rowId = row.id;
|
||||
|
||||
if (!isConnectedRef) {
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("WebSocket не подключен");
|
||||
return false;
|
||||
}
|
||||
@ -208,7 +183,7 @@ export const RealtimeProvider = ({
|
||||
const columnId = column.id;
|
||||
const rowId = row.id;
|
||||
|
||||
if (!isConnectedRef) {
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("WebSocket не подключен");
|
||||
return false;
|
||||
}
|
||||
@ -238,7 +213,7 @@ export const RealtimeProvider = ({
|
||||
async (row, column, value) => {
|
||||
const columnId = column.id;
|
||||
const rowId = row.id;
|
||||
if (!isConnectedRef) {
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("WebSocket не подключен");
|
||||
return false;
|
||||
}
|
||||
@ -266,7 +241,7 @@ export const RealtimeProvider = ({
|
||||
|
||||
const addRow = useCallback(
|
||||
async (rowData) => {
|
||||
if (!isConnectedRef) {
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("Нет подключения или авторизации");
|
||||
return false;
|
||||
}
|
||||
@ -274,9 +249,10 @@ export const RealtimeProvider = ({
|
||||
const message = {
|
||||
event: "row_added",
|
||||
data: rowData,
|
||||
sheet: sheetName,
|
||||
form_id: formId,
|
||||
direction: direction,
|
||||
};
|
||||
addCommonField(message);
|
||||
|
||||
return sendMessage(message);
|
||||
},
|
||||
[isConnected, sendMessage, sheetName, formId, direction],
|
||||
@ -284,7 +260,7 @@ export const RealtimeProvider = ({
|
||||
|
||||
const deleteRow = useCallback(
|
||||
async (rowId) => {
|
||||
if (!isConnectedRef) {
|
||||
if (!isConnected) {
|
||||
onErrorRef.current?.("Нет подключения или авторизации");
|
||||
return false;
|
||||
}
|
||||
@ -292,8 +268,10 @@ export const RealtimeProvider = ({
|
||||
const message = {
|
||||
event: "row_deleted",
|
||||
data: { row_id: rowId },
|
||||
sheet: sheetName,
|
||||
form_id: formId,
|
||||
direction: direction,
|
||||
};
|
||||
addCommonField(message);
|
||||
|
||||
return sendMessage(message);
|
||||
},
|
||||
|
||||
@ -4,9 +4,8 @@ import { useRealtime } from "../contexts/RealtimeContext";
|
||||
import { deleteRow, insertCell, updateCells } from "../utils/cellUtils";
|
||||
import { toast } from "react-toastify";
|
||||
import { isVspNewRow, isVspRow } from "../utils/rowUtils";
|
||||
import { ProjectsApi } from "../../../api/projects";
|
||||
|
||||
const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
||||
const useRealtimeData = (formId, sheetName, direction) => {
|
||||
const [data, setData] = useState([]);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
@ -78,10 +77,7 @@ const useRealtimeData = (formId, sheetName, direction, formType, year) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
let res;
|
||||
|
||||
if(formType == 'PROJECT'){
|
||||
res = await ProjectsApi.getTableData(formId, sheetName, year)
|
||||
} else if (direction !== null) {
|
||||
if (direction !== null) {
|
||||
res = await FormsSheetApi.get(formId, sheetName, {
|
||||
params: { direction: direction },
|
||||
});
|
||||
|
||||
@ -107,16 +107,30 @@ export const useWebSocket = (url, onMessage) => {
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
const login = useCallback(
|
||||
(token) => {
|
||||
if (!token) {
|
||||
console.warn("No token provided for login");
|
||||
return false;
|
||||
}
|
||||
return sendMessage({
|
||||
event: "user_login",
|
||||
data: { token },
|
||||
});
|
||||
},
|
||||
[sendMessage],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
connect();
|
||||
return () => disconnect();
|
||||
}, [connect, disconnect]);
|
||||
|
||||
return {
|
||||
isConnectedRef,
|
||||
isConnected,
|
||||
isConnected: isConnectedRef,
|
||||
error,
|
||||
sendMessage,
|
||||
login,
|
||||
disconnect,
|
||||
};
|
||||
};
|
||||
|
||||
@ -112,7 +112,6 @@ export const getTableColumns = ({
|
||||
globalFilter={
|
||||
table.getState().globalFilter
|
||||
}
|
||||
columnFilter={table.getState().columnFilters?.find(f => f.id === column.id)?.value}
|
||||
isEditable={row.original?.row_type === 'INPUT' || false}
|
||||
backgroundColor={columnColors[column.id]?.color_type?.[row.original?.row_type || row.row_type]}
|
||||
color={columnColors[column.id]?.color_type?.['COLOR']}
|
||||
|
||||
@ -25,7 +25,7 @@ import { StageStatusChip } from './StageStatusChip';
|
||||
import { formatDateForApi } from './utils';
|
||||
import CollapsibleTree from '../common/CollapsibleTree';
|
||||
import { collectLeafColumns } from '../RealtimeTable/utils/columnUtils';
|
||||
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants/constants';
|
||||
import { STAGE_ROLE_RUSSIAN_NAME } from '../../constants';
|
||||
|
||||
export const AddStagesModal = ({
|
||||
isOpen,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Box, Divider, Stack } from '@mui/material';
|
||||
import { UnLockedSvg } from '../common/icons/icons';
|
||||
import { ROLES_ID_RUSSIAN_NAME, EDIT_ACCESS_RUSSIAN } from '../../constants/constants';
|
||||
import { ROLES_ID_RUSSIAN_NAME, EDIT_ACCESS_RUSSIAN } from '../../constants';
|
||||
|
||||
export const StageInfoForTooltip = ({ stageData = [] }) => {
|
||||
const accessData = stageData[0] || [];
|
||||
|
||||
@ -20,7 +20,7 @@ import {
|
||||
import { getStageStatus, getStatusColors } from './utils';
|
||||
import { formatDate } from '../../../utils/formatDate';
|
||||
import { StageStatusChip } from '../StageStatusChip';
|
||||
import { ROLES_NAME_ID, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../constants';
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
|
||||
export const StagesTable = ({ stages, onEdit, onDelete }) => {
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
// components/NavigationToggle.jsx
|
||||
import React from 'react';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { PrimaryOutlinedButton } from './Buttons/Buttons'; // Ваш компонент кнопки
|
||||
|
||||
const NavigationToggle = ({ currentView, onViewChange }) => {
|
||||
const handleNavigateToTasks = () => {
|
||||
onViewChange('/tasks');
|
||||
};
|
||||
|
||||
const handleNavigateToProjects = () => {
|
||||
onViewChange('/projects');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
||||
{currentView === 'tasks' ? (
|
||||
<PrimaryOutlinedButton onClick={handleNavigateToProjects}>
|
||||
← К проектам
|
||||
</PrimaryOutlinedButton>
|
||||
) : (
|
||||
<PrimaryOutlinedButton onClick={handleNavigateToTasks}>
|
||||
← К задачам
|
||||
</PrimaryOutlinedButton>
|
||||
)}
|
||||
<Typography variant="h6" color="primary" sx={{ fontWeight: 200, fontSize: '1rem' }}>
|
||||
{currentView === 'tasks' ? 'Управление задачами' : 'Управление проектами'}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationToggle;
|
||||
@ -1,257 +0,0 @@
|
||||
// src/components/common/PaginatedList/PaginatedList.jsx
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Pagination,
|
||||
PaginationItem,
|
||||
Select,
|
||||
MenuItem,
|
||||
Typography,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import { CardsSkeleton } from './Skeleton';
|
||||
import {
|
||||
ExportExitButton,
|
||||
ExportWithTextButton,
|
||||
} from './Buttons/ButtonsActions';
|
||||
import { exportBulkForms, exportSingleForm } from '../../utils/exportFile';
|
||||
|
||||
/**
|
||||
* Универсальный компонент для отображения списка с пагинацией и экспортом
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Array} props.items - Массив элементов для отображения
|
||||
* @param {boolean} props.loading - Состояние загрузки
|
||||
* @param {number} props.totalCount - Общее количество элементов
|
||||
* @param {number} props.page - Текущая страница
|
||||
* @param {number} props.limit - Количество элементов на странице
|
||||
* @param {Function} props.onPageChange - Обработчик изменения страницы
|
||||
* @param {Function} props.onLimitChange - Обработчик изменения лимита
|
||||
* @param {Function} props.renderItem - Функция рендеринга элемента
|
||||
* @param {Function} props.getItemId - Функция получения ID элемента
|
||||
* @param {Function} props.getItemTitle - Функция получения заголовка элемента
|
||||
* @param {string} props.entityName - Название сущности (для текстов)
|
||||
* @param {string} props.emptyMessage - Сообщение при пустом списке
|
||||
* @param {number} props.skeletonCount - Количество силуэтов таблиц при загрузки
|
||||
* @param {Object} props.extraActions - Дополнительные действия справа
|
||||
* @param {boolean} props.enableExport - Включить экспорт
|
||||
* @param {Array} props.limitOptions - Опции для выбора лимита
|
||||
* @param {number} props.maxHeight - Максимальная высота контейнера со скроллом
|
||||
* @param {boolean} props.enableScroll - Включить скролл для списка
|
||||
* @param {boolean} props.filterActions - Элементы фильтрации
|
||||
*/
|
||||
export function PaginatedList({
|
||||
items = [],
|
||||
loading = false,
|
||||
totalCount = 0,
|
||||
page = 1,
|
||||
limit = 20,
|
||||
onPageChange,
|
||||
onLimitChange,
|
||||
renderItem,
|
||||
getItemId,
|
||||
getItemTitle = (item) => `Элемент ${getItemId(item)}`,
|
||||
entityName = 'элементов',
|
||||
emptyMessage = 'Элементы не найдены',
|
||||
skeletonCount = 20,
|
||||
extraActions = null,
|
||||
enableExport = true,
|
||||
limitOptions = [10, 20, 50, 100, 200],
|
||||
maxHeight = 'calc(100vh - 300px)',
|
||||
enableScroll = true,
|
||||
filterActions = null,
|
||||
}) {
|
||||
const [exportMode, setExportMode] = useState(false);
|
||||
const [exportList, setExportList] = useState([]);
|
||||
|
||||
// Сброс списка экспорта при переключении режима
|
||||
useEffect(() => {
|
||||
setExportList([]);
|
||||
}, [exportMode]);
|
||||
|
||||
const handleAddToExport = (itemId, checked) => {
|
||||
if (checked) {
|
||||
setExportList((prev) => [...prev, itemId]);
|
||||
} else {
|
||||
setExportList((prev) => prev.filter((id) => id !== itemId));
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkExport = async () => {
|
||||
await exportBulkForms(exportList);
|
||||
setExportMode(false);
|
||||
};
|
||||
|
||||
const handleSingleExport = (itemId) => {
|
||||
const item = items.find((item) => getItemId(item) === itemId);
|
||||
const title = item ? getItemTitle(item) : `Элемент ${itemId}`;
|
||||
exportSingleForm(itemId, title);
|
||||
};
|
||||
|
||||
// Если включен экспорт, передаем его пропсы в дочерние элементы
|
||||
const renderItemWithExport = (item) => {
|
||||
if (enableExport) {
|
||||
return renderItem(item, {
|
||||
exportMode,
|
||||
onAddForExport: (checked) => {
|
||||
handleAddToExport(getItemId(item), checked);
|
||||
},
|
||||
onExportClick: () => {
|
||||
handleSingleExport(getItemId(item));
|
||||
},
|
||||
isSelected: exportList.includes(getItemId(item)),
|
||||
});
|
||||
}
|
||||
return renderItem(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }}>
|
||||
{/* Верхняя панель с экспортом и дополнительными действиями */}
|
||||
{enableExport && (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<div>
|
||||
{filterActions}
|
||||
</div>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: '1.5rem' }}>
|
||||
{exportMode && (
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Выделено {entityName}: {exportList.length}
|
||||
</Typography>
|
||||
)}
|
||||
<Box sx={{ flex: 1 }} />
|
||||
<Stack direction="row" spacing="0.5rem">
|
||||
{!exportMode ? (
|
||||
<>
|
||||
<ExportWithTextButton
|
||||
text="Пакетный экспорт"
|
||||
onClick={() => setExportMode(true)}
|
||||
height='2.5rem'
|
||||
/>
|
||||
{extraActions}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ExportWithTextButton
|
||||
text="Экспортировать"
|
||||
onClick={handleBulkExport}
|
||||
disabled={exportList.length === 0}
|
||||
height='2.5rem'
|
||||
/>
|
||||
<ExportExitButton onClick={() => setExportMode(false)} />
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
)}
|
||||
|
||||
{/* Контейнер со скроллом для списка */}
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
overflow: enableScroll ? 'auto' : 'visible',
|
||||
maxHeight: enableScroll ? maxHeight : 'none',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '8px',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
background: '#f1f1f1',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
background: '#888',
|
||||
borderRadius: '4px',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
background: '#555',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* Список элементов */}
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
|
||||
{loading ? (
|
||||
<CardsSkeleton count={skeletonCount} />
|
||||
) : items.length === 0 ? (
|
||||
<Box sx={{ textAlign: 'center', py: 4, width: '100%' }}>
|
||||
<Typography variant="body1" color="textSecondary">
|
||||
{emptyMessage}
|
||||
</Typography>
|
||||
</Box>
|
||||
) : (
|
||||
items.map((item) => renderItemWithExport(item))
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Пагинация */}
|
||||
{!loading && items.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
pt: 2,
|
||||
pb: 2,
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
flexShrink: 0,
|
||||
mt: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Показать на странице:
|
||||
</Typography>
|
||||
<Select
|
||||
value={limit}
|
||||
onChange={onLimitChange}
|
||||
size="small"
|
||||
sx={{ minWidth: 70 }}
|
||||
disabled={loading}
|
||||
>
|
||||
{limitOptions.map((option) => (
|
||||
<MenuItem key={option} value={option}>
|
||||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Всего: {totalCount}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Pagination
|
||||
count={Math.ceil(totalCount / limit)}
|
||||
page={page}
|
||||
onChange={onPageChange}
|
||||
color="primary"
|
||||
size="large"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
disabled={loading}
|
||||
renderItem={(item) => (
|
||||
<PaginationItem
|
||||
{...item}
|
||||
sx={{
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'white',
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary.dark',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box sx={{ width: 120 }} />
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { EDIT_ACCESS_RUSSIAN, ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
||||
import { EDIT_ACCESS_RUSSIAN, ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||
import {
|
||||
SelectWrapper,
|
||||
SelectContainer,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
import { Switch, Buttons, StyledNavLink } from './SwitchFormTask.style';
|
||||
|
||||
export function SwitchFormTask() {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_OPTIONS } from '../../../constants/constants';
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_OPTIONS } from '../../../constants';
|
||||
import {
|
||||
Section,
|
||||
SectionStartPart,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import TableCard from '../TableCard/TableCard';
|
||||
@ -6,10 +6,10 @@ import { toast } from 'react-toastify';
|
||||
|
||||
const TablesList = ({
|
||||
filteredTables,
|
||||
projects,
|
||||
query,
|
||||
basePath = '/table',
|
||||
formInfo,
|
||||
isProject = false,
|
||||
formInfo
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -22,7 +22,7 @@ const TablesList = ({
|
||||
if (!formInfo){
|
||||
return;
|
||||
}
|
||||
const path = `${basePath}/form/${formInfo.id}/form-type/${isProject ? 'PROJECT' : formInfo.form_type_code}/${table.sheet}/${table?.direction}/${table?.year}`;
|
||||
const path = `${basePath}/form/${formInfo.id}/form-type/${formInfo.form_type_code}/${table.sheet}/${table?.direction}`;
|
||||
|
||||
return (
|
||||
<TableCard
|
||||
|
||||
@ -26,7 +26,7 @@ import { formatDate } from '../../utils/formatDate';
|
||||
import { IconWithContent } from './IconWithContent';
|
||||
import { Chip } from '../styles/StyledChip';
|
||||
import { ExportButton } from '../common/Buttons/ButtonsActions';
|
||||
import { FORM_TYPE_TRANSLATE } from '../../constants/constants';
|
||||
import { FORM_TYPE_TRANSLATE } from '../../constants';
|
||||
|
||||
const TaskCard = styled.div`
|
||||
width: 26.8rem;
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
export const DEFAULT_PROJECT_CONFIG = {
|
||||
'ССП/РФ': {
|
||||
type: 'multiselect',
|
||||
options: [],
|
||||
placeholder: 'Выберите ССП/РФ',
|
||||
defaultValue: null,
|
||||
required_field: true,
|
||||
},
|
||||
'Год': {
|
||||
type: 'number',
|
||||
placeholder: 'Введите год',
|
||||
defaultValue: null,
|
||||
min: 0,
|
||||
step: 1,
|
||||
required_field: true,
|
||||
},
|
||||
'Тип проекта развития': {
|
||||
type: 'multiselect',
|
||||
options: [
|
||||
'Открытие ВСП',
|
||||
'Закрытие ВСП',
|
||||
'Переезд ВСП',
|
||||
'Реновация РФ',
|
||||
'Открытие УРМ',
|
||||
],
|
||||
placeholder: 'Выберите тип проекта',
|
||||
defaultValue: null,
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
'Название проекта': {
|
||||
type: 'text',
|
||||
placeholder: 'Введите название проекта',
|
||||
defaultValue: null,
|
||||
required_field: true,
|
||||
},
|
||||
'Формат ВСП': {
|
||||
type: 'multiselect',
|
||||
options: [
|
||||
'Фланговый',
|
||||
'Типовой',
|
||||
'Розничный',
|
||||
'МСБ',
|
||||
'Лёгкий',
|
||||
'Мини',
|
||||
'Розничный-киоск',
|
||||
'МБО',
|
||||
'Офис самообслуживания',
|
||||
'Другое',
|
||||
],
|
||||
placeholder: 'Выберите формат ВСП',
|
||||
defaultValue: null,
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
'Адрес объекта': {
|
||||
type: 'text',
|
||||
placeholder: 'Введите адрес объекта',
|
||||
defaultValue: '',
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
'Целевая численность, чел.': {
|
||||
type: 'number',
|
||||
placeholder: 'Введите число',
|
||||
defaultValue: 0,
|
||||
min: 0,
|
||||
step: 1,
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
'Тип размещения': {
|
||||
type: 'multiselect',
|
||||
options: ['Собственность', 'Аренда', 'Субаренда'],
|
||||
placeholder: 'Выберите тип размещения',
|
||||
defaultValue: null,
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
'Площадь размещения, м2': {
|
||||
type: 'number',
|
||||
placeholder: 'Введите число',
|
||||
defaultValue: 0,
|
||||
min: 0,
|
||||
step: 1,
|
||||
required_field: false,
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { PageContainer } from '../../StyledForPage';
|
||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch.jsx';
|
||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
import { Box } from '@mui/material';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Box, Typography, Link, Chip } from '@mui/material';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../../constants/constants';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../../constants';
|
||||
import {
|
||||
ENTITY_RUSSIAN_NAMES,
|
||||
getRussianNamesForType,
|
||||
|
||||
@ -82,15 +82,15 @@ export const EVENT_CONFIGS = {
|
||||
},
|
||||
ACCESS_EXTEND: {
|
||||
form_id: 'Информация о задаче',
|
||||
closes_at_after: 'Дата закрытия (новая)',
|
||||
closes_at_before: 'Дата закрытия (старая)',
|
||||
closes_at_after: 'Дата закрытия',
|
||||
closes_at_before: 'Дата открытия',
|
||||
phase_name: 'Названия этапа',
|
||||
sheet: "Лист",
|
||||
},
|
||||
ACCESS_WINDOW_CHANGE: {
|
||||
form_id: 'Информация о задаче',
|
||||
closes_at: 'Дата закрытия',
|
||||
opens_at: 'Дата открытия',
|
||||
open_at: 'Дата открытия',
|
||||
phase_name: 'Названия этапа',
|
||||
role: 'Для роли',
|
||||
column_keys: 'Колонки в этапе',
|
||||
|
||||
@ -4,7 +4,7 @@ import { FormsApi } from '../../../../api/forms';
|
||||
import { SspApi } from '../../../../api/ssp';
|
||||
import { TasksApi } from '../../../../api/tasks';
|
||||
import { UsersApi } from '../../../../api/users';
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, ROLES_ID_RUSSIAN_NAME, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../../constants/constants';
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, ROLES_ID_RUSSIAN_NAME, SHEET_NAME, STAGE_ROLE_RUSSIAN_NAME } from '../../../../constants';
|
||||
|
||||
export const VALUE_TRANSFORMERS = {
|
||||
Роль: transformRole,
|
||||
|
||||
@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react';
|
||||
import { PageContainer } from '../../StyledForPage';
|
||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
import {
|
||||
Box,
|
||||
|
||||
@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import Modal from '../../../components/common/Modal/Modal';
|
||||
import { Box, Button, Typography } from '@mui/material';
|
||||
import SelectWithOptions from '../components/SelectWithOptions';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
import { AutocompleteForChangeOptions } from '../components/AutocompleteForChangeOptions';
|
||||
|
||||
export const ModalEditUserSsp = ({
|
||||
|
||||
@ -15,7 +15,7 @@ import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
|
||||
import {
|
||||
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
||||
ROLES_ID_RUSSIAN_NAME,
|
||||
} from '../../../../constants/constants';
|
||||
} from '../../../../constants';
|
||||
import SelectWithOptions from '../../components/SelectWithOptions';
|
||||
import useAbbreviation from './hooks/useAbbreviation';
|
||||
import {
|
||||
|
||||
@ -5,12 +5,12 @@ import {
|
||||
CheckedIcon,
|
||||
IndeterminateIcon,
|
||||
} from '../../IconsForTable/icons';
|
||||
import { ROLES_NAME_ID } from '../../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../../constants';
|
||||
import { createFilterData } from '../../utils/filters';
|
||||
import {
|
||||
ROLES_ID_RUSSIAN_NAME_ARRAY as ROLES,
|
||||
ROLES_ID_NAME,
|
||||
} from '../../../../constants/constants';
|
||||
} from '../../../../constants';
|
||||
import SelectWithOptions from '../../components/SelectWithOptions';
|
||||
import RowActionsMenu from './RowActionMenu';
|
||||
import DepartmentCell from './DepartmentCell';
|
||||
|
||||
@ -12,7 +12,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import { CheckIcon, CheckedIcon } from '../IconsForTable/icons';
|
||||
import { TrashSvg } from '../../../components/common/icons/icons';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
|
||||
const StyleForChecked = {
|
||||
display: 'flex',
|
||||
|
||||
@ -6,7 +6,7 @@ import { Button, Stack } from '@mui/material';
|
||||
import Select from '@mui/material/Select';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||
import { UserIcon, TrashSvg } from '../../../components/common/icons/icons';
|
||||
import {
|
||||
DangerOutlinedButton,
|
||||
|
||||
@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
|
||||
import { PageContainer } from '../../StyledForPage';
|
||||
import { HeaderSwitch } from '../../../components/HeaderMenu/HeaderSwitch';
|
||||
import { SwitchUserSsp } from '../../../components/AdminPanel/SwitchUserSsp';
|
||||
import { ROLES_ID_RUSSIAN_NAME_ARRAY, ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_ID_RUSSIAN_NAME_ARRAY, ROLES_NAME_ID } from '../../../constants';
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
import { Box, TextField, Autocomplete, FormLabel } from '@mui/material';
|
||||
import SearchComponent from '../../../components/common/SearchComponent';
|
||||
@ -10,7 +10,7 @@ import { toast } from 'react-toastify';
|
||||
import { SspApi } from '../../../api/ssp';
|
||||
import { UsersApi } from '../../../api/users';
|
||||
import UserTable from './UserTable/UserTable';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants/constants';
|
||||
import { ROLES_ID_RUSSIAN_NAME } from '../../../constants';
|
||||
import { ModalEditUserSsp } from './ModalEditUserSsp';
|
||||
import { ModalAddUsers } from './ModalAddUsers';
|
||||
import UsersActionBar from './UsersActionBar';
|
||||
|
||||
@ -13,7 +13,7 @@ import TableDictVsp from './components/TableDictVsp';
|
||||
import { PageContainer } from '../StyledForPage';
|
||||
import { SspApi } from '../../api/ssp';
|
||||
import { useAuth } from '../../app/context/AuthProvider';
|
||||
import { ROLES_NAME_ID } from '../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../constants';
|
||||
import DictVspFilters from './components/DictVspFilters';
|
||||
|
||||
const DictVspInfo = () => {
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
PrimaryButton,
|
||||
} from '../../../../components/common/Buttons/Buttons';
|
||||
import { useAuth } from '../../../../app/context/AuthProvider';
|
||||
import { ROLES_NAME_ID } from '../../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../../constants';
|
||||
import {
|
||||
ModalContainer,
|
||||
FieldContainer,
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
PrimaryOutlinedButton,
|
||||
} from '../../../components/common/Buttons/Buttons';
|
||||
import { useAuth } from '../../../app/context/AuthProvider';
|
||||
import { ROLES_NAME_ID } from '../../../constants/constants';
|
||||
import { ROLES_NAME_ID } from '../../../constants';
|
||||
import ModalExport from './Modals/ModalExport';
|
||||
|
||||
const TableDictVsp = ({
|
||||
|
||||
@ -8,13 +8,12 @@ import { useParams } from 'react-router-dom';
|
||||
import { RealtimeProvider } from '../components/RealtimeTable/contexts/RealtimeContext';
|
||||
import { useAuth } from '../app/context/AuthProvider';
|
||||
import { BackButton } from '../components/common/Buttons/BackButton';
|
||||
import { SHEET_NAME } from '../constants/constants';
|
||||
import { SHEET_NAME } from '../constants';
|
||||
|
||||
const TableInfo = ({ formId, sheetName, isProject = false }) => {
|
||||
const path = (isProject ? `/project/` : '/task/') + formId;
|
||||
const TableInfo = ({ formId, sheetName }) => {
|
||||
const portalContent = (
|
||||
<TaskInfoContainer>
|
||||
<BackButton to={path} />
|
||||
<BackButton to={`/task/${formId}`} />
|
||||
{sheetName && (
|
||||
<NameTask>{SHEET_NAME[sheetName] || sheetName}</NameTask>
|
||||
)}
|
||||
@ -29,29 +28,20 @@ const TableInfo = ({ formId, sheetName, isProject = false }) => {
|
||||
};
|
||||
|
||||
export default function NewTablePage() {
|
||||
const { formId, formType, sheetName, direction, year } = useParams();
|
||||
const { formId, formType, sheetName, direction } = useParams();
|
||||
const { user } = useAuth();
|
||||
const isProject = formType == 'PROJECT';
|
||||
|
||||
return (
|
||||
<>
|
||||
{formId && <TableInfo formId={formId} sheetName={sheetName} isProject={isProject} />}
|
||||
{formId && <TableInfo formId={formId} sheetName={sheetName} />}
|
||||
{user && (
|
||||
<RealtimeProvider
|
||||
formId={formId}
|
||||
sheetName={sheetName}
|
||||
userId={user.id}
|
||||
direction={direction === 'null' ? null : direction}
|
||||
isProject={isProject}
|
||||
year={year}
|
||||
>
|
||||
<RealtimeTable
|
||||
formType={formType}
|
||||
formId={formId}
|
||||
sheetName={sheetName}
|
||||
direction={direction === 'null' ? null : direction}
|
||||
year={year}
|
||||
/>
|
||||
<RealtimeTable formType={formType} formId={formId} sheetName={sheetName} direction={direction === 'null' ? null : direction} />
|
||||
</RealtimeProvider>
|
||||
)}
|
||||
|
||||
|
||||
@ -1,307 +0,0 @@
|
||||
// src/components/common/ProjectCardComponent/ModalCreateProject.jsx
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Box, Button, Divider, TextField, Typography } from '@mui/material';
|
||||
import SelectWithOptions from '../AdminPanel/components/SelectWithOptions';
|
||||
import Modal from '../../components/common/Modal/Modal';
|
||||
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
||||
|
||||
export const ModalCreateProject = ({
|
||||
open,
|
||||
onClose,
|
||||
onCreate,
|
||||
config = {},
|
||||
initialData = null,
|
||||
isEdit = false,
|
||||
projectId = null,
|
||||
}) => {
|
||||
const [projectData, setProjectData] = useState(() => {
|
||||
// Если есть initialData, используем её, иначе создаем из конфига
|
||||
if (initialData) {
|
||||
return { ...initialData };
|
||||
}
|
||||
const initial = {};
|
||||
Object.keys(config).forEach((key) => {
|
||||
initial[key] = config[key].defaultValue;
|
||||
});
|
||||
return initial;
|
||||
});
|
||||
|
||||
const [errors, setErrors] = useState({});
|
||||
|
||||
// Обновляем данные при изменении initialData
|
||||
useEffect(() => {
|
||||
if (initialData) {
|
||||
setProjectData({ ...initialData });
|
||||
} else {
|
||||
const initial = {};
|
||||
Object.keys(config).forEach((key) => {
|
||||
initial[key] = config[key].defaultValue;
|
||||
});
|
||||
setProjectData(initial);
|
||||
}
|
||||
setErrors({});
|
||||
}, [initialData, config]);
|
||||
|
||||
const handleChange = (field, value) => {
|
||||
setProjectData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
// Очищаем ошибку для поля при изменении
|
||||
if (errors[field]) {
|
||||
setErrors((prev) => ({
|
||||
...prev,
|
||||
[field]: undefined,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const validateField = (fieldName, config) => {
|
||||
if (config.required_field) {
|
||||
const value = projectData[fieldName];
|
||||
|
||||
// Проверка для разных типов полей
|
||||
if (config.type === 'multiselect') {
|
||||
if (!value || (Array.isArray(value) && value.length === 0) || value === '') {
|
||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
||||
}
|
||||
} else if (config.type === 'text') {
|
||||
if (!value || value.trim() === '') {
|
||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
||||
}
|
||||
} else if (config.type === 'number') {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return `Поле "${fieldName}" обязательно для заполнения`;
|
||||
}
|
||||
// Проверка на минимальное значение
|
||||
if (config.min !== undefined && Number(value) < config.min) {
|
||||
return `Значение должно быть не меньше ${config.min}`;
|
||||
}
|
||||
// Проверка на максимальное значение
|
||||
if (config.max !== undefined && Number(value) > config.max) {
|
||||
return `Значение должно быть не больше ${config.max}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const validateAllFields = () => {
|
||||
const newErrors = {};
|
||||
let hasErrors = false;
|
||||
|
||||
Object.entries(config).forEach(([fieldName, fieldConfig]) => {
|
||||
const error = validateField(fieldName, fieldConfig);
|
||||
if (error) {
|
||||
newErrors[fieldName] = error;
|
||||
hasErrors = true;
|
||||
}
|
||||
});
|
||||
|
||||
setErrors(newErrors);
|
||||
return !hasErrors;
|
||||
};
|
||||
|
||||
const handleClickCreate = () => {
|
||||
if (validateAllFields()) {
|
||||
onCreate(projectData);
|
||||
// Не закрываем модалку здесь, пусть это делает родительский компонент
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
const resetData = {};
|
||||
Object.keys(config).forEach((key) => {
|
||||
resetData[key] = config[key].defaultValue;
|
||||
});
|
||||
setProjectData(resetData);
|
||||
setErrors({});
|
||||
onClose();
|
||||
};
|
||||
|
||||
const renderField = (fieldName, config) => {
|
||||
const hasError = !!errors[fieldName];
|
||||
const errorText = errors[fieldName];
|
||||
|
||||
switch (config.type) {
|
||||
case 'multiselect':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
paddingBottom: '.25rem',
|
||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
{config.required_field && (
|
||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
||||
)}
|
||||
</Typography>
|
||||
<SelectWithOptions
|
||||
className="sameSize"
|
||||
value={projectData[fieldName] || ''}
|
||||
options={config.options}
|
||||
placeholder={config.placeholder}
|
||||
onChange={(value) => handleChange(fieldName, value)}
|
||||
width="28.25rem"
|
||||
style={{
|
||||
borderColor: hasError ? 'var(--error-color, #d32f2f)' : undefined,
|
||||
borderWidth: hasError ? '2px' : undefined,
|
||||
}}
|
||||
/>
|
||||
{hasError && (
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'var(--error-color, #d32f2f)',
|
||||
fontSize: '0.75rem',
|
||||
marginTop: '4px',
|
||||
}}
|
||||
>
|
||||
{errorText}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'text':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
||||
paddingBottom: '.25rem',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
{config.required_field && (
|
||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
||||
)}
|
||||
</Typography>
|
||||
<TextField
|
||||
className="sameSize"
|
||||
value={projectData[fieldName] || ''}
|
||||
onChange={(e) => handleChange(fieldName, e.target.value)}
|
||||
placeholder={config.placeholder}
|
||||
size="small"
|
||||
error={hasError}
|
||||
helperText={hasError ? errorText : ''}
|
||||
FormHelperTextProps={{
|
||||
sx: {
|
||||
marginLeft: 0,
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'number':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
color: hasError ? 'var(--error-color, #d32f2f)' : 'var(--grey-dark)',
|
||||
paddingBottom: '.25rem',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
{config.required_field && (
|
||||
<span style={{ color: 'var(--error-color, #d32f2f)', marginLeft: '4px' }}>*</span>
|
||||
)}
|
||||
</Typography>
|
||||
<TextField
|
||||
className="sameSize"
|
||||
type="text"
|
||||
value={projectData[fieldName] || ''}
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value;
|
||||
//только числа
|
||||
const isValidInput =
|
||||
/^\d*\.?\d*$/.test(inputValue) || inputValue === '';
|
||||
if (isValidInput) {
|
||||
handleChange(fieldName, inputValue);
|
||||
}
|
||||
}}
|
||||
placeholder={config.placeholder}
|
||||
size="small"
|
||||
error={hasError}
|
||||
helperText={hasError ? errorText : ''}
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: config.min,
|
||||
max: config.max || null,
|
||||
step: config.step || 1,
|
||||
},
|
||||
}}
|
||||
FormHelperTextProps={{
|
||||
sx: {
|
||||
marginLeft: 0,
|
||||
fontSize: '0.75rem',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={isEdit ? 'Редактирование проекта' : 'Создание проекта'}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
customSize={'43.75'}
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
variant="grey"
|
||||
onClick={handleClose}
|
||||
style={{ height: '2.5rem' }}
|
||||
>
|
||||
Отменить
|
||||
</Button>
|
||||
<PrimaryButton
|
||||
variant="contained"
|
||||
onClick={handleClickCreate}
|
||||
style={{ height: '2.5rem' }}
|
||||
>
|
||||
{isEdit ? 'Сохранить' : 'Создать'}
|
||||
</PrimaryButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Divider />
|
||||
<Box
|
||||
sx={{
|
||||
m: '1.25rem 0',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1.25rem',
|
||||
'& .sameSize': {
|
||||
height: '2.69rem',
|
||||
minWidth: '39.75rem',
|
||||
},
|
||||
'& .gray-color': { color: '#4A5565' },
|
||||
}}
|
||||
>
|
||||
{Object.entries(config).map(([fieldName, config]) =>
|
||||
renderField(fieldName, config),
|
||||
)}
|
||||
</Box>
|
||||
<Divider />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@ -1,349 +0,0 @@
|
||||
// src/components/common/ProjectCardComponent/ProjectCardComponent.jsx
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Box,
|
||||
Stack,
|
||||
Chip,
|
||||
} from '@mui/material';
|
||||
import { ProjectsApi } from '../../api/projects';
|
||||
import { toast } from 'react-toastify';
|
||||
import {
|
||||
CheckBoxCheckSvg,
|
||||
CheckBoxUncheckSvg,
|
||||
DateIcon,
|
||||
Edit,
|
||||
ExportSvg,
|
||||
UserIcon,
|
||||
BuildingIcon,
|
||||
LocationIcon,
|
||||
} from '../../components/common/icons/icons';
|
||||
import { IconWithContent } from '../../components/common/IconWithContent';
|
||||
import { ExportButton } from '../../components/common/Buttons/ButtonsActions';
|
||||
import { ModalCreateProject } from './ModalCreateProject';
|
||||
|
||||
const ProjectCard = styled.div`
|
||||
width: 26.8rem;
|
||||
height: 15rem;
|
||||
display: flex;
|
||||
padding: 1.25rem;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
box-sizing: border-box;
|
||||
border: 0.06rem solid var(--Stroke, rgba(0, 0, 0, 0.1));
|
||||
border-radius: 1rem;
|
||||
box-shadow:
|
||||
0rem 2rem 4rem -2rem rgba(0, 0, 0, 0.02),
|
||||
0rem 4rem 6rem -1rem rgba(0, 0, 0, 0.02);
|
||||
background: var(--White, rgba(255, 255, 255, 1));
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
`;
|
||||
|
||||
const CardHeader = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const ProjectId = styled.div`
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
line-height: 1rem;
|
||||
letter-spacing: 0rem;
|
||||
text-align: left;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
`;
|
||||
|
||||
const CardBody = styled.div`
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.8125rem;
|
||||
justify-content: flex-end;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const ProjectTitle = styled.div`
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--Text-Primary, #333);
|
||||
line-height: 1.63;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
const StatusChip = styled(Chip)`
|
||||
font-size: 0.7rem;
|
||||
height: 1.5rem;
|
||||
background-color: ${({ status }) => {
|
||||
switch (status) {
|
||||
case 'active': return '#4caf50';
|
||||
case 'completed': return '#2196f3';
|
||||
case 'paused': return '#ff9800';
|
||||
case 'archived': return '#9e9e9e';
|
||||
default: return '#9e9e9e';
|
||||
}
|
||||
}};
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const LevelChip = styled(Chip)`
|
||||
font-size: 0.6rem;
|
||||
height: 1.25rem;
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
`;
|
||||
|
||||
const ProjectInfoGrid = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
`;
|
||||
|
||||
const InfoChip = styled(Chip)`
|
||||
font-size: 0.6rem;
|
||||
height: 1.25rem;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: rgba(0, 0, 0, 0.7);
|
||||
`;
|
||||
|
||||
const getStatusLabel = (status) => {
|
||||
const statusMap = {
|
||||
active: 'Активный',
|
||||
completed: 'Завершен',
|
||||
paused: 'Приостановлен',
|
||||
archived: 'Архивирован',
|
||||
};
|
||||
return statusMap[status] || status || 'Не указан';
|
||||
};
|
||||
|
||||
export const ProjectCardComponent = ({
|
||||
project,
|
||||
projectDataInit,
|
||||
exportMode = false,
|
||||
isLoadingFile = false,
|
||||
onAddForExport,
|
||||
onExportClick,
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
org_unit_name,
|
||||
year,
|
||||
status,
|
||||
level,
|
||||
project_type,
|
||||
vsp_format,
|
||||
placement_type,
|
||||
object_address,
|
||||
staff_count,
|
||||
total_area,
|
||||
report_count,
|
||||
parent_id,
|
||||
} = project;
|
||||
|
||||
const [isEditModalOpen, setEditModalOpen] = useState(false);
|
||||
const [currentName, setCurrentName] = useState(name || '');
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
const [editFormData, setEditFormData] = useState({});
|
||||
const [projectData, setProjectData] = useState(projectDataInit);
|
||||
|
||||
useEffect(() => {
|
||||
setIsChecked(false);
|
||||
}, [exportMode]);
|
||||
|
||||
|
||||
|
||||
// Функция для подготовки данных проекта к редактированию
|
||||
const prepareProjectDataForEdit = () => {
|
||||
return {
|
||||
'ССП/РФ': project.org_unit_id || null,
|
||||
'Год': year || null,
|
||||
'Тип проекта развития': project_type || null,
|
||||
'Название проекта': currentName,
|
||||
'Формат ВСП': vsp_format || null,
|
||||
'Адрес объекта': object_address || '',
|
||||
'Целевая численность, чел.': staff_count || 0,
|
||||
'Тип размещения': placement_type || null,
|
||||
'Площадь размещения, м2': total_area || 0,
|
||||
};
|
||||
};
|
||||
|
||||
const handleOpenEdit = (event) => {
|
||||
event.stopPropagation();
|
||||
setEditFormData(prepareProjectDataForEdit());
|
||||
setEditModalOpen(true);
|
||||
};
|
||||
|
||||
const handleCloseEdit = () => {
|
||||
setEditModalOpen(false);
|
||||
setEditFormData({});
|
||||
};
|
||||
|
||||
const handleCheckboxClick = (event) => {
|
||||
event.stopPropagation();
|
||||
if (!isLoadingFile) {
|
||||
setIsChecked(!isChecked);
|
||||
onAddForExport(!isChecked);
|
||||
} else {
|
||||
toast.warning('Файл формируется!');
|
||||
}
|
||||
};
|
||||
|
||||
const handleExportProject = (event) => {
|
||||
event.stopPropagation();
|
||||
onExportClick?.(id, name);
|
||||
};
|
||||
|
||||
const handleNavigate = () => {
|
||||
if (!exportMode) {
|
||||
navigate(`/project/${id}`);
|
||||
}
|
||||
};
|
||||
|
||||
// Обновленная функция обновления проекта
|
||||
const updateProject = async (formData) => {
|
||||
try {
|
||||
const projectData = {
|
||||
name: formData['Название проекта'] || currentName,
|
||||
project_type: formData['Тип проекта развития'] || null,
|
||||
vsp_format: formData['Формат ВСП'] || null,
|
||||
object_address: formData['Адрес объекта'] || '',
|
||||
staff_count: Number(formData['Целевая численность, чел.']) || 0,
|
||||
placement_type: formData['Тип размещения'] || null,
|
||||
total_area: Number(formData['Площадь размещения, м2']) || 0,
|
||||
year: formData['Год'] ? Number(formData['Год']) : null,
|
||||
branch_id: formData['ССП/РФ'] || null,
|
||||
};
|
||||
|
||||
await ProjectsApi.update(id, projectData);
|
||||
|
||||
// Обновляем локальные данные
|
||||
setCurrentName(projectData.name);
|
||||
setEditModalOpen(false);
|
||||
toast.success('Данные проекта успешно обновлены');
|
||||
|
||||
// Можно добавить рефреш данных или перезагрузку страницы
|
||||
window.location.reload(); // или используйте callback для обновления списка
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error?.response?.data?.detail || 'Не удалось обновить данные проекта',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveProject = async (formData) => {
|
||||
await updateProject(formData);
|
||||
};
|
||||
|
||||
const getInitials = (name) => {
|
||||
if (!name) return '?';
|
||||
const words = name.split(' ');
|
||||
if (words.length >= 2) {
|
||||
return `${words[0][0]}${words[1][0]}`.toUpperCase();
|
||||
}
|
||||
return name.substring(0, 2).toUpperCase();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProjectCard onClick={handleNavigate} key={id}>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<CardHeader>
|
||||
<ProjectId>ID: {id}</ProjectId>
|
||||
<Stack direction={'row'} spacing={'.5rem'} sx={{
|
||||
|
||||
}} >
|
||||
{!exportMode && (
|
||||
<>
|
||||
{status && (
|
||||
<StatusChip
|
||||
status={status}
|
||||
label={getStatusLabel(status)}
|
||||
size="small"
|
||||
/>
|
||||
)}
|
||||
{/* {Пока нет редактирование + экспорт} */}
|
||||
{/* <ExportButton onClick={handleExportProject} />
|
||||
<Edit onClick={handleOpenEdit} /> */}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</CardHeader>
|
||||
|
||||
<ProjectTitle>{currentName}</ProjectTitle>
|
||||
|
||||
<ProjectInfoGrid>
|
||||
{project_type && (
|
||||
<InfoChip label={`Тип: ${project_type}`} size="small" />
|
||||
)}
|
||||
{report_count !== undefined && report_count !== null && (
|
||||
<InfoChip label={`Отчетов: ${report_count}`} size="small" />
|
||||
)}
|
||||
|
||||
</ProjectInfoGrid>
|
||||
</Box>
|
||||
|
||||
<Stack
|
||||
sx={{
|
||||
direction: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
alignItems: 'end',
|
||||
}}
|
||||
>
|
||||
<CardBody>
|
||||
<IconWithContent icon={<DateIcon />}>
|
||||
{year || 'Год не указан'}
|
||||
</IconWithContent>
|
||||
<IconWithContent icon={<UserIcon />}>
|
||||
{org_unit_name || 'Организация не указана'}
|
||||
</IconWithContent>
|
||||
</CardBody>
|
||||
|
||||
{exportMode && (
|
||||
<div>
|
||||
{isChecked ? (
|
||||
<CheckBoxCheckSvg size="1.625rem" />
|
||||
) : (
|
||||
<CheckBoxUncheckSvg size="1.625rem" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Stack>
|
||||
</ProjectCard>
|
||||
|
||||
<ModalCreateProject
|
||||
open={isEditModalOpen}
|
||||
onClose={handleCloseEdit}
|
||||
onCreate={handleSaveProject}
|
||||
config={projectData}
|
||||
initialData={editFormData}
|
||||
isEdit={true}
|
||||
projectId={id}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,298 +0,0 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { ProjectsApi } from '../../api/projects';
|
||||
import { CardsSkeleton } from '../../components/common/Skeleton';
|
||||
import SearchComponent from '../../components/common/SearchComponent';
|
||||
import { ProjectCardComponent } from './ProjectCard';
|
||||
import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch';
|
||||
import { PageContainer, TasksContainer } from '../StyledForPage';
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
Button,
|
||||
Stack,
|
||||
Pagination,
|
||||
PaginationItem,
|
||||
Select,
|
||||
MenuItem,
|
||||
Typography,
|
||||
IconButton,
|
||||
Tooltip,
|
||||
TextField,
|
||||
Autocomplete,
|
||||
} from '@mui/material';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ModalCreateProject } from './ModalCreateProject';
|
||||
import {
|
||||
ROLES_NAME_ID,
|
||||
} from '../../constants/constants';
|
||||
import { useAuth } from '../../app/context/AuthProvider';
|
||||
import { PrimaryButton, PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
|
||||
import {
|
||||
ExportExitButton,
|
||||
ExportWithTextButton,
|
||||
} from '../../components/common/Buttons/ButtonsActions';
|
||||
import { exportBulkForms, exportSingleForm } from '../../utils/exportFile';
|
||||
import { WhitePlus } from '../../components/common/icons/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { PaginatedList } from '../../components/common/PaginatedList';
|
||||
import { DEFAULT_PROJECT_CONFIG } from '../../constants/projectConfig';
|
||||
import { SspApi } from '../../api/ssp';
|
||||
import NavigationToggle from '../../components/common/NavigationToggle';
|
||||
|
||||
export { DEFAULT_PROJECT_CONFIG } from '../../constants/projectConfig';
|
||||
|
||||
export default function ProjectsPage() {
|
||||
const navigate = useNavigate();
|
||||
const [items, setItems] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
// Новые фильтры
|
||||
const [yearFilter, setYearFilter] = useState('');
|
||||
const [branchFilter, setBranchFilter] = useState('');
|
||||
|
||||
// Pagination state
|
||||
const [page, setPage] = useState(1);
|
||||
const [limit, setLimit] = useState(20);
|
||||
const [totalCount, setTotalCount] = useState(0);
|
||||
|
||||
const { user } = useAuth();
|
||||
|
||||
const [exportMode, setExportMode] = useState(false);
|
||||
const [exportList, setExportList] = useState([]);
|
||||
|
||||
const [projectData, setProjectData] = useState(DEFAULT_PROJECT_CONFIG);
|
||||
|
||||
// Получаем список годов для фильтра (можно динамически или задать диапазон)
|
||||
const yearOptions = useMemo(() => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const years = [];
|
||||
for (let year = currentYear - 5; year <= currentYear + 2; year++) {
|
||||
years.push(year);
|
||||
}
|
||||
return years;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
if (user.role_id == ROLES_NAME_ID.admin) {
|
||||
SspApi.getAll().then(data => {
|
||||
if (data.success) {
|
||||
setProjectData(prev => ({
|
||||
...prev, 'ССП/РФ': {
|
||||
...prev['ССП/РФ'],
|
||||
options: data.result
|
||||
}
|
||||
}))
|
||||
return;
|
||||
}
|
||||
toast.error('Ошибка при получении ССП/РФ');
|
||||
});
|
||||
return;
|
||||
}
|
||||
setProjectData(prev => ({
|
||||
...prev, 'ССП/РФ': {
|
||||
...prev['ССП/РФ'],
|
||||
options: user.org_units,
|
||||
}
|
||||
}))
|
||||
}, [user])
|
||||
|
||||
const openModal = () => {
|
||||
setModalOpen(true);
|
||||
};
|
||||
|
||||
const mapFormDataToApi = (formData) => {
|
||||
const apiData = {
|
||||
name: formData['Название проекта'] || '',
|
||||
project_type: formData['Тип проекта развития'] || null,
|
||||
vsp_format: formData['Формат ВСП'] || null,
|
||||
object_address: formData['Адрес объекта'] || '',
|
||||
staff_count: formData['Целевая численность, чел.'] || 0,
|
||||
placement_type: formData['Тип размещения'] || null,
|
||||
total_area: formData['Площадь размещения, м2'] || 0,
|
||||
year: formData['Год'] || null,
|
||||
branch_id: formData['ССП/РФ'] || null,
|
||||
|
||||
};
|
||||
return apiData;
|
||||
};
|
||||
|
||||
const handleCreateProject = async (formData) => {
|
||||
try {
|
||||
const apiData = mapFormDataToApi(formData);
|
||||
|
||||
await ProjectsApi.create(apiData);
|
||||
|
||||
setPage(1);
|
||||
loadProjects(1, limit);
|
||||
|
||||
setModalOpen(false);
|
||||
toast.success('Проект успешно создан');
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error.response?.data?.detail ||
|
||||
error.response?.data?.message ||
|
||||
'Ошибка при создании проекта',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const loadProjects = async (currentPage = page, currentLimit = limit) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const skip = (currentPage - 1) * currentLimit;
|
||||
const params = {
|
||||
offset: skip,
|
||||
limit: currentLimit,
|
||||
// Добавляем фильтры в параметры запроса
|
||||
...(yearFilter && { year: parseInt(yearFilter) }),
|
||||
...(branchFilter && { branch_id: parseInt(branchFilter.id) }),
|
||||
};
|
||||
|
||||
const res = await ProjectsApi.list(params);
|
||||
const data = res.result;
|
||||
|
||||
setItems(data);
|
||||
setTotalCount(res.count);
|
||||
|
||||
} catch (error) {
|
||||
toast.error('Ошибка загрузки проектов');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Сбрасываем страницу при изменении любых фильтров
|
||||
useEffect(() => {
|
||||
setPage(1);
|
||||
loadProjects(1, limit);
|
||||
}, [yearFilter, branchFilter]);
|
||||
|
||||
const handlePageChange = (event, value) => {
|
||||
setPage(value);
|
||||
loadProjects(value, limit);
|
||||
};
|
||||
|
||||
const handleLimitChange = (event) => {
|
||||
const newLimit = event.target.value;
|
||||
setLimit(newLimit);
|
||||
setPage(1);
|
||||
loadProjects(1, newLimit);
|
||||
};
|
||||
|
||||
// Сброс всех фильтров
|
||||
const handleClearFilters = () => {
|
||||
setYearFilter('');
|
||||
setBranchFilter('');
|
||||
setPage(1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadProjects(page, limit);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setExportList([]);
|
||||
}, [exportMode]);
|
||||
|
||||
const handleChangeToExportList = (projectId, checked) => {
|
||||
if (checked) {
|
||||
setExportList((prev) => [...prev, projectId]);
|
||||
} else {
|
||||
const newExportList = exportList.filter((t) => t != projectId);
|
||||
setExportList(newExportList);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBulkExport = async () => {
|
||||
await exportBulkForms(exportList);
|
||||
setExportMode(false);
|
||||
};
|
||||
|
||||
const handleSingleExport = (projectId, projectTitle) => {
|
||||
exportSingleForm(projectId, projectTitle);
|
||||
};
|
||||
|
||||
const handleRefresh = () => {
|
||||
loadProjects(page, limit);
|
||||
toast.info('Список проектов обновлен');
|
||||
};
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageContainer>
|
||||
<HeaderSwitch />
|
||||
<NavigationToggle currentView={'projects'} onViewChange={(path) => navigate(path)} />
|
||||
|
||||
<PaginatedList
|
||||
items={items}
|
||||
loading={loading}
|
||||
totalCount={totalCount}
|
||||
page={page}
|
||||
limit={limit}
|
||||
onPageChange={handlePageChange}
|
||||
onLimitChange={handleLimitChange}
|
||||
entityName="проектов"
|
||||
emptyMessage="Проекты не найдены"
|
||||
renderItem={(project, exportProps) => (
|
||||
<ProjectCardComponent
|
||||
key={project.id}
|
||||
project={project}
|
||||
projectDataInit={projectData}
|
||||
{...exportProps}
|
||||
/>
|
||||
)}
|
||||
getItemId={(project) => project.id}
|
||||
getItemTitle={(project) => project.name || 'Проект'}
|
||||
extraActions={
|
||||
user.role_id === ROLES_NAME_ID.admin && (
|
||||
<PrimaryButton onClick={openModal} startIcon={<WhitePlus />} height='2.5rem' >
|
||||
Создать проект
|
||||
</PrimaryButton>
|
||||
)
|
||||
}
|
||||
filterActions={
|
||||
<Box sx={{ display: 'flex', gap: 1.5, flexWrap: 'wrap', alignItems: 'center', height: '2.5rem' }}>
|
||||
<Select
|
||||
size="small"
|
||||
displayEmpty
|
||||
value={yearFilter}
|
||||
onChange={(e) => setYearFilter(e.target.value)}
|
||||
sx={{ width: 130 }}
|
||||
>
|
||||
<MenuItem value="">Все годы</MenuItem>
|
||||
{yearOptions.map((year) => (
|
||||
<MenuItem key={year} value={year}>{year}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<Autocomplete
|
||||
size="small"
|
||||
options={projectData['ССП/РФ']?.options ?? []}
|
||||
noOptionsText="Не найдено"
|
||||
getOptionLabel={(option) => option.title || option.name || '-'}
|
||||
value={branchFilter}
|
||||
onChange={(event, newValue) => setBranchFilter(newValue)}
|
||||
sx={{ width: 200 }}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} placeholder="Все" size="small" />
|
||||
)}
|
||||
/>
|
||||
<PrimaryOutlinedButton onClick={handleClearFilters} height="100%"> Сбросить</PrimaryOutlinedButton>
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</PageContainer>
|
||||
|
||||
<ModalCreateProject
|
||||
open={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
onCreate={handleCreateProject}
|
||||
config={projectData}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,8 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const PageContainer = styled.div`
|
||||
padding: 2rem;
|
||||
padding-top: 1rem;
|
||||
padding: 2.5rem;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@ -57,7 +57,7 @@ export default function TablePage() {
|
||||
|
||||
const connection = connectToTable(tableId, user?.id, {
|
||||
onError: () => {
|
||||
toast.error('Ошибка подключения к серверу');
|
||||
toast.error('Ошибка подключения к серверу реального времени');
|
||||
},
|
||||
onClose: (event) => {
|
||||
if (isUnmountingRef.current) {
|
||||
|
||||
183
web/src/pages/TaskPage/ModalCreateProject.jsx
Normal file
183
web/src/pages/TaskPage/ModalCreateProject.jsx
Normal file
@ -0,0 +1,183 @@
|
||||
import { useState } from 'react';
|
||||
import { Box, Button, Divider, TextField, Typography } from '@mui/material';
|
||||
import SelectWithOptions from '../AdminPanel/components/SelectWithOptions';
|
||||
import Modal from '../../components/common/Modal/Modal';
|
||||
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
||||
|
||||
export const ModalCreateProject = ({
|
||||
open,
|
||||
onClose,
|
||||
onCreate,
|
||||
config = {},
|
||||
}) => {
|
||||
const [projectData, setProjectData] = useState(() => {
|
||||
const initialData = {};
|
||||
Object.keys(config).forEach((key) => {
|
||||
initialData[key] = config[key].defaultValue;
|
||||
});
|
||||
return initialData;
|
||||
});
|
||||
|
||||
const handleChange = (field, value) => {
|
||||
setProjectData((prev) => ({
|
||||
...prev,
|
||||
[field]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleClickCreate = () => {
|
||||
onCreate(projectData);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
const resetData = {};
|
||||
Object.keys(config).forEach((key) => {
|
||||
resetData[key] = config[key].defaultValue;
|
||||
});
|
||||
setProjectData(resetData);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const renderField = (fieldName, config) => {
|
||||
switch (config.type) {
|
||||
case 'multiselect':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
paddingBottom: '.25rem',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
</Typography>
|
||||
<SelectWithOptions
|
||||
className="sameSize"
|
||||
value={projectData[fieldName] || ''}
|
||||
options={config.options}
|
||||
placeholder={config.placeholder}
|
||||
onChange={(value) => handleChange(fieldName, value)}
|
||||
width="28.25rem"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'text':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--grey-dark)',
|
||||
paddingBottom: '.25rem',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
</Typography>
|
||||
<TextField
|
||||
className="sameSize"
|
||||
value={projectData[fieldName] || ''}
|
||||
onChange={(e) => handleChange(fieldName, e.target.value)}
|
||||
placeholder={config.placeholder}
|
||||
size="small"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
case 'number':
|
||||
return (
|
||||
<Box key={fieldName}>
|
||||
<Typography
|
||||
className="gray-color"
|
||||
sx={{
|
||||
fontWeight: '400',
|
||||
fontSize: '0.75rem',
|
||||
color: 'var(--grey-dark)',
|
||||
paddingBottom: '.25rem',
|
||||
}}
|
||||
>
|
||||
{fieldName}
|
||||
</Typography>
|
||||
<TextField
|
||||
className="sameSize"
|
||||
type="text"
|
||||
value={projectData[fieldName] || ''}
|
||||
onChange={(e) => {
|
||||
const inputValue = e.target.value;
|
||||
//только числа
|
||||
const isValidInput =
|
||||
/^\d*\.?\d*$/.test(inputValue) || inputValue === '';
|
||||
if (isValidInput) {
|
||||
handleChange(fieldName, inputValue);
|
||||
}
|
||||
}}
|
||||
placeholder={config.placeholder}
|
||||
size="small"
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
min: config.min,
|
||||
max: config.max || null,
|
||||
step: config.step || 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={'Создание проекта'}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
customSize={'43.75'}
|
||||
actions={
|
||||
<>
|
||||
<Button
|
||||
variant="grey"
|
||||
onClick={handleClose}
|
||||
style={{ height: '2.5rem' }}
|
||||
>
|
||||
Отменить
|
||||
</Button>
|
||||
<PrimaryButton
|
||||
variant="contained"
|
||||
onClick={handleClickCreate}
|
||||
style={{ height: '2.5rem' }}
|
||||
>
|
||||
Создать
|
||||
</PrimaryButton>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Divider />
|
||||
<Box
|
||||
sx={{
|
||||
m: '1.25rem 0',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '1.25rem',
|
||||
'& .sameSize': {
|
||||
height: '2.69rem',
|
||||
minWidth: '39.75rem',
|
||||
},
|
||||
'& .gray-color': { color: '#4A5565' },
|
||||
}}
|
||||
>
|
||||
{Object.entries(config).map(([fieldName, config]) =>
|
||||
renderField(fieldName, config),
|
||||
)}
|
||||
</Box>
|
||||
<Divider />
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@ -3,7 +3,6 @@ import { createPortal } from 'react-dom';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { TasksApi } from '../../api/tasks';
|
||||
import { TablesApi } from '../../api/tables';
|
||||
import { ProjectsApi } from '../../api/projects'; // Добавьте импорт API для проектов
|
||||
import {
|
||||
TaskInfoContainer,
|
||||
NameTask,
|
||||
@ -19,16 +18,17 @@ import { toast } from 'react-toastify';
|
||||
import { SettingModal as SettingStageModal } from '../../components/Stages/SettingModal';
|
||||
import { PrimaryOutlinedButton } from '../../components/common/Buttons/Buttons';
|
||||
import { FormsApi } from '../../api/forms';
|
||||
import { ModalCreateProject } from './ModalCreateProject';
|
||||
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
||||
import { exportSingleForm } from '../../utils/exportFile';
|
||||
import { exportSingleForm } from '../../utils/exportForm';
|
||||
import TablesList from '../../components/common/TableList/TableList';
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants/constants';
|
||||
import { DIRECTION_TRANSLATE, FORM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants';
|
||||
|
||||
const TaskInfo = ({ task, isProject }) => {
|
||||
const TaskInfo = ({ task }) => {
|
||||
const portalContent = (
|
||||
<TaskInfoContainer>
|
||||
<BackButton to={isProject ? "/projects" : "/tasks"} />
|
||||
<NameTask>{isProject ? 'Проект' : FORM_TYPE_TRANSLATE[task.form_type_code] || ''}</NameTask>
|
||||
<BackButton to="/tasks" />
|
||||
<NameTask>{FORM_TYPE_TRANSLATE[task.form_type_code] || ''}</NameTask>
|
||||
</TaskInfoContainer>
|
||||
);
|
||||
|
||||
@ -40,13 +40,9 @@ const TaskInfo = ({ task, isProject }) => {
|
||||
};
|
||||
|
||||
export default function TaskPage() {
|
||||
const { taskId, projectId } = useParams();
|
||||
const { taskId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Определяем, что у нас за ID и какой тип
|
||||
const isProject = !!projectId;
|
||||
const id = isProject ? Number(projectId) : Number(taskId);
|
||||
|
||||
const id = Number(taskId);
|
||||
const [task, setTask] = useState(null);
|
||||
const [tables, setTables] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@ -55,42 +51,23 @@ export default function TaskPage() {
|
||||
const [modalProjectOpen, setModalProjectOpen] = useState(false);
|
||||
const [tempProjects, setTempProjects] = useState([]);
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [isProjectData, setIsProjectData] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
const getSheets = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (isProject) {
|
||||
// Получаем информацию о проекте
|
||||
const projectData = await ProjectsApi.get(id);
|
||||
setTask(projectData.result);
|
||||
setIsProjectData(true);
|
||||
|
||||
const mappedTables = projectData.result.reports.map(sheet => ({
|
||||
name: SHEET_NAME[sheet.report_type] || sheet.report_type,
|
||||
sheet: sheet.report_type,
|
||||
year: sheet.year,
|
||||
}));
|
||||
setTables(mappedTables);
|
||||
} else {
|
||||
// Получаем информацию о задаче
|
||||
const taskData = await TasksApi.getById(id);
|
||||
setTask(taskData.result);
|
||||
setIsProjectData(false);
|
||||
|
||||
// Получаем таблицы задачи
|
||||
const tablesData = await TasksApi.getSheets(id);
|
||||
const mappedTables = tablesData.result.all_sheets.map(sheet => ({
|
||||
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
||||
sheet: sheet.sheet_name,
|
||||
direction: sheet.direction,
|
||||
}));
|
||||
setTables(mappedTables.filter(t => t.sheet !== "OTCH9F"));
|
||||
}
|
||||
const data = await TasksApi.getSheets(id);
|
||||
// Преобразуем данные с учетом direction
|
||||
const mappedTables = data.result.all_sheets.map(sheet => ({
|
||||
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
||||
sheet: sheet.sheet_name,
|
||||
direction: sheet.direction,
|
||||
}));
|
||||
//пока скрыт
|
||||
setTables(mappedTables.filter(t => t.sheet !== "OTCH9F",));
|
||||
} catch (error) {
|
||||
toast.error(
|
||||
error?.response?.data?.detail || `Ошибка получения данных ${isProject ? 'проекта' : 'задачи'}`,
|
||||
error?.response?.data?.detail || 'Ошибка получения данных задачи',
|
||||
);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
@ -98,11 +75,27 @@ export default function TaskPage() {
|
||||
};
|
||||
|
||||
if (Number.isFinite(id)) {
|
||||
getData();
|
||||
getSheets();
|
||||
}
|
||||
}, [id, isProject]);
|
||||
}, [id]);
|
||||
|
||||
// Объединили два useEffect в один
|
||||
useEffect(() => {
|
||||
const getFormInfo = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const data = await TasksApi.getById(id);
|
||||
setTask(data.result);
|
||||
} catch (error) {
|
||||
// обработка ошибки
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (Number.isFinite(id)) {
|
||||
getFormInfo();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const handleCreateProject = (projectData) => {
|
||||
// логика создания проекта
|
||||
@ -114,14 +107,6 @@ export default function TaskPage() {
|
||||
return searchString.includes(query.toLowerCase());
|
||||
});
|
||||
|
||||
// Функция для экспорта
|
||||
const handleExport = () => {
|
||||
const fileName = isProject
|
||||
? `project_${task?.title || id}`
|
||||
: `form_${task?.title || taskId}`;
|
||||
exportSingleForm(id, fileName);
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex justify-center pt-12">
|
||||
@ -132,7 +117,7 @@ export default function TaskPage() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{task && <TaskInfo task={task} isProject={isProject} />}
|
||||
{task && <TaskInfo task={task} />}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -158,7 +143,9 @@ export default function TaskPage() {
|
||||
onChange={setQuery}
|
||||
/>
|
||||
<Stack sx={{ flexDirection: 'row', spacing: '.5rem', justifyContent: 'end', gap: '.5rem' }}>
|
||||
<ExportWithTextButton onClick={handleExport} />
|
||||
<ExportWithTextButton
|
||||
onClick={() => exportSingleForm(taskId, task?.title ?? 'form')}
|
||||
/>
|
||||
<PrimaryOutlinedButton
|
||||
variant="outlined"
|
||||
onClick={() => setModalStageOpen(true)}
|
||||
@ -169,10 +156,9 @@ export default function TaskPage() {
|
||||
<SettingStageModal
|
||||
isOpen={modalStageOpen}
|
||||
onClose={() => setModalStageOpen(false)}
|
||||
taskId={isProject ? undefined : taskId}
|
||||
projectId={isProject ? projectId : undefined}
|
||||
mode={isProject ? "project" : "task"}
|
||||
formTypeCode={ task?.form_type_code || ''}
|
||||
taskId={taskId}
|
||||
mode="task"
|
||||
formTypeCode={task?.form_type_code || ''}
|
||||
sheets={tables}
|
||||
/>
|
||||
|
||||
@ -183,9 +169,8 @@ export default function TaskPage() {
|
||||
query={query}
|
||||
projects={projects}
|
||||
filteredTables={filteredTables}
|
||||
basePath={"/table"}
|
||||
basePath="/table"
|
||||
formInfo={task}
|
||||
isProject={isProject}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { forwardRef } from 'react';
|
||||
import { Box, Paper, Typography } from '@mui/material';
|
||||
import { CustomCheckbox } from '../StyledCheckbox';
|
||||
import { CustomCheckbox } from '../../components/common/StyledCheckbox';
|
||||
|
||||
export const OrgUnitsAutocompletePaper = forwardRef(
|
||||
function OrgUnitsAutocompletePaper(
|
||||
@ -27,7 +27,7 @@ import {
|
||||
ORG_UNIT_TYPE_OPTIONS,
|
||||
ROLES_NAME_ID,
|
||||
FORM_TYPE_TRANSLATE
|
||||
} from '../../constants/constants';
|
||||
} from '../../constants';
|
||||
import { useAuth } from '../../app/context/AuthProvider';
|
||||
import { SspApi } from '../../api/ssp';
|
||||
import { PrimaryButton } from '../../components/common/Buttons/Buttons';
|
||||
@ -35,13 +35,10 @@ import {
|
||||
ExportExitButton,
|
||||
ExportWithTextButton,
|
||||
} from '../../components/common/Buttons/ButtonsActions';
|
||||
import { exportBulkForms, exportSingleForm } from '../../utils/exportFile';
|
||||
import { exportBulkForms, exportSingleForm } from '../../utils/exportForm';
|
||||
import { WhitePlus } from '../../components/common/icons/icons';
|
||||
import { OrgUnitsAutocompletePaper } from '../../components/common/OrgUnitsAutocompletePaper/OrgUnitsAutocompletePaper';
|
||||
import { renderOrgUnitValue } from '../../components/common/OrgUnitsAutocompletePaper/renderOrgUnitValue';
|
||||
import { PaginatedList } from '../../components/common/PaginatedList';
|
||||
import NavigationToggle from '../../components/common/NavigationToggle';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { OrgUnitsAutocompletePaper } from './OrgUnitsAutocompletePaper';
|
||||
import { renderOrgUnitValue } from './renderOrgUnitValue';
|
||||
|
||||
const modalSelectMenuProps = {
|
||||
sx: { zIndex: 1301 },
|
||||
@ -55,8 +52,6 @@ const modalSelectSx = {
|
||||
};
|
||||
|
||||
export default function TasksPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [items, setItems] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [loadingUpdate, setLoadingUpdate] = useState(false);
|
||||
@ -260,34 +255,128 @@ export default function TasksPage() {
|
||||
<>
|
||||
<PageContainer>
|
||||
<HeaderSwitch />
|
||||
<NavigationToggle currentView={'tasks'} onViewChange={(path) => navigate(path)} />
|
||||
<PaginatedList
|
||||
items={items}
|
||||
loading={loading}
|
||||
totalCount={totalCount}
|
||||
page={page}
|
||||
limit={limit}
|
||||
onPageChange={handlePageChange}
|
||||
onLimitChange={handleLimitChange}
|
||||
entityName="задач"
|
||||
emptyMessage="Задачи не найдены"
|
||||
renderItem={(task, exportProps) => (
|
||||
<TaskCardComponent
|
||||
key={task.id}
|
||||
taskForm={task}
|
||||
{...exportProps}
|
||||
/>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
{exportMode && <span>Выделено задач: {exportList.length}</span>}
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||
<div style={{ width: '20rem' }}>
|
||||
{/* <SearchComponent
|
||||
placeholder="Поиск по названию задачи"
|
||||
value={query}
|
||||
onChange={setQuery}
|
||||
/> */}
|
||||
</div>
|
||||
<Stack direction={'row'} spacing={'.5rem'}>
|
||||
{!exportMode ? (
|
||||
<>
|
||||
<ExportWithTextButton
|
||||
text={'Пакетный экспорт'}
|
||||
onClick={() => {
|
||||
setExportMode(true);
|
||||
}}
|
||||
></ExportWithTextButton>
|
||||
{user.role_id == ROLES_NAME_ID.admin && (
|
||||
<PrimaryButton onClick={openModal} startIcon={<WhitePlus />}>
|
||||
Создать задачу
|
||||
</PrimaryButton>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ExportWithTextButton
|
||||
text={'Экспортировать'}
|
||||
onClick={handleBulkExport}
|
||||
disabled={exportList.length === 0}
|
||||
/>
|
||||
<ExportExitButton onClick={() => setExportMode(false)} />
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* TODO: вернуть, когда будет реализовано на бэке */}
|
||||
{/* <FilterComponent statusFilter={statusFilter} setStatusFilter={setStatusFilter} /> */}
|
||||
<TasksContainer>
|
||||
{loading ? (
|
||||
<CardsSkeleton count={limit} />
|
||||
) : (
|
||||
<>
|
||||
{items.map((t) => (
|
||||
<TaskCardComponent
|
||||
exportMode={exportMode}
|
||||
taskForm={t}
|
||||
key={t.id + '-task-id'}
|
||||
onAddForExport={(checked) => {
|
||||
handleChangeToExportList(t.id, checked);
|
||||
}}
|
||||
onExportClick={handleSingleExport}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
getItemId={(task) => task.id}
|
||||
getItemTitle={(task) => task.title || 'Задача'}
|
||||
extraActions={
|
||||
user.role_id === ROLES_NAME_ID.admin && (
|
||||
<PrimaryButton onClick={openModal} startIcon={<WhitePlus />} height={'2.5rem'}>
|
||||
Создать задачу
|
||||
</PrimaryButton>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</TasksContainer>
|
||||
|
||||
{/* Pagination Footer */}
|
||||
{!loading && items.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
pt: 2,
|
||||
pb: 2,
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Показать на странице:
|
||||
</Typography>
|
||||
<Select
|
||||
value={limit}
|
||||
onChange={handleLimitChange}
|
||||
size="small"
|
||||
sx={{ minWidth: 70 }}
|
||||
disabled={loading}
|
||||
>
|
||||
<MenuItem value={10}>10</MenuItem>
|
||||
<MenuItem value={20}>20</MenuItem>
|
||||
<MenuItem value={50}>50</MenuItem>
|
||||
<MenuItem value={100}>100</MenuItem>
|
||||
<MenuItem value={200}>200</MenuItem>
|
||||
</Select>
|
||||
</Box>
|
||||
</Box>
|
||||
<Pagination
|
||||
count={Math.ceil(totalCount / limit)}
|
||||
page={page}
|
||||
onChange={handlePageChange}
|
||||
color="primary"
|
||||
size="large"
|
||||
showFirstButton
|
||||
showLastButton
|
||||
disabled={loading}
|
||||
renderItem={(item) => (
|
||||
<PaginationItem
|
||||
{...item}
|
||||
sx={{
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: 'primary.main',
|
||||
color: 'white',
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary.dark',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ width: 120 }} /> {/* Spacer for symmetry */}
|
||||
</Box>
|
||||
)}
|
||||
</PageContainer>
|
||||
<Modal
|
||||
open={modalOpen}
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
import { FormsSheetApi } from "../api/form_sheet";
|
||||
import { ProjectsApi } from "../api/projects";
|
||||
import { TasksApi } from "../api/tasks";
|
||||
import { downloadFile } from "./downloadFile";
|
||||
import { getApiErrorMessage } from "./getApiErrorMessage";
|
||||
import { toast } from "react-toastify";
|
||||
|
||||
export const exportSingleForm = async (taskId, taskTitle) => {
|
||||
try {
|
||||
const response = await TasksApi.exportTask(taskId);
|
||||
await downloadFile(response, taskTitle, "xlsx");
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
"Ошибка при скачивании файла",
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportSheet = async (formId, sheetName, direction) => {
|
||||
try {
|
||||
const normalizedDirection =
|
||||
direction && direction !== "null" ? direction : undefined;
|
||||
const params = normalizedDirection
|
||||
? { direction: normalizedDirection }
|
||||
: {};
|
||||
const response = await FormsSheetApi.export(formId, sheetName, params);
|
||||
await downloadFile(response, sheetName, "xlsx");
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
"Ошибка при скачивании файла",
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportSheetProject = async (formId, sheetName, year) => {
|
||||
try {
|
||||
const response = await ProjectsApi.export(formId, year, sheetName);
|
||||
await downloadFile(response, sheetName, "xlsx");
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
"Ошибка при скачивании файла",
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportBulkForms = async (formIds, fileName = "Задачи") => {
|
||||
try {
|
||||
const response = await TasksApi.exportTasks({ form_ids: formIds });
|
||||
await downloadFile(response, fileName, "xlsx");
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
"Ошибка при скачивании файла",
|
||||
);
|
||||
toast.error(message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
48
web/src/utils/exportForm.js
Normal file
48
web/src/utils/exportForm.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { FormsSheetApi } from '../api/form_sheet';
|
||||
import { TasksApi } from '../api/tasks';
|
||||
import { downloadFile } from './downloadFile';
|
||||
import { getApiErrorMessage } from './getApiErrorMessage';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export const exportSingleForm = async (taskId, taskTitle) => {
|
||||
try {
|
||||
const response = await TasksApi.exportTask(taskId);
|
||||
await downloadFile(response, taskTitle, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportSheet = async (formId, sheetName, direction) => {
|
||||
try {
|
||||
const normalizedDirection =
|
||||
direction && direction !== 'null' ? direction : undefined;
|
||||
const params = normalizedDirection ? { direction: normalizedDirection } : {};
|
||||
const response = await FormsSheetApi.export(formId, sheetName, params);
|
||||
await downloadFile(response, sheetName, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
}
|
||||
};
|
||||
|
||||
export const exportBulkForms = async (formIds, fileName = 'Задачи') => {
|
||||
try {
|
||||
const response = await TasksApi.exportTasks({ form_ids: formIds });
|
||||
await downloadFile(response, fileName, 'xlsx');
|
||||
} catch (error) {
|
||||
const message = await getApiErrorMessage(
|
||||
error,
|
||||
'Ошибка при скачивании файла',
|
||||
);
|
||||
toast.error(message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user