diff --git a/src/_metronic/layout/components/aside/AsideMenuMain.tsx b/src/_metronic/layout/components/aside/AsideMenuMain.tsx index 5f81977..74d208f 100755 --- a/src/_metronic/layout/components/aside/AsideMenuMain.tsx +++ b/src/_metronic/layout/components/aside/AsideMenuMain.tsx @@ -72,7 +72,11 @@ export function AsideMenuMain() { icon='/media/icons/duotune/coding/cod009.svg' > - + diff --git a/src/app/modules/production/ProductionPage.tsx b/src/app/modules/production/ProductionPage.tsx index b099833..7bd3fdf 100755 --- a/src/app/modules/production/ProductionPage.tsx +++ b/src/app/modules/production/ProductionPage.tsx @@ -51,6 +51,7 @@ const DownStatusPage = lazy(() => import('./components/setup/downType/DownStatus const ModelsForManufacturer = lazy( () => import('./components/setup/equipment/ModelsForManufacturer') ) + const ModelClass = lazy(() => import('./components/setup/equipment/ModelClass')) const EquipmentRegister = lazy(() => import('./components/equipment-register/EquipmentRegister')) @@ -383,10 +384,10 @@ const ProductionPage: React.FC = () => { /> - All Model Class + All Equipment Types }> @@ -1113,7 +1114,7 @@ const ProductionPage: React.FC = () => { element={ <> - Metering By Model Class Summary{' '} + Metering By Equip. Type Summary }> diff --git a/src/app/modules/production/components/entries/backlog/Backlog.tsx b/src/app/modules/production/components/entries/backlog/Backlog.tsx index 9eb5bc8..0ffa6c0 100755 --- a/src/app/modules/production/components/entries/backlog/Backlog.tsx +++ b/src/app/modules/production/components/entries/backlog/Backlog.tsx @@ -283,6 +283,7 @@ const Backlog = () => { onSuccess: () => { message.success('Backlog completed successfully') queryClient.invalidateQueries('backlog') + queryClient.invalidateQueries('completedBacklogs') setIsCompleting(false) setIsModalOpen(false) setSubmitLoading(false) diff --git a/src/app/modules/production/components/entries/backlog/CompletedBacklogs.tsx b/src/app/modules/production/components/entries/backlog/CompletedBacklogs.tsx index a08fef7..3763964 100644 --- a/src/app/modules/production/components/entries/backlog/CompletedBacklogs.tsx +++ b/src/app/modules/production/components/entries/backlog/CompletedBacklogs.tsx @@ -93,9 +93,12 @@ const CompletedBacklogs = () => { { title: 'Days', render: (text: any, record: any) => { - return Math.round( - parseFloat(dayjs(record?.cdate).diff(dayjs(record?.bdate), 'day', true).toFixed(2)) - ) + if (record?.cdate >= record?.bdate) { + return Math.round( + parseFloat(dayjs(record?.cdate).diff(dayjs(record?.bdate), 'day', true).toFixed(2)) + ) + } + return 'N/A' }, }, { diff --git a/src/app/modules/production/components/equipment-register/Add.tsx b/src/app/modules/production/components/equipment-register/Add.tsx index 6446291..28c3fb2 100755 --- a/src/app/modules/production/components/equipment-register/Add.tsx +++ b/src/app/modules/production/components/equipment-register/Add.tsx @@ -1,7 +1,7 @@ -import {Button, DatePicker, Form, Input, InputNumber, message, Select, Steps} from 'antd' +import {Button, Cascader, DatePicker, Form, Input, InputNumber, message, Select, Steps} from 'antd' import React, {useState} from 'react' import {KTCard, KTCardBody} from '../../../../../_metronic/helpers' -import {getModels, postEquipment} from '../../../../urls' +import {getModelClasses, postEquipment} from '../../../../urls' import {useMutation, useQuery, useQueryClient} from 'react-query' import {Link} from 'react-router-dom' import {useAuth} from '../../../auth' @@ -11,19 +11,22 @@ import { DoneAllOutlined, InfoRounded, } from '@mui/icons-material' - +interface Option { + value: string | number + label: string + children?: Option[] +} const AddEquipRegister = () => { const {Step} = Steps let [form] = Form.useForm() let [generalInfo] = Form.useForm() const queryClient = useQueryClient() const {tenant} = useAuth() - const {data: listOfModels} = useQuery('models', () => getModels(tenant)) const {mutate: addEquipment, isLoading: submitLoading} = useMutation( (data) => postEquipment(data, tenant), { onSuccess: () => { - queryClient.invalidateQueries('listOfEquipment') + queryClient.invalidateQueries('equipments') message.success('Equipment Added Successfully') form.resetFields() generalInfo.resetFields() @@ -35,6 +38,10 @@ const AddEquipRegister = () => { }, } ) + const {data: listOfModelClass, isLoading: isModelClassloading} = useQuery('modelClassQuery', () => + getModelClasses(tenant) + ) + console.log('listOfModelClass', listOfModelClass) function onDetailsFinish(values: any) { setDetailsState(values) @@ -51,6 +58,8 @@ const AddEquipRegister = () => { ...detailsState, ...generalInfoState, } + formData['modelId'] = formData['modelClass'][1] + formData['modelClass'] = undefined const formDataWithoutUndefined = Object.keys(formData).reduce((acc: any, key: any) => { if (formData[key] !== undefined) { acc[key] = formData[key] @@ -59,7 +68,6 @@ const AddEquipRegister = () => { }, {}) addEquipment(formDataWithoutUndefined) } - const DetailsForm = ({onDetailsFinish}: any) => { return (
{ title='Add Equipment' >
+
+ { + // //allow selecting only if the equipment id field is not empty + // if (form.getFieldValue('equipmentId') === undefined) { + // return Promise.reject(new Error('Please enter Equipment ID first!')) + // } + // if (value === undefined) { + // return Promise.reject(new Error('Please select Equipment Type!')) + // } + // return Promise.resolve() + // }, + // }, + ]} + > + {/**/} + {/* {listOfModelClass?.data?.map((item: any) => (*/} + {/* */} + {/* {item.name}*/} + {/* /!*({item?.manufacturer?.name?.trim()})*!/*/} + {/* */} + {/* ))}*/} + {/**/} + + +
+
{ { required: true, pattern: new RegExp(/^[a-zA-Z0-9]+$/), - message: 'Equipment ID must be alphanumeric', + message: 'Alphanumeric Equipment ID Required!', + }, + { + validator: async (_, value) => { + //check if the equipment id first characters match with the model class code + if (value === undefined) { + return Promise.reject(new Error()) + } + + if (!form.getFieldValue('modelClass')) { + return Promise.reject(new Error('Please Select Equipment Type First!')) + } + const modelClassSelected = listOfModelClass?.data?.find((modelClass: any) => { + return modelClass?.modelClassId === form.getFieldValue('modelClass')[0] + }) + if (listOfModelClass?.data) { + if ( + !value + ?.trim() + ?.toLowerCase() + ?.startsWith(modelClassSelected?.code?.trim()?.toLowerCase()) + ) { + return Promise.reject( + new Error(`Equipment ID should start with ${modelClassSelected?.code}!`) + ) + } + } + }, }, ]} + className='form-control form-control-solid' >
-
- - - -
@@ -112,19 +182,38 @@ const AddEquipRegister = () => { />
+ {/*
*/} + {/* */} + {/* */} + {/* {modelsUnderSelectedClass?.data?.map((item: any) => (*/} + {/* */} + {/* {item.name}{' '}*/} + {/* ({item?.manufacturer?.name?.trim()})*/} + {/* */} + {/* ))}*/} + {/* */} + {/* */} + {/*
*/}
- - + +
@@ -191,6 +280,7 @@ const AddEquipRegister = () => { { @@ -767,13 +769,7 @@ const UpdateRegister = () => {
- - {/* console.log(value)}*/} - {/* // style={{ width: 200 }}*/} - {/*/>*/} - +
column.visible !== false)} bordered rowKey={(record: any) => record.manufacturerId} dataSource={manufacturers?.data} diff --git a/src/app/modules/production/components/setup/equipment/ModelClass.tsx b/src/app/modules/production/components/setup/equipment/ModelClass.tsx index 4e417e4..dfaf450 100755 --- a/src/app/modules/production/components/setup/equipment/ModelClass.tsx +++ b/src/app/modules/production/components/setup/equipment/ModelClass.tsx @@ -16,10 +16,9 @@ const ModelClass = () => { const {data: modelClassData, isLoading: modelClassDataLoading} = useQuery('modelClassQuery', () => getModelClasses(tenant) ) - console.log('modelClassData', modelClassData) const {mutate: addModelClass} = useMutation((data) => postModelClass(data, tenant), { onSuccess: () => { - message.success('ModelClass Added Successfully') + message.success('Equipment Type Added Successfully') queryClient.invalidateQueries('modelClassQuery') form.resetFields() setSubmitLoading(false) @@ -33,7 +32,7 @@ const ModelClass = () => { }) const {mutate: updateModelClass} = useMutation((data) => putModelClass(data, tenant), { onSuccess: () => { - message.success('ModelClass Updated Successfully') + message.success('Equipment Type Updated Successfully') queryClient.invalidateQueries('modelClassQuery') form.resetFields() setSubmitLoading(false) @@ -47,7 +46,7 @@ const ModelClass = () => { }) const {mutate: removeModelClass} = useMutation((id: any) => deleteModelClass(id), { onSuccess: () => { - message.success('ModelClass Deleted Successfully') + message.success('Equipment Type Deleted Successfully') queryClient.invalidateQueries('modelClassQuery') setSubmitLoading(false) }, @@ -59,7 +58,7 @@ const ModelClass = () => { function handleDelete(record: any) { if (record?.models?.length > 0) { - message.error('Model Class is in use') + message.error('Equipment Type is in use') return } removeModelClass(record?.modelClassId) @@ -83,6 +82,7 @@ const ModelClass = () => { { title: 'ModelClass ID', dataIndex: 'modelClassId', + visible: false, sorter: (a: any, b: any) => { if (a.modelClassId > b.modelClassId) { return 1 @@ -95,7 +95,7 @@ const ModelClass = () => { defaultSortOrder: 'descend', }, { - title: 'Code', + title: 'Prefix', dataIndex: 'code', sorter: (a: any, b: any) => { if (a.code > b.code) { @@ -165,7 +165,9 @@ const ModelClass = () => {
column?.visible !== false && column?.dataIndex !== 'modelClassId' + )} bordered dataSource={modelClassData?.data} loading={modelClassDataLoading} @@ -211,8 +213,8 @@ const ModelClass = () => { )} - - + + diff --git a/src/app/modules/production/components/setup/equipment/ModelsForManufacturer.tsx b/src/app/modules/production/components/setup/equipment/ModelsForManufacturer.tsx index 3e580f2..82c6f5b 100755 --- a/src/app/modules/production/components/setup/equipment/ModelsForManufacturer.tsx +++ b/src/app/modules/production/components/setup/equipment/ModelsForManufacturer.tsx @@ -74,6 +74,7 @@ const ModelsForManufacturer = () => { { title: 'Model ID', dataIndex: 'modelId', + visible: false, defaultSortOrder: 'descend', sorter: (a: any, b: any) => { if (a.modelId > b.modelId) { @@ -112,7 +113,7 @@ const ModelsForManufacturer = () => { }, }, { - title: 'Model Class', + title: 'Equipment Type', dataIndex: 'modelClass', sorter: (a: any, b: any) => { if (a.modelClass?.name > b.modelClass?.name) { @@ -222,7 +223,7 @@ const ModelsForManufacturer = () => {
column.visible !== false)} bordered loading={isModelDataLoading} dataSource={modelData?.data?.filter( @@ -276,8 +277,8 @@ const ModelsForManufacturer = () => { - - {modelClassData?.data?.map((modelClass: any) => ( {modelClass.name} diff --git a/src/app/modules/production/components/setup/fleet/FleetPage.tsx b/src/app/modules/production/components/setup/fleet/FleetPage.tsx index 17b0a1b..ecea9d2 100755 --- a/src/app/modules/production/components/setup/fleet/FleetPage.tsx +++ b/src/app/modules/production/components/setup/fleet/FleetPage.tsx @@ -37,7 +37,7 @@ const FleetPage = () => { sorter: (a: any, b: any) => a.modlName - b.modlName, }, { - title: 'Model Class', + title: 'Equipment Type', dataIndex: 'modlClass', sorter: (a: any, b: any) => a.modlClass - b.modlClass, }, diff --git a/src/app/pages/dashboard/DevexpressDashboardComponent.jsx b/src/app/pages/dashboard/DevexpressDashboardComponent.jsx index cc7c512..760c44d 100755 --- a/src/app/pages/dashboard/DevexpressDashboardComponent.jsx +++ b/src/app/pages/dashboard/DevexpressDashboardComponent.jsx @@ -7,12 +7,25 @@ import React, {lazy, Suspense} from 'react' import TopBarProgress from 'react-topbar-progress-indicator' import {SERVER} from '../../urls' import {useAuth} from '../../modules/auth' -import 'devexpress-dashboard/model/parameters/parameter' -import {Parameter} from 'devexpress-dashboard/model' + const DashboardControl = lazy(() => import('devexpress-dashboard-react')) const DevexpressDashboardComponent = (props) => { const {tenant} = useAuth() + function onBeforeRender(e) { + const dashboardControl = e.component + dashboardControl.setDashboardState({ + Parameters: { + TenantId: [tenant], + }, + }) + //hide the parameter panel if there is only one parameter + var parameterExtension = dashboardControl.findExtension('dashboard-parameter-dialog') + if (parameterExtension) { + parameterExtension.showDialogButton(false) + } + } + return ( }>
@@ -20,34 +33,9 @@ const DevexpressDashboardComponent = (props) => { id='web-dashboard' style={{height: '100%'}} endpoint={`${SERVER}/dashboards/dashboardcontrol`} - //optional configuration with default values workingMode={props.workingMode ? props.workingMode : 'ViewerOnly'} - dashboardId={props.dashboardId} // or a path to a dashboard file - // onDashboardInitializing={(e) => { - // const parameters = e.dashboard.parameters - // let p = new Parameter() - // p.name('TenantId') - // p.defaultValue('tarkwa') - // p.allowMultiselect(true) - // p.allowNull(true) - // p.description('Company') - // p.visible = true - // parameters.removeAll() - // parameters.push(p) - // }} - // onDashboardInitialized={(e) => { - // let p = new Parameter() - // const parameters = e.dashboard.parameters - // p.name('TenantId') - // p.defaultValue('tarkwa') - // p.description('Company') - // p.visible = true - // parameters.removeAll() - // parameters.push(p) - // }} - // onBeforeRender={(e) => { - // const parameters = e.component. - // }} + dashboardId={props.dashboardId} + onDashboardInitialized={onBeforeRender} >
diff --git a/src/app/pages/dashboard/dashboardSettings/ParameterItem.js b/src/app/pages/dashboard/dashboardSettings/ParameterItem.js new file mode 100644 index 0000000..7b69f89 --- /dev/null +++ b/src/app/pages/dashboard/dashboardSettings/ParameterItem.js @@ -0,0 +1,246 @@ +import {CustomItemViewer} from 'devexpress-dashboard/common' +import {CustomItem} from 'devexpress-dashboard/model' +import {FormItemTemplates} from 'devexpress-dashboard/designer' +import dxButton from 'devextreme/ui/button' + +const PARAMETER_EXTENSION_NAME = 'ParameterItem' + +const svgIcon = + ` + + + + + + ` +const onOffButtons = [{text: 'On'}, {text: 'Off'}] +const buttonsStyle = { + containerHeight: 60, + height: 40, + width: 82, + marginRight: 15, + marginTop: 10, +} +const parameterMetadata = { + customProperties: [ + { + ownerType: CustomItem, + propertyName: 'showHeaders', + valueType: 'string', + defaultValue: 'On', + }, + { + ownerType: CustomItem, + propertyName: 'showParameterName', + valueType: 'string', + defaultValue: 'On', + }, + { + ownerType: CustomItem, + propertyName: 'automaticUpdates', + valueType: 'string', + defaultValue: 'Off', + }, + ], + optionsPanelSections: [ + { + title: 'Parameters settings', + items: [ + { + dataField: 'showHeaders', + template: FormItemTemplates.buttonGroup, + editorOptions: { + items: onOffButtons, + }, + }, + { + dataField: 'showParameterName', + template: FormItemTemplates.buttonGroup, + editorOptions: { + items: onOffButtons, + }, + }, + { + dataField: 'automaticUpdates', + template: FormItemTemplates.buttonGroup, + editorOptions: { + items: onOffButtons, + }, + }, + ], + }, + ], + icon: PARAMETER_EXTENSION_NAME, + title: 'Parameters', +} + +class ParameterItemViewer extends CustomItemViewer { + constructor(model, container, options, parameterExtension) { + super(model, container, options) + + this.buttons = [] + + this.parameterExtension = parameterExtension + this._subscribeProperties() + this.parameterExtension.showDialogButton(false) + this.parameterExtension.subscribeToContentChanges(() => { + this._generateParametersContent() + }) + this.dialogButtonSubscribe = this.parameterExtension.showDialogButton.subscribe(() => { + this.parameterExtension.showDialogButton(false) + }) + } + + setSize(width, height) { + super.setSize(width, height) + this._setGridHeight() + } + dispose() { + super.dispose() + this.parametersContent && this.parametersContent.dispose && this.parametersContent.dispose() + this.dialogButtonSubscribe.dispose() + this.parameterExtension.showDialogButton(true) + this.buttons.forEach((button) => button.dispose()) + } + renderContent(dxElement, changeExisting) { + let element = dxElement.jquery ? dxElement.get(0) : dxElement + if (!changeExisting) { + while (element.firstChild) element.removeChild(element.firstChild) + this.buttons.forEach((button) => button.dispose()) + element.style.overflow = 'auto' + + this.gridContainer = document.createElement('div') + + element.appendChild(this.gridContainer) + this._generateParametersContent() + + this.buttonContainer = document.createElement('div') + + this.buttonContainer.style.height = buttonsStyle.containerHeight + 'px' + this.buttonContainer.style.width = + buttonsStyle.width * 2 + buttonsStyle.marginRight * 2 + 'px' + this.buttonContainer.style.cssFloat = 'right' + + element.appendChild(this.buttonContainer) + + this.buttons.push( + this._createButton(this.buttonContainer, 'Reset', () => { + this.parametersContent.resetParameterValues() + }) + ) + this.buttons.push( + this._createButton(this.buttonContainer, 'Submit', () => { + this._submitValues() + }) + ) + if (this.getPropertyValue('automaticUpdates') !== 'Off') + this.buttonContainer.style.display = 'none' + } + } + _generateParametersContent() { + this.parametersContent = this.parameterExtension.renderContent(this.gridContainer) + this.parametersContent.valueChanged.add(() => this._updateParameterValues()) + this._setGridHeight() + this._update({ + showHeaders: this.getPropertyValue('showHeaders'), + showParameterName: this.getPropertyValue('showParameterName'), + }) + } + _submitValues() { + this.parametersContent.submitParameterValues() + this._update({ + showHeaders: this.getPropertyValue('showHeaders'), + showParameterName: this.getPropertyValue('showParameterName'), + }) + } + _updateParameterValues() { + if (this.getPropertyValue('automaticUpdates') !== 'Off') { + this._submitValues() + } + } + _setGridHeight() { + let gridHeight = this.contentHeight() + if (this.getPropertyValue('automaticUpdates') === 'Off') + gridHeight -= buttonsStyle.containerHeight + this.parametersContent.grid.option('height', gridHeight) + } + _createButton(container, buttonText, onClick) { + let button = document.createElement('div') + button.style.marginRight = buttonsStyle.marginRight + 'px' + button.style.marginTop = buttonsStyle.marginTop + 'px' + container.appendChild(button) + return new dxButton(button, { + text: buttonText, + height: buttonsStyle.height + 'px', + width: buttonsStyle.width + 'px', + onClick: onClick, + }) + } + _subscribeProperties() { + this.subscribe('showHeaders', (showHeaders) => { + this._update({showHeaders: showHeaders}) + }) + this.subscribe('showParameterName', (showParameterName) => { + this._update({showParameterName: showParameterName}) + }) + this.subscribe('automaticUpdates', (automaticUpdates) => { + this._update({automaticUpdates: automaticUpdates}) + }) + } + _update(options) { + if (!!options.showHeaders) { + this.parametersContent.grid.option('showColumnHeaders', options.showHeaders === 'On') + } + if (!!options.showParameterName) { + this.parametersContent.valueChanged.empty() + this.parametersContent.grid.columnOption(0, 'visible', options.showParameterName === 'On') + this.parametersContent.valueChanged.add(() => { + return this._updateParameterValues() + }) + } + if (!!options.automaticUpdates) { + switch (options.automaticUpdates) { + case 'Off': + this.buttonContainer.style.display = 'block' + break + case 'On': + this.buttonContainer.style.display = 'none' + break + default: + break + } + } + this._setGridHeight() + } +} + +class ParameterItem { + constructor(dashboardControl) { + dashboardControl.registerIcon(svgIcon) + this.dashboardControl = dashboardControl + this.name = PARAMETER_EXTENSION_NAME + this.metaData = parameterMetadata + } + + createViewerItem = (model, $element, content) => { + var parameterExtension = this.dashboardControl.findExtension('dashboard-parameter-dialog') + if (!parameterExtension) { + throw Error( + 'The "dashboard-parameter-dialog" extension does not exist. To register this extension, use the DashboardControl.registerExtension method.' + ) + } + return new ParameterItemViewer(model, $element, content, parameterExtension) + } +} + +export default ParameterItem