import { FC, useState, useEffect, useCallback } from 'react'; import { unstable_useBlocker as useBlocker } from 'react-router-dom'; import { Button, Typography, Box, Grid, Dialog, DialogActions, DialogContent, DialogTitle, TextField, MenuItem, InputAdornment } from '@mui/material'; import { useTheme } from '@table-library/react-table-library/theme'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { toast } from 'react-toastify'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import WarningIcon from '@mui/icons-material/Warning'; import CancelIcon from '@mui/icons-material/Cancel'; import DoneIcon from '@mui/icons-material/Done'; import AddIcon from '@mui/icons-material/Add'; import { ValidatedTextField, ButtonRow, FormLoader, SectionContent, BlockNavigation } from 'components'; import { DeviceValueUOM_s, EntityItem } from './types'; import { extractErrorMessage, updateValue } from 'utils'; import { validate } from 'validators'; import { entityItemValidation } from './validators'; import { useI18nContext } from 'i18n/i18n-react'; import { ValidateFieldsError } from 'async-validator'; import * as EMSESP from './api'; const SettingsEntities: FC = () => { const { LL, locale } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); const emptyEntity = { device_id: '', type_id: '', offset: 0, factor: 1, uom: 0, val_type: 2, name: '', deleted: false }; const [entity, setEntity] = useState([emptyEntity]); const [entityItem, setEntityItem] = useState(); const [errorMessage, setErrorMessage] = useState(); const [creating, setCreating] = useState(false); const [fieldErrors, setFieldErrors] = useState(); useEffect(() => { setNumChanges(getNumChanges()); }); const entity_theme = useTheme({ Table: ` --data-table-library_grid-template-columns: repeat(1, minmax(60px, 1fr)) 80px 80px 80px 80px; `, BaseRow: ` font-size: 14px; .td { height: 32px; } `, BaseCell: ` &:nth-of-type(2) { text-align: center; } &:nth-of-type(3) { text-align: center; } &:nth-of-type(4) { text-align: center; } &:nth-of-type(5) { text-align: right; } `, HeaderRow: ` text-transform: uppercase; background-color: black; color: #90CAF9; .th { border-bottom: 1px solid #565656; height: 36px; } `, Row: ` background-color: #1e1e1e; position: relative; cursor: pointer; .td { border-top: 1px solid #565656; border-bottom: 1px solid #565656; } &:hover .td { border-top: 1px solid #177ac9; border-bottom: 1px solid #177ac9; } &:nth-of-type(odd) .td { background-color: #303030; } ` }); const fetchEntities = useCallback(async () => { try { const response = await EMSESP.readEntities(); setOriginalEntity(response.data.entity); } catch (error) { setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING())); } }, [LL]); useEffect(() => { fetchEntities(); }, [fetchEntities]); const setOriginalEntity = (data: EntityItem[]) => { setEntity( data.map((ei) => ({ ...ei, o_device_id: ei.device_id, o_type_id: ei.type_id, o_offset: ei.offset, o_factor: ei.factor, o_uom: ei.uom, o_val_type: ei.val_type, o_name: ei.name, o_deleted: ei.deleted })) ); }; function hasEntityChanged(ei: EntityItem) { return ( ei.device_id !== ei.o_device_id || ei.type_id !== ei.o_type_id || ei.name !== ei.o_name || ei.offset !== ei.o_offset || ei.uom !== ei.o_uom || ei.factor !== ei.o_factor || ei.val_type !== ei.o_val_type || ei.deleted !== ei.o_deleted ); } const getNumChanges = () => { if (!entity) { return 0; } return entity.filter((ei) => hasEntityChanged(ei)).length; }; const saveEntity = async () => { if (entity) { try { const response = await EMSESP.writeEntities({ entity: entity .filter((ei) => !ei.deleted) .map((condensed_ei) => { return { device_id: condensed_ei.device_id, type_id: condensed_ei.type_id, offset: condensed_ei.offset, factor: condensed_ei.factor, val_type: condensed_ei.val_type, uom: condensed_ei.uom, name: condensed_ei.name }; }) }); if (response.status === 200) { toast.success(LL.SUCCESS()); } else { toast.error(LL.PROBLEM_UPDATING()); } } catch (error) { toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } setOriginalEntity(entity); } }; const editEntityItem = (ei: EntityItem) => { setCreating(false); setEntityItem(ei); }; const addEntityItem = () => { setCreating(true); setEntityItem(emptyEntity); }; const updateEntityItem = () => { if (entityItem) { setEntity([...entity.filter((ei) => creating || ei.o_name !== entityItem.o_name), entityItem]); } setEntityItem(undefined); }; function formatValue(value: any, uom: number) { if (value === undefined) { return ''; } if (uom === 0) { return new Intl.NumberFormat().format(value); } return new Intl.NumberFormat().format(value) + ' ' + DeviceValueUOM_s[uom]; } function showHex(value: string, digit: number) { if (digit === 4) { return '0x' + ('000' + value).slice(-4); } return '0x' + ('0' + value).slice(-2); } const renderEntity = () => { if (!entity) { return ; } return ( !ei.deleted).sort((a, b) => a.name.localeCompare(b.time)) }} theme={entity_theme} layout={{ custom: true }} > {(tableList: any) => ( <>
{LL.NAME(0)} Device ID Type ID Offset {LL.VALUE()}
{tableList.map((ei: EntityItem) => ( editEntityItem(ei)}> {ei.name} {showHex(ei.device_id, 2)} {showHex(ei.type_id, 4)} {ei.offset} {formatValue(ei.value, ei.uom)} ))} )}
); }; const removeEntityItem = (ei: EntityItem) => { ei.deleted = true; setEntityItem(ei); updateEntityItem(); }; const validateEntityItem = async () => { if (entityItem) { try { setFieldErrors(undefined); await validate(entityItemValidation(entity, entityItem), entityItem); updateEntityItem(); } catch (errors: any) { setFieldErrors(errors); } } }; const closeDialog = () => { setEntityItem(undefined); setFieldErrors(undefined); }; const renderEditEntity = () => { if (entityItem) { return ( closeDialog()}> {creating ? LL.ADD(1) + ' ' + LL.NEW() : LL.EDIT()} {LL.CUSTOM_ENTITIES()} 0x }} /> 0x }} /> BOOL INT UINT SHORT USHORT ULONG TIME {entityItem.val_type !== 0 && ( <> {DeviceValueUOM_s.map((val, i) => ( {val} ))} )} {!creating && ( )} ); } }; return ( {blocker ? : null} {LL.ENTITIES_HELP_1()} {renderEntity()} {renderEditEntity()} {numChanges !== 0 && ( )} ); }; export default SettingsEntities;