WEB APP: ЦИТК-841 панель "Мониторинг сборки изделий" - добавлена SVG-модель и проведён первый этап рефакторинга

This commit is contained in:
Mikhail Chechnev 2024-05-18 00:52:43 +03:00
parent 46d078219f
commit dea0f3643d
10 changed files with 689 additions and 702 deletions

View File

@ -11,7 +11,7 @@ import { object2Base64XML } from "../../core/utils"; //Вспомогатель
//--------- //---------
//Размер страницы данных //Размер страницы данных
const DATA_GRID_PAGE_SIZE = 10; const DATA_GRID_PAGE_SIZE = 0;
//----------- //-----------
//Тело модуля //Тело модуля
@ -25,10 +25,10 @@ const useMechRecAssemblyMon = () => {
showPlanList: false, showPlanList: false,
planCtlgs: [], planCtlgs: [],
planCtlgsLoaded: false, planCtlgsLoaded: false,
selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null }, selectedPlanCtlg: {},
plans: [], plans: [],
plansLoaded: false, plansLoaded: false,
selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null } selectedPlan: {}
}); });
//Подключение к контексту взаимодействия с сервером //Подключение к контексту взаимодействия с сервером
@ -66,19 +66,21 @@ const useMechRecAssemblyMon = () => {
); );
//Выбор каталога планов //Выбор каталога планов
const selectPlan = project => { const selectPlanCtlg = planCtlg => {
setState(pv => ({ setState(pv => ({
...pv, ...pv,
selectedPlanCtlg: project, selectedPlanCtlg: { ...planCtlg },
selectedPlan: {},
showPlanList: false showPlanList: false
})); }));
}; };
//Сброс выбора каталога планов //Сброс выбора каталога планов
const unselectPlan = () => const unselectPlanCtlg = () =>
setState(pv => ({ setState(pv => ({
...pv, ...pv,
selectedPlanCtlg: { NRN: null, SNAME: null, NMIN_YEAR: null, NMAX_YEAR: null }, selectedPlanCtlg: {},
selectedPlan: {},
showPlanList: false showPlanList: false
})); }));
@ -99,7 +101,7 @@ const useMechRecAssemblyMon = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [state.selectedPlanCtlg]); }, [state.selectedPlanCtlg]);
return [state, setState, selectPlan, unselectPlan]; return [state, setState, selectPlanCtlg, unselectPlanCtlg];
}; };
//Хук для информации по производственным составам //Хук для информации по производственным составам
@ -109,6 +111,8 @@ const useCostProductComposition = nProdPlan => {
init: false, init: false,
showPlanList: false, showPlanList: false,
products: [], products: [],
productsLoaded: false,
model: null,
selectedProduct: null selectedProduct: null
}); });
@ -124,7 +128,13 @@ const useCostProductComposition = nProdPlan => {
respArg: "COUT", respArg: "COUT",
isArray: name => name === "XFCPRODCMP" isArray: name => name === "XFCPRODCMP"
}); });
setCostProductComposition(pv => ({ ...pv, init: true, products: [...(data?.XFCPRODCMP || [])], productsLoaded: true })); setCostProductComposition(pv => ({
...pv,
init: true,
products: [...(data?.XFCPRODCMP || [])],
productsLoaded: true,
model: data?.BMODEL
}));
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [costProductComposition.init, executeStored]); }, [costProductComposition.init, executeStored]);
@ -138,204 +148,53 @@ const useCostProductComposition = nProdPlan => {
return [costProductComposition, setCostProductComposition]; return [costProductComposition, setCostProductComposition];
}; };
//Хук для таблицы маршрутных листов //Хук для таблицы детализации изделия
const useCostRouteLists = (plan, product) => { const useProductDetailsTable = (plan, product, orders, pageNumber, stored) => {
//Собственное состояние - флаг загрузки
const [isLoading, setLoading] = useState(true);
//Собственное состояние - таблица данных //Собственное состояние - таблица данных
const [costRouteLists, setCostRouteLists] = useState({ const [data, setData] = useState({
dataLoaded: false,
columnsDef: [], columnsDef: [],
orders: null,
rows: [], rows: [],
reload: true,
pageNumber: 1,
morePages: true,
selectedProduct: null
});
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Загрузка данных таблицы с сервера
const loadData = useCallback(
async () => {
if (costRouteLists.reload) {
const data = await executeStored({
stored: "PKG_P8PANELS_MECHREC.FCROUTLST_MON_DG_GET",
args: {
NPRODCMPSP: product,
NFCPRODPLAN: plan,
CORDERS: { VALUE: object2Base64XML(costRouteLists.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
NPAGE_NUMBER: costRouteLists.pageNumber,
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
NINCLUDE_DEF: costRouteLists.dataLoaded ? 0 : 1
},
respArg: "COUT"
});
setCostRouteLists(pv => ({
...pv,
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
dataLoaded: true,
reload: false,
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
}));
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[costRouteLists.reload, costRouteLists.orders, costRouteLists.dataLoaded, costRouteLists.pageNumber, executeStored, SERV_DATA_TYPE_CLOB]
);
//При изменении продукта
useEffect(() => {
//Если продукт указан
if (product) {
//Принудительно обновляем состояние
setCostRouteLists(pv => ({
...pv,
dataLoaded: false,
columnsDef: [],
orders: null,
rows: [],
reload: true,
pageNumber: 1,
morePages: true,
selectedProduct: null
}));
//Загружаем данные с учетом выбранного продукта
loadData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [product]);
//При необходимости обновить данные таблицы
useEffect(() => {
//Если продукт указан и необходимо стандартное обновление
if (product) {
loadData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [costRouteLists.reload, loadData]);
//При изменении плана
useEffect(() => {
setCostRouteLists(pv => ({
...pv,
dataLoaded: false,
columnsDef: [],
orders: null,
rows: [],
reload: true,
pageNumber: 1,
morePages: true,
selectedProduct: null
}));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [plan]);
return [costRouteLists, setCostRouteLists];
};
//Хук для таблицы комплектовочных ведомостей
const useCostDeliverySheets = (plan, product) => {
//Собственное состояние - таблица данных
const [costDeliverySheets, setCostDeliverySheets] = useState({
dataLoaded: false,
columnsDef: [],
orders: null,
rows: [],
reload: true,
pageNumber: 1,
morePages: true,
selectedProduct: null
});
//Подключение к контексту взаимодействия с сервером
const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Загрузка данных таблицы с сервера
const loadData = useCallback(
async () => {
if (costDeliverySheets.reload) {
const data = await executeStored({
stored: "PKG_P8PANELS_MECHREC.FCDELIVSH_DG_GET",
args: {
NPRODCMPSP: product,
NFCPRODPLAN: plan,
CORDERS: { VALUE: object2Base64XML(costDeliverySheets.orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
NPAGE_NUMBER: costDeliverySheets.pageNumber,
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
NINCLUDE_DEF: costDeliverySheets.dataLoaded ? 0 : 1
},
respArg: "COUT"
});
setCostDeliverySheets(pv => ({
...pv,
columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
rows: pv.pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
dataLoaded: true,
reload: false,
morePages: (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
}));
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[
costDeliverySheets.reload,
costDeliverySheets.orders,
costDeliverySheets.dataLoaded,
costDeliverySheets.pageNumber,
executeStored,
SERV_DATA_TYPE_CLOB
]
);
//При изменении продукта
useEffect(() => {
//Если продукт указан
if (product) {
//Принудительно обновляем состояние
setCostDeliverySheets(pv => ({
...pv,
dataLoaded: false,
columnsDef: [],
orders: null,
rows: [],
reload: true,
pageNumber: 1,
morePages: true morePages: true
})); });
//Загружаем данные с учетом выбранного продукта
loadData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [product]);
//При необходимости обновить данные таблицы //Подключение к контексту взаимодействия с сервером
useEffect(() => { const { executeStored, SERV_DATA_TYPE_CLOB } = useContext(BackEndСtx);
//Если продукт указан и необходимо стандартное обновление
if (product) {
loadData();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [costDeliverySheets.reload, loadData]);
//При изменении плана //Загрузка данных при изменении зависимостей
useEffect(() => { useEffect(() => {
setCostDeliverySheets(pv => ({ const loadData = async () => {
try {
setLoading(true);
const data = await executeStored({
stored,
args: {
NPRODCMPSP: product,
NFCPRODPLAN: plan,
CORDERS: { VALUE: object2Base64XML(orders, { arrayNodeName: "orders" }), SDATA_TYPE: SERV_DATA_TYPE_CLOB },
NPAGE_NUMBER: pageNumber,
NPAGE_SIZE: DATA_GRID_PAGE_SIZE,
NINCLUDE_DEF: 1
},
respArg: "COUT"
});
setData(pv => ({
...pv, ...pv,
dataLoaded: false, columnsDef: data.XCOLUMNS_DEF ? [...data.XCOLUMNS_DEF] : pv.columnsDef,
columnsDef: [], rows: pageNumber == 1 ? [...(data.XROWS || [])] : [...pv.rows, ...(data.XROWS || [])],
orders: null, morePages: DATA_GRID_PAGE_SIZE == 0 ? false : (data.XROWS || []).length >= DATA_GRID_PAGE_SIZE
rows: [],
reload: true,
pageNumber: 1,
morePages: true,
selectedProduct: null
})); }));
// eslint-disable-next-line react-hooks/exhaustive-deps } finally {
}, [plan]); setLoading(false);
}
};
if (plan && product) loadData();
}, [plan, product, orders, pageNumber, stored, executeStored, SERV_DATA_TYPE_CLOB]);
return [costDeliverySheets, setCostDeliverySheets]; //Вернём данные
return { data, isLoading };
}; };
export { useMechRecAssemblyMon, useCostProductComposition, useCostRouteLists, useCostDeliverySheets }; export { useMechRecAssemblyMon, useCostProductComposition, useProductDetailsTable };

View File

@ -1,102 +0,0 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Панель мониторинга: Информация об объекте
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Typography, Box, ImageList, ImageListItem } from "@mui/material"; //Интерфейсные элементы
import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта
//---------
//Константы
//---------
//Стили
const STYLES = {
PLAN_INFO: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
gap: "24px",
border: "1px solid",
borderRadius: "25px"
}
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Картинка объекта
const CardImage = ({ card }) => {
return (
<Box width={180} height={180}>
<ImageList variant="masonry" cols={1} gap={8}>
<ImageListItem key={1}>
<img src={`data:image/png;base64,${card["#text"]}`} alt={"Image not loaded."} loading="lazy" width={180} />
{/* <img src={`./${airplaneImg}`} alt={"Image not loaded."} loading="lazy" width={180} /> */}
</ImageListItem>
</ImageList>
</Box>
);
};
//Контроль свойств - Заголовок первого уровня
CardImage.propTypes = {
card: PropTypes.object
};
//-----------
//Тело модуля
//-----------
//Информация об объекте
const CardBlock = ({ card, handleCardClick }) => {
return (
<Box sx={STYLES.PLAN_INFO} onClick={() => handleCardClick(card)}>
<CardImage card={card} />
<Box textAlign="center">
<Typography variant="UDO_body1" color="text.secondary.fontColor">
Номер борта
</Typography>
<Typography variant="h2">{card.SNUMB}</Typography>
</Box>
<ProgressBox
prms={{
NPROGRESS: card.NPROGRESS,
SDETAIL: card.SDETAIL,
width: "155px",
height: "155px",
progressVariant: "h3",
detailVariant: "UDO_body2"
}}
/>
<Box>
<Typography variant="UDO_body1" color="text.secondary.fontColor">
Год выпуска:
</Typography>
<Typography variant="subtitle1" mt={-1}>
{card.NYEAR}
</Typography>
</Box>
</Box>
);
};
//Контроль свойств - Заголовок первого уровня
CardBlock.propTypes = {
card: PropTypes.object,
handleCardClick: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { CardBlock };

View File

@ -1,289 +0,0 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Панель мониторинга: Детализация по объекту
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Box, Grid, Container, Button, Typography } from "@mui/material"; //Интерфейсные элементы
import { ProgressBox } from "../elements/progressBox"; //Блок информации по прогрессу объекта
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { useCostProductComposition, useCostRouteLists, useCostDeliverySheets } from "../backend"; //Компоненты панели
//---------
//Константы
//---------
//Стили
const STYLES = {
TABLE_INFO_MAIN: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
border: "1px solid",
borderRadius: "25px",
height: "35vh"
},
TABLE_INFO_SUB: {
margin: "21.6px 0px",
maxHeight: "100%",
overflow: "auto",
textAlign: "center",
width: "100%"
},
DETAIL_INFO: {
display: "flex",
justifyContent: "space-around",
alignItems: "center",
border: "1px solid",
borderRadius: "25px",
height: "17vh"
},
PRODUCT_SELECTOR: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
border: "1px solid",
borderRadius: "25px",
height: "53vh",
marginTop: "16px"
},
PLAN_INFO_MAIN: {
display: "flex",
flexDirection: "column",
gap: "16px"
},
PLAN_INFO_SUB: {
display: "flex",
justifyContent: "space-between",
width: "280px",
borderBottom: "1px solid"
}
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Информация об объекте
const CardDetailInfo = ({ cardInfo }) => {
return (
<>
<Box sx={STYLES.PLAN_INFO_MAIN}>
<Box sx={STYLES.PLAN_INFO_SUB}>
<Typography variant="UDO_body1" mt={1}>
Номер борта:
</Typography>
<Typography variant="subtitle2">{cardInfo.SNUMB}</Typography>
</Box>
<Box sx={STYLES.PLAN_INFO_SUB}>
<Typography variant="UDO_body1" mt={1}>
Год выпуска:
</Typography>
<Typography variant="subtitle2">{cardInfo.NYEAR}</Typography>
</Box>
</Box>
<ProgressBox
prms={{
NPROGRESS: cardInfo.NPROGRESS,
SDETAIL: cardInfo.SDETAIL,
width: "110px",
height: "110px",
progressVariant: "subtitle2",
detailVariant: "body3"
}}
/>
</>
);
};
//Контроль свойств - Информация об объекте
CardDetailInfo.propTypes = {
cardInfo: PropTypes.object
};
//Детали объекта
const CardSelector = ({ products, setCostProductComposition }) => {
//При выборе детали в SVG
const handleProductClick = product => {
setCostProductComposition(pv => ({ ...pv, selectedProduct: product }));
};
return (
<>
<Box sx={STYLES.PLAN_INFO_MAIN}>
{products.map(el => (
<Button key={el.NRN} onClick={() => handleProductClick(el.NRN)}>{`${el.SNAME}`}</Button>
))}
</Box>
</>
);
};
//Контроль свойств - Детали объекта
CardSelector.propTypes = {
products: PropTypes.array,
setCostProductComposition: PropTypes.func
};
//Генерация представления ячейки заголовка
const headCellRender = ({ columnDef }) => {
//Описываем общий стиль
let cellStyle = { padding: "2px 5px", fontSize: "12px", textAlign: "center", lineHeight: "1rem" };
let stackProps = { justifyContent: "center" };
//Дополнительные свойства
switch (columnDef.name) {
case "NREMN_LABOUR":
//Добавляем максимальную ширину
cellStyle = { ...cellStyle, maxWidth: "90px" };
break;
case "NDEFICIT":
//Добавляем максимальную ширину
cellStyle = { ...cellStyle, maxWidth: "55px" };
break;
case "NAPPLICABILITY":
//Добавляем максимальную ширину
cellStyle = { ...cellStyle, maxWidth: "90px" };
break;
default:
break;
}
return {
stackProps,
cellStyle
};
};
//Генерация заливки строки исходя от значений
const dataCellRender = ({ row, columnDef }) => {
//Описываем общий стиль
let cellStyle = { padding: "2px 5px", fontSize: "12px" };
//Для всех кроме содержания и номенклатуры добавляем выравнивание
switch (columnDef.name) {
case "SOPERATION":
break;
case "SNOMEN":
break;
default:
//Добавляем выравнивание
cellStyle = { ...cellStyle, textAlign: "center" };
break;
}
return {
cellStyle,
data: row[columnDef]
};
};
//-----------
//Тело модуля
//-----------
//Детализация по объекту
const CardDetail = ({ card, handleBackClick }) => {
//Собственное состояние - данные производственных составов SVG
const [costProductComposition, setCostProductComposition] = useCostProductComposition(card.NRN);
//Собственное состояние - таблица данных маршрутных листов
const [costRouteLists, setCostRouteLists] = useCostRouteLists(card.NRN, costProductComposition.selectedProduct);
//Собственное состояние - таблица данных комплектовочных ведомостей
const [сostDeliverySheets, setСostDeliverySheets] = useCostDeliverySheets(card.NRN, costProductComposition.selectedProduct);
//При изменении состояния сортировки маршрутных листов
const costRouteListsOrderChanged = ({ orders }) => setCostRouteLists(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
//При изменении количества отображаемых страниц маршрутных листов
const costRouteListsPagesCountChanged = () => setCostRouteLists(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
//При изменении состояния сортировки комплектовочных ведомостей
const СostDeliverySheetsOrderChanged = ({ orders }) => setСostDeliverySheets(pv => ({ ...pv, orders: [...orders], pageNumber: 1, reload: true }));
//При изменении количества отображаемых страниц комплектовочных ведомостей
const СostDeliverySheetsPagesCountChanged = () => setСostDeliverySheets(pv => ({ ...pv, pageNumber: pv.pageNumber + 1, reload: true }));
return (
<Container>
<Button onClick={() => handleBackClick()}>Назад</Button>
<Grid container spacing={2}>
<Grid item xs={5}>
<Box sx={STYLES.TABLE_INFO_MAIN}>
<Box sx={STYLES.TABLE_INFO_SUB}>
{!costRouteLists.dataLoaded ? (
<Typography variant="UDO_body2">Выберите агрегат самолёта, чтобы увидеть информацию</Typography>
) : costRouteLists.rows.length === 0 ? (
<Typography variant="subtitle2">Нет данных по МК</Typography>
) : (
<>
<Typography variant="h4">Маршрутная карта</Typography>
<P8PDataGrid
{...P8P_DATA_GRID_CONFIG_PROPS}
columnsDef={costRouteLists.columnsDef}
rows={costRouteLists.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={costRouteLists.morePages}
reloading={costRouteLists.reload}
dataCellRender={dataCellRender}
headCellRender={headCellRender}
onOrderChanged={costRouteListsOrderChanged}
onPagesCountChanged={costRouteListsPagesCountChanged}
/>
</>
)}
</Box>
</Box>
<Box sx={STYLES.TABLE_INFO_MAIN} mt={2}>
<Box sx={STYLES.TABLE_INFO_SUB}>
{!сostDeliverySheets.dataLoaded ? (
<Typography variant="UDO_body2">Выберите агрегат самолёта, чтобы увидеть информацию</Typography>
) : сostDeliverySheets.rows.length === 0 ? (
<Typography variant="subtitle2">Нет данных по КВ</Typography>
) : (
<>
<Typography variant="h4">Дефицит по КВ</Typography>
<P8PDataGrid
{...P8P_DATA_GRID_CONFIG_PROPS}
columnsDef={сostDeliverySheets.columnsDef}
rows={сostDeliverySheets.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={сostDeliverySheets.morePages}
reloading={сostDeliverySheets.reload}
dataCellRender={dataCellRender}
headCellRender={headCellRender}
onOrderChanged={СostDeliverySheetsOrderChanged}
onPagesCountChanged={СostDeliverySheetsPagesCountChanged}
/>
</>
)}
</Box>
</Box>
</Grid>
<Grid item xs={7}>
<Box sx={STYLES.DETAIL_INFO}>
<CardDetailInfo cardInfo={card} />
</Box>
<Box sx={STYLES.PRODUCT_SELECTOR}>
<CardSelector products={costProductComposition.products} setCostProductComposition={setCostProductComposition} />
</Box>
</Grid>
</Grid>
</Container>
);
};
//Контроль свойств - Детализация по объекту
CardDetail.propTypes = {
card: PropTypes.object,
handleBackClick: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { CardDetail };

View File

@ -0,0 +1,324 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Панель мониторинга: Детализация по объекту
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useEffect, useState } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Box, Grid, Container, Button, Typography, Icon, Stack, IconButton } from "@mui/material"; //Интерфейсные элементы
import { P8PDataGrid, P8P_DATA_GRID_SIZE } from "../../../components/p8p_data_grid"; //Таблица данных
import { P8PSVG } from "../../../components/p8p_svg"; //Интерактивные изображения
import { P8P_DATA_GRID_CONFIG_PROPS } from "../../../config_wrapper"; //Подключение компонентов к настройкам приложения
import { useCostProductComposition, useProductDetailsTable } from "../backend"; //Взаимодействие с сервером
import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
//---------
//Константы
//---------
//Стили
const STYLES = {
BOX_INFO_MAIN: {
border: "1px solid",
borderRadius: "25px",
height: "35vh"
},
BOX_INFO_SUB: isMessage => ({
overflow: "hidden",
textAlign: "center",
width: "100%",
height: "100%",
display: "flex",
flexDirection: "column",
justifyContent: isMessage ? "center" : "flex-start",
paddingLeft: "5px",
paddingRight: "5px",
...(isMessage ? { padding: "5px" } : { paddingTop: "10px" })
}),
DETAIL_INFO: {
display: "flex",
justifyContent: "space-around",
alignItems: "center",
border: "1px solid",
borderRadius: "25px",
height: "17vh"
},
PRODUCT_SELECTOR_CONTAINER: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
border: "1px solid",
borderRadius: "25px",
height: "53vh",
marginTop: "16px"
},
PRODUCT_SELECTOR_MODEL: { width: "70%" },
PLAN_INFO_MAIN: {
display: "flex",
flexDirection: "column",
gap: "16px"
},
PLAN_INFO_SUB: {
display: "flex",
justifyContent: "space-between",
width: "280px",
borderBottom: "1px solid"
},
TABLE_DETAILS: { height: "260px" },
TABLE_DETAILS_HEADER_CELL: maxWidth => ({
padding: "2px 2px",
fontSize: "11px",
textAlign: "center",
lineHeight: "1rem",
...(maxWidth ? { maxWidth } : {})
}),
TABLE_DETAILS_DATA_CELL: textAlign => ({ padding: "2px 2px", fontSize: "11px", ...(textAlign ? { textAlign } : {}) }),
CARD_DETAILS_CONTAINER: { minWidth: "1200px", maxWidth: "1400px" },
CARD_DETAILS_NAVIGATION_STACK: { width: "100%" }
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Информация о плане
const PlanInfo = ({ plan }) => {
return (
<>
<Box sx={STYLES.PLAN_INFO_MAIN}>
<Box sx={STYLES.PLAN_INFO_SUB}>
<Typography variant="UDO_body1" mt={1}>
Номер борта:
</Typography>
<Typography variant="subtitle2">{plan.SNUMB}</Typography>
</Box>
<Box sx={STYLES.PLAN_INFO_SUB}>
<Typography variant="UDO_body1" mt={1}>
Год выпуска:
</Typography>
<Typography variant="subtitle2">{plan.NYEAR}</Typography>
</Box>
</Box>
<ProgressBox
progress={plan.NPROGRESS}
detail={plan.SDETAIL}
width={"110px"}
height={"110px"}
progressVariant={"subtitle2"}
detailVariant={"body3"}
/>
</>
);
};
//Контроль свойств - Информация о плане
PlanInfo.propTypes = {
plan: PropTypes.object
};
//Модель выпуска плана
const PlanProductCompositionModel = ({ model, products, setCostProductComposition }) => {
//При выборе детали на модели
const handleProductClick = ({ item }) => {
const product = products.find(p => p.SMODEL_ID == item.id);
if (product) setCostProductComposition(pv => ({ ...pv, selectedProduct: { ...product } }));
};
//Генерация содержимого
return (
<>
<Box sx={STYLES.PRODUCT_SELECTOR_MODEL}>
{model ? (
<P8PSVG
data={atob(model)}
items={products.map(p => ({ id: p.SMODEL_ID, backgroundColor: p.SMODEL_BG_COLOR || "red", desc: p.SNAME, title: p.SNAME }))}
fillOpacity={"0.3"}
onItemClick={handleProductClick}
/>
) : (
<Typography variant="subtitle2">Модель изделия не загружена</Typography>
)}
</Box>
</>
);
};
//Контроль свойств - Модель выпуска плана
PlanProductCompositionModel.propTypes = {
model: PropTypes.any,
products: PropTypes.array,
setCostProductComposition: PropTypes.func
};
//Генерация представления ячейки заголовка
const headCellRender = ({ columnDef }) => ({
stackProps: { justifyContent: "center" },
cellStyle: STYLES.TABLE_DETAILS_HEADER_CELL(
["NREMN_LABOUR", "NAPPLICABILITY"].includes(columnDef.name) ? "90px" : ["NDEFICIT"].includes(columnDef.name) ? "55px" : null
)
});
//Генерация заливки строки исходя от значений
const dataCellRender = ({ row, columnDef }) => ({
cellStyle: STYLES.TABLE_DETAILS_DATA_CELL(["SOPERATION", "SNOMEN"].includes(columnDef.name) ? null : "center"),
data: row[columnDef]
});
//Таблица детализации изделия
const ProductDetailsTable = ({ plan, product, stored, noProductMessage, noDataFoundMessage, title }) => {
//Собственное состояние
const [state, setState] = useState({ plan: null, product: null, orders: null, pageNumber: 1 });
//Собственное состояние - данные таблицы
const { data, isLoading } = useProductDetailsTable(state.plan, state.product, state.orders, state.pageNumber, stored);
//При изменении состояния сортировки
const handleOrderChanged = ({ orders }) => setState(pv => ({ ...pv, orders: [...orders], pageNumber: 1 }));
//При изменении количества отображаемых страниц
const handlePagesCountChanged = () => setState(pv => ({ ...pv, pageNumber: pv.pageNumber + 1 }));
//При изменении изделия
useEffect(() => {
setState(pv => ({ ...pv, plan, product, orders: null, pageNumber: 1 }));
}, [product, plan]);
//Генерация содержимого
return (
<Box sx={STYLES.BOX_INFO_SUB(!product || data.rows.length === 0)}>
{!product ? (
<Typography variant="UDO_body2">{noProductMessage}</Typography>
) : isLoading ? null : data.rows.length === 0 ? (
<Typography variant="subtitle2">{noDataFoundMessage}</Typography>
) : (
<>
<Typography variant="h4">
<b>{title}</b>
</Typography>
<P8PDataGrid
{...P8P_DATA_GRID_CONFIG_PROPS}
containerComponentProps={{ sx: STYLES.TABLE_DETAILS, elevation: 0 }}
columnsDef={data.columnsDef}
rows={data.rows}
size={P8P_DATA_GRID_SIZE.SMALL}
morePages={data.morePages}
fixedHeader={true}
reloading={false}
dataCellRender={dataCellRender}
headCellRender={headCellRender}
onOrderChanged={handleOrderChanged}
onPagesCountChanged={handlePagesCountChanged}
/>
</>
)}
</Box>
);
};
//Контроль свойств - Таблица детализации изделия
ProductDetailsTable.propTypes = {
plan: PropTypes.number.isRequired,
product: PropTypes.number,
stored: PropTypes.string.isRequired,
noProductMessage: PropTypes.string.isRequired,
noDataFoundMessage: PropTypes.string.isRequired,
title: PropTypes.string.isRequired
};
//-----------
//Тело модуля
//-----------
//Детализация по объекту
const PlanDetail = ({ plan, disableNavigatePrev = false, disableNavigateNext = false, onNavigate, onBack }) => {
//Собственное состояние - данные производственных составов SVG
const [costProductComposition, setCostProductComposition] = useCostProductComposition(plan.NRN);
//Формируем представление
return (
<Container maxWidth={false} sx={STYLES.CARD_DETAILS_CONTAINER}>
<Grid container direction="row" justifyContent="center" alignItems="center" spacing={0}>
<Grid item display="flex" justifyContent="center" xs={1}>
<Stack display="flex" direction="row" justifyContent="flex-end" alignItems="center" sx={STYLES.CARD_DETAILS_NAVIGATION_STACK}>
<IconButton disabled={disableNavigatePrev} onClick={() => (onNavigate ? onNavigate(-1) : null)}>
<Icon>navigate_before</Icon>
</IconButton>
</Stack>
</Grid>
<Grid item xs={10}>
<Container maxWidth={false}>
<Button onClick={() => (onBack ? onBack() : null)}>
<Stack direction="row">
<Icon>chevron_left</Icon>Назад
</Stack>
</Button>
<Grid container spacing={2} sx={{ paddingTop: "5px" }}>
<Grid item xs={5}>
<Box sx={STYLES.BOX_INFO_MAIN}>
<ProductDetailsTable
plan={plan.NRN}
product={costProductComposition.selectedProduct?.NRN}
stored={"PKG_P8PANELS_MECHREC.FCROUTLST_DG_BY_PRDCMPSP_GET"}
noProductMessage={"Укажите элемент модели, чтобы увидеть информацию о маршрутных картах"}
noDataFoundMessage={"Маршрутные карты не найдены"}
title={"Маршрутные карты"}
/>
</Box>
<Box sx={STYLES.BOX_INFO_MAIN} mt={2}>
<ProductDetailsTable
plan={plan.NRN}
product={costProductComposition.selectedProduct?.NRN}
stored={"PKG_P8PANELS_MECHREC.FCDELIVSH_DG_BY_PRDCMPSP_GET"}
noProductMessage={"Укажите элемент модели, чтобы увидеть информацию о комплектовочных ведомостях"}
noDataFoundMessage={"Комплектовочные ведомости не найдены"}
title={"Дефицит комплектации"}
/>
</Box>
</Grid>
<Grid item xs={7}>
<Box sx={STYLES.DETAIL_INFO}>
<PlanInfo plan={plan} />
</Box>
<Box sx={STYLES.PRODUCT_SELECTOR_CONTAINER}>
<PlanProductCompositionModel
model={costProductComposition.model}
products={costProductComposition.products}
setCostProductComposition={setCostProductComposition}
/>
</Box>
</Grid>
</Grid>
</Container>
</Grid>
<Grid item display="flex" justifyContent="center" xs={1}>
<Stack display="flex" direction="row" justifyContent="flex-start" alignItems="center" sx={STYLES.CARD_DETAILS_NAVIGATION_STACK}>
<IconButton disabled={disableNavigateNext} onClick={() => (onNavigate ? onNavigate(1) : null)}>
<Icon>navigate_next</Icon>
</IconButton>
</Stack>
</Grid>
</Grid>
</Container>
);
};
//Контроль свойств - Детализация по объекту
PlanDetail.propTypes = {
plan: PropTypes.object,
disableNavigatePrev: PropTypes.bool,
disableNavigateNext: PropTypes.bool,
onNavigate: PropTypes.func,
onBack: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { PlanDetail };

View File

@ -0,0 +1,83 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Компонент: Список планов
*/
//---------------------
//Подключение библиотек
//---------------------
import React, { useState } from "react"; //Классы React
import { Container, Grid, IconButton, Icon } from "@mui/material"; //Интерфейсные элементы
import PropTypes from "prop-types"; //Контроль свойств компонента
import { PlansListItem } from "./plans_list_item"; //Элемент списка планов
//---------
//Константы
//---------
//Количество одновременно отображаемых элементов списка по умолчанию
const DEFAULT_PAGE_SIZE = 5;
//Стили
const STYLES = {
PLAN_DOCUMENTS_LIST: { minWidth: "1024px" }
};
//-----------
//Тело модуля
//-----------
//Список планов
const PlansList = ({ plans, pageSize = DEFAULT_PAGE_SIZE, onItemClick }) => {
//Состояние прокрутки списка отображаемых планов
const [scroll, setScroll] = useState(0);
//Отработка нажатия на прокрутку списка планов влево
const handleScrollLeft = () => setScroll(pv => (pv <= 1 ? 0 : pv - 1));
//Отработка нажатия на прокрутку списка планов вправо
const handleScrollRight = () => setScroll(pv => (pv + pageSize >= plans.length ? pv : pv + 1));
//Сборка представления
return (
<Container>
<Grid container direction="row" justifyContent="center" alignItems="center" spacing={2} sx={STYLES.PLAN_DOCUMENTS_LIST}>
<Grid item display="flex" justifyContent="center" xs={1}>
<IconButton onClick={handleScrollLeft} disabled={scroll <= 0}>
<Icon>navigate_before</Icon>
</IconButton>
</Grid>
{plans.map((el, i) =>
i >= scroll && i < scroll + pageSize ? (
<Grid item key={`${el.NRN}_${i}`} xs={2}>
<PlansListItem
card={el}
cardIndex={i}
onClick={(card, cardIndex) => (onItemClick ? onItemClick(card, cardIndex) : null)}
/>
</Grid>
) : null
)}
<Grid item display="flex" justifyContent="center" xs={1}>
<IconButton onClick={handleScrollRight} disabled={scroll + pageSize >= plans.length}>
<Icon>navigate_next</Icon>
</IconButton>
</Grid>
</Grid>
</Container>
);
};
//Контроль свойств - Список планов
PlansList.propTypes = {
plans: PropTypes.arrayOf(PropTypes.object),
pageSize: PropTypes.number,
onItemClick: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { PlansList };

View File

@ -0,0 +1,108 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Компонент: Элемент списка планов
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Typography, Box, ImageList, ImageListItem, Icon } from "@mui/material"; //Интерфейсные элементы
import { ProgressBox } from "./progress_box"; //Информация по прогрессу объекта
//---------
//Константы
//---------
//Стили
const STYLES = {
CONTAINER: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
gap: "24px",
border: "1px solid",
borderRadius: "25px",
cursor: "pointer"
},
IMAGE_BOX: { width: "180px", height: "180px", alignItems: "center", justifyContent: "center", display: "flex" },
IMAGE_LIST_ITEM: { textAlign: "center" },
IMAGE_IMG: { width: "160px" }
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//Изображение для элемента
const PlansListItemImage = ({ card }) => {
return (
<Box sx={STYLES.IMAGE_BOX}>
<ImageList variant="masonry" cols={1} gap={8}>
<ImageListItem key={1} sx={STYLES.IMAGE_LIST_ITEM}>
{card["BIMAGE"] ? (
<img src={`data:image/png;base64,${card["BIMAGE"]}`} loading="lazy" style={STYLES.IMAGE_IMG} />
) : (
<Icon sx={{ fontSize: "5rem" }}>construction</Icon>
)}
</ImageListItem>
</ImageList>
</Box>
);
};
//Контроль свойств - Изображение для элемента
PlansListItemImage.propTypes = {
card: PropTypes.object
};
//-----------
//Тело модуля
//-----------
//Элемент списка планов
const PlansListItem = ({ card, cardIndex, onClick }) => {
return (
<Box sx={STYLES.CONTAINER} onClick={() => (onClick ? onClick(card, cardIndex) : null)}>
<PlansListItemImage card={card} />
<Box textAlign="center">
<Typography variant="UDO_body1" color="text.secondary.fontColor">
Номер борта
</Typography>
<Typography variant="h2">{card.SNUMB}</Typography>
</Box>
<ProgressBox
progress={card.NPROGRESS}
detail={card.SDETAIL}
width={"155px"}
height={"155px"}
progressVariant={"h3"}
detailVariant={"UDO_body2"}
/>
<Box>
<Typography variant="UDO_body1" color="text.secondary.fontColor">
Год выпуска:
</Typography>
<Typography variant="subtitle1" mt={-1}>
{card.NYEAR}
</Typography>
</Box>
</Box>
);
};
//Контроль свойств - Элемент списка планов
PlansListItem.propTypes = {
card: PropTypes.object,
cardIndex: PropTypes.number,
onClick: PropTypes.func
};
//----------------
//Интерфейс модуля
//----------------
export { PlansListItem };

View File

@ -0,0 +1,75 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Компонент: Информация по прогрессу объекта
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Typography, Box } from "@mui/material"; //Интерфейсные элементы
//---------
//Константы
//---------
//Стили
const STYLES = {
PROGRESS_BOX: (width, height) => ({
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
margin: "0px 32px",
borderRadius: "50%",
...(width ? { width } : {}),
...(height ? { height } : {})
})
};
//-----------
//Тело модуля
//-----------
//Информация по прогрессу объекта
const ProgressBox = ({ progress, detail, width, height, progressVariant, detailVariant }) => {
//Определяем цвет тени
let boxShadow = "0 0 30px #d3d3d3";
switch (true) {
case progress >= 70:
boxShadow = "0 0 30px #21d21e66";
break;
case progress >= 40:
boxShadow = "0 0 30px #fddd3566";
break;
case progress >= 10:
boxShadow = "0 0 30px #ea5c4966";
break;
}
//Возвращаем содержимое
return (
<Box sx={STYLES.PROGRESS_BOX(width, height)} boxShadow={boxShadow}>
<Typography variant={progressVariant}>{`${progress}%`}</Typography>
<Typography variant={detailVariant}>{detail}</Typography>
</Box>
);
};
//Контроль свойств - Информация по прогрессу объекта
ProgressBox.propTypes = {
progress: PropTypes.number,
detail: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
progressVariant: PropTypes.string,
detailVariant: PropTypes.string
};
//----------------
//Интерфейс модуля
//----------------
export { ProgressBox };

View File

@ -1,76 +0,0 @@
/*
Парус 8 - Панели мониторинга - ПУП - Мониторинг сборки изделий
Панель мониторинга: Блок информации по прогрессу объекта
*/
//---------------------
//Подключение библиотек
//---------------------
import React from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента
import { Typography, Box } from "@mui/material"; //Интерфейсные элементы
//---------
//Константы
//---------
//Стили
const STYLES = {
PROGRESS_INFO: {
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
margin: "0px 32px",
borderRadius: "50%"
}
};
//------------------------------------
//Вспомогательные функции и компоненты
//------------------------------------
//-----------
//Тело модуля
//-----------
//Детализация по объекту
//Блок информации по прогрессу объекта
const ProgressBox = ({ prms }) => {
//Инициализируем цвет тени
let boxShadow = null;
//Определяем цвет тени
switch (true) {
case prms.NPROGRESS >= 70:
boxShadow = "0 0 30px #21d21e66";
break;
case prms.NPROGRESS >= 40:
boxShadow = "0 0 30px #fddd3566";
break;
case prms.NPROGRESS >= 10:
boxShadow = "0 0 30px #ea5c4966";
break;
default:
boxShadow = "0 0 30px #d3d3d3";
}
//Возвращаем блок
return (
<Box sx={{ ...STYLES.PROGRESS_INFO, width: prms.width, height: prms.height }} boxShadow={boxShadow}>
<Typography variant={prms.progressVariant}>{`${prms.NPROGRESS}%`}</Typography>
<Typography variant={prms.detailVariant}>{prms.SDETAIL}</Typography>
</Box>
);
};
//Контроль свойств - Блок информации по прогрессу объекта
ProgressBox.propTypes = {
prms: PropTypes.object
};
//----------------
//Интерфейс модуля
//----------------
export { ProgressBox };

View File

@ -9,26 +9,12 @@
import React, { useState, useContext } from "react"; //Классы React import React, { useState, useContext } from "react"; //Классы React
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
import { import { Drawer, Fab, Box, List, ListItemButton, ListItemText, Typography, TextField, FormGroup, FormControlLabel, Checkbox } from "@mui/material"; //Интерфейсные элементы
Drawer,
Fab,
Box,
List,
ListItemButton,
ListItemText,
Typography,
Grid,
TextField,
FormGroup,
FormControlLabel,
Checkbox,
Container
} from "@mui/material"; //Интерфейсные элементы
import { ThemeProvider } from "@mui/material/styles"; //Подключение темы import { ThemeProvider } from "@mui/material/styles"; //Подключение темы
import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений import { MessagingСtx } from "../../context/messaging"; //Контекст сообщений
import { CardBlock } from "./blocks/cardBlock"; //Информация об объекте import { PlansList } from "./components/plans_list"; //Список планов
import { CardDetail } from "./blocks/cardDetail"; //Детализация по объекту import { PlanDetail } from "./components/plan_detail"; //Детали плана
import { theme } from "./styles/themes.js"; //Стиль темы import { theme } from "./styles/themes"; //Стиль темы
import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки import { useFilteredPlanCtlgs } from "./hooks"; //Вспомогательные хуки
import { useMechRecAssemblyMon } from "./backend"; //Хук корневой панели мониторинга сборки изделий import { useMechRecAssemblyMon } from "./backend"; //Хук корневой панели мониторинга сборки изделий
@ -49,7 +35,8 @@ const STYLES = {
display: "inline-block", display: "inline-block",
flexShrink: 0, flexShrink: 0,
[`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" } [`& .MuiDrawer-paper`]: { width: "350px", display: "inline-block", boxSizing: "border-box" }
} },
PLANS_LIST_BOX: { paddingTop: "20px" }
}; };
//------------------------------------ //------------------------------------
@ -131,11 +118,18 @@ PlanCtlgsList.propTypes = {
//Корневая панель мониторинга сборки изделий //Корневая панель мониторинга сборки изделий
const MechRecAssemblyMon = () => { const MechRecAssemblyMon = () => {
//Собственное состояние //Собственное состояние
const [state, setState, selectPlan, unselectPlan] = useMechRecAssemblyMon(); const [state, setState, selectPlanCtlg, unselectPlanCtlg] = useMechRecAssemblyMon();
//Состояние для фильтра каталогов //Состояние фильтра каталогов
const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false }); const [filter, setFilter] = useState({ ctlgName: "", haveDocs: false });
//Состояние навигации по карточкам детализации
const [planDetailNavigation, setPlanDetailNavigation] = useState({
disableNavigatePrev: false,
disableNavigateNext: false,
currentPlanIndex: 0
});
//Массив отфильтрованных каталогов //Массив отфильтрованных каталогов
const filteredPlanCtgls = useFilteredPlanCtlgs(state.planCtlgs, filter); const filteredPlanCtgls = useFilteredPlanCtlgs(state.planCtlgs, filter);
@ -143,24 +137,38 @@ const MechRecAssemblyMon = () => {
const { InlineMsgInfo } = useContext(MessagingСtx); const { InlineMsgInfo } = useContext(MessagingСtx);
//Обработка нажатия на элемент в списке каталогов планов //Обработка нажатия на элемент в списке каталогов планов
const handleProjectClick = project => { const handlePlanCtlgClick = planCtlg => {
if (state.selectedPlanCtlg.NRN != project.NRN) selectPlan(project); if (state.selectedPlanCtlg.NRN != planCtlg.NRN) selectPlanCtlg(planCtlg);
else unselectPlan(); else unselectPlanCtlg();
}; };
//Обработка нажатия на карточку объекта //Перемещение к нужному плану
const handleCardClick = plan => { const navigateToPlan = planIndex => {
if (planIndex < 0) planIndex = 0;
if (planIndex > state.plans.length - 1) planIndex = state.plans.length - 1;
setState(pv => ({ setState(pv => ({
...pv, ...pv,
selectedPlan: { NRN: plan.NRN, SNUMB: plan.SNUMB, NPROGRESS: plan.NPROGRESS, SDETAIL: plan.SDETAIL, NYEAR: plan.NYEAR } selectedPlan: { ...state.plans[planIndex] }
}));
setPlanDetailNavigation(pv => ({
...pv,
disableNavigatePrev: planIndex == 0 ? true : false,
disableNavigateNext: planIndex == state.plans.length - 1 ? true : false,
currentPlanIndex: planIndex
})); }));
}; };
//Обработка нажатия на документ плана
const handlePlanClick = (plan, planIndex) => navigateToPlan(planIndex);
//Обработка нажатия на кнопку "Назад" //Обработка нажатия на кнопку "Назад"
const handleBackClick = () => { const handlePlanDetailBackClick = () => {
setState(pv => ({ ...pv, selectedPlan: { NRN: null, SNUMB: null, NPROGRESS: null, SDETAIL: null, NYEAR: null } })); setState(pv => ({ ...pv, selectedPlan: {} }));
}; };
//Обработка навигации из карточки с деталями плана
const handlePlanDetailNavigateClick = direction => navigateToPlan(planDetailNavigation.currentPlanIndex + direction);
//Генерация содержимого //Генерация содержимого
return ( return (
<Box p={2}> <Box p={2}>
@ -179,34 +187,30 @@ const MechRecAssemblyMon = () => {
selectedPlanCtlg={state.selectedPlanCtlg.NRN} selectedPlanCtlg={state.selectedPlanCtlg.NRN}
filter={filter} filter={filter}
setFilter={setFilter} setFilter={setFilter}
onClick={handleProjectClick} onClick={handlePlanCtlgClick}
/> />
</Drawer> </Drawer>
{state.init == true ? ( {state.init == true ? (
state.selectedPlanCtlg.NRN ? ( state.selectedPlanCtlg.NRN ? (
<> <>
<Typography variant="h1" align="center" py={3}> <Typography variant="h3" align="center" color="text.title.fontColor" py={2}>
{`${state.selectedPlanCtlg.SNAME} на ${state.selectedPlanCtlg.NMIN_YEAR}г. - ${state.selectedPlanCtlg.NMAX_YEAR}г.`} {`${state.selectedPlanCtlg.SNAME} ${
state.selectedPlanCtlg.NMIN_YEAR ? `с ${state.selectedPlanCtlg.NMIN_YEAR} г` : ""
} ${state.selectedPlanCtlg.NMAX_YEAR ? `по ${state.selectedPlanCtlg.NMAX_YEAR}` : ""}`}
</Typography> </Typography>
{state.plansLoaded == true ? ( {state.plansLoaded == true ? (
state.selectedPlan.NRN ? ( state.selectedPlan.NRN ? (
<CardDetail card={state.selectedPlan} handleBackClick={handleBackClick} /> <PlanDetail
plan={state.selectedPlan}
disableNavigatePrev={planDetailNavigation.disableNavigatePrev}
disableNavigateNext={planDetailNavigation.disableNavigateNext}
onNavigate={handlePlanDetailNavigateClick}
onBack={handlePlanDetailBackClick}
/>
) : ( ) : (
<Container> <Box sx={STYLES.PLANS_LIST_BOX}>
<Grid container spacing={5}> <PlansList plans={state.plans} onItemClick={handlePlanClick} />
{state.plans.map(el => ( </Box>
<Grid
item
xs={state.plans.length >= 5 ? 2.4 : 12 / state.plans.length}
key={el.NRN}
display="flex"
justifyContent="center"
>
<CardBlock card={el} handleCardClick={handleCardClick} />
</Grid>
))}
</Grid>
</Container>
) )
) : null} ) : null}
</> </>

View File

@ -4,6 +4,7 @@ import { createTheme } from "@mui/material/styles"; //Интерфейсные
const theme = createTheme({ const theme = createTheme({
palette: { palette: {
text: { text: {
title: { fontColor: "rgba(0, 0, 0, 0.65)" },
secondary: { fontColor: "rgba(0, 0, 0, 0.298)" } secondary: { fontColor: "rgba(0, 0, 0, 0.298)" }
} }
}, },