diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index c9b3557601b96c3d7689913620d02a20ed4e3cb5..41e07bed2d4a87f00d40d2c1dade10a4b0ca8543 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -192,7 +192,7 @@ class App extends Component { onBackButtonEvent = (e) => { e.preventDefault(); if (this.state.isEditDirty) { - const leavePage = window.confirm("Do you want to leave this page? Your changes may not be saved."); + const leavePage = window.confirm("Do you want to discard your changes? Your changes may not be saved."); if (leavePage) { this.setState({isEditDirty: false}); window.history.back(); @@ -283,8 +283,10 @@ class App extends Component { } </> <CustomDialog type="confirmation" visible={this.state.showDirtyDialog} width="40vw" - header={'Confirmation'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Confirmation'} message={'Do you want to discard your changes? Your changes may not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]}> </CustomDialog> </div> </Provider> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js index ca787e5e175bc129bf07ed8491d0170496ef82f7..e74ea5c157c79d13e1a9ca612295b58887ff7a3e 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/JSONEditor/JEditor.js @@ -373,6 +373,7 @@ function Jeditor(props) { disable_edit_json: true, disable_properties: true, disable_collapse: false, + prompt_before_delete: false, show_errors: props.errorsOn ? props.errorsOn : 'change', // Can be 'interaction', 'change', 'always', 'never' compact: true, ajax: true, @@ -407,7 +408,10 @@ function Jeditor(props) { editor.on('change',(e) => { setEditorOutput(e); }); - + // Called when a row is deleted + // editor.on('deleteRow', (e) => { + // console.log(e); + // }); //adding onchange event for subband if(subBandProps) { subBandProps.forEach(function(pathName){ diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss index 4084cac226ef81e7ff1efac5e8fc37b82af6b0e5..b78481b37d26a8a7cd48dbe806dc4433a7a4beb6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/_overrides.scss @@ -531,4 +531,9 @@ h3 + div + p { .p-picklist-buttons-cell { margin-top: 2em; -} \ No newline at end of file +} + +.act-btn-grp{ + margin-top: 0.5em !important; + margin-left: 0.25em; +} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js index 91cffa436258075c353647bd486b4c7501f34ad1..618cde5ba4cf2bc1c6119b0ca22b8a1516aba85b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/components/CustomDialog.js @@ -35,7 +35,7 @@ export class CustomDialog extends Component { <> <Button key="submit" type="primary" onClick={this.props.onSubmit?this.props.onSubmit:this.props.onClose} label={isConfirm?'Yes':'Ok'} /> {isConfirm && - <Button key="back" onClick={this.props.onCancel} label="No" /> + <Button key="back" onClick={this.props.onCancel} className='act-btn-cancel' label="No" /> } </> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_buttongroups.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_buttongroups.scss new file mode 100644 index 0000000000000000000000000000000000000000..155425065521147d62bc7af13ee255f7b4723d19 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_buttongroups.scss @@ -0,0 +1,32 @@ +/* +CSS for buttons +*/ +.act-btn-accept { + color: #ffffff; + background: #007ad9; + border: 1px solid #007ad9; +} + +.act-btn-cancel { + color: #000000; + background: #d1cecf; + border: 1px solid #d1cecf; +} + +.act-btn-cancel:enabled:hover { + color: #000000; + background: #b3adaf; + border: 1px solid #b3adaf; +} + +.act-btn-dispose { + color: #ffffff; + background: #e91224; + border: 1px solid #e91224; +} + +.act-btn-dispose:enabled:hover { + color: #ffffff; + background: #bd0716; + border: 1px solid #bd0716; +} \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss index 00fa0131c6b9232b347b5e08bd9a944c2dd832af..b6df9673214598a11da65aa64acd7a695c84a665 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss +++ b/SAS/TMSS/frontend/tmss_webapp/src/layout/sass/_layout.scss @@ -23,4 +23,5 @@ @import "./workflow"; @import "./_report"; @import "./customdialog"; +@import "./_buttongroups"; // @import "./splitpane"; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.js index cbc6a438390a6b9ff1a9eedf3f8e67a98dad661b..f06365b62dc82b786c2b6917ad7f271890c3629a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.js @@ -6,14 +6,14 @@ import { Calendar } from 'primereact/calendar'; import { InputTextarea } from 'primereact/inputtextarea'; import { Dropdown } from 'primereact/dropdown'; import { Button } from 'primereact/button'; -import { Dialog } from 'primereact/dialog'; import { Toast } from 'primereact/toast'; +import { Checkbox } from 'primereact/checkbox'; import { ResourceInputList } from '../../components/Resources/ResourceInputList'; import { CustomDialog } from '../../layout/components/CustomDialog'; import moment from 'moment'; import { publish } from '../../App'; import _ from 'lodash'; - +import { appGrowl, setAppGrowl } from '../../layout/components/AppGrowl'; import AppLoader from '../../layout/components/AppLoader'; import PageHeader from '../../layout/components/PageHeader'; import CycleService from '../../services/cycle.service'; @@ -45,6 +45,8 @@ export class CycleCreate extends Component { errors: {}, // Validation Errors resources: [], // Selected Resources for Allocation resourceList: [], // Available Resources for Allocation + createAnother: false, + multiCreateCount: 1 } // Validateion Rules this.formRules = { @@ -194,14 +196,15 @@ export class CycleCreate extends Component { setCycleQuotaParams(key, event) { let cycleQuota = _.cloneDeep(this.state.cycleQuota); const previousValue = cycleQuota[key]; - if (event.target.value) { + const eventValue = event.target?.value || event.value + if (eventValue) { let resource = _.find(this.state.resources, {'name': key}); let newValue = 0; if (this.resourceUnitMap[resource.quantity_value] && - event.target.value.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { - newValue = _.trim(event.target.value.replace(this.resourceUnitMap[resource.quantity_value].display,'')); + eventValue.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { + newValue = _.trim(eventValue.replace(this.resourceUnitMap[resource.quantity_value].display,'')); } else { - newValue = _.trim(event.target.value); + newValue = _.trim(eventValue); } //If value entered is not one decimal number then don't change the value and keep previous value. cycleQuota[key] = (newValue==="NaN" || !/^[0-9]*(\.[0-9]{0,1})?$/.exec(newValue))?previousValue:(newValue); @@ -307,16 +310,19 @@ export class CycleCreate extends Component { CycleService.saveCycle(this.state.cycle, this.defaultResourcesEnabled?cycleQuota:[]) .then(cycle => { - if (cycle.url) { - let dialog = {}; - if (this.defaultResourcesEnabled) { - dialog = {header: 'Success', detail: 'Cycle saved successfully. Do you want to create another Cycle?'}; + if (appGrowl === null) { + setAppGrowl(this.growl) + } + if (cycle.url) { + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Cycle saved successfully.'}); + if (this.state.createAnother) { + this.setState({cycle:cycle}); + this.reset(); } else { - dialog = {header: 'Success', detail: 'Cycle saved successfully with default Resource allocations. Do you want to view and edit them?'}; + this.setState({cycle:cycle, redirect: `/cycle`}); } - this.setState({cycle:cycle, dialogVisible: true, dialog: dialog}) } else { - this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Cycle'}); + appGrowl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Cycle'}); this.setState({errors: cycle}); } }); @@ -342,9 +348,12 @@ export class CycleCreate extends Component { * Function to cancel form creation and navigate to other page/component */ cancelCreate() { - console.log('pp') publish('edit-dirty', false); - this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/cycle`); + if (this.state.multiCreateCount > 1) { + this.props.history.go(this.state.multiCreateCount*-1); + } else { + this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/cycle`); + } this.setState({showDialog: false}); } @@ -354,6 +363,7 @@ export class CycleCreate extends Component { reset() { if (this.defaultResourcesEnabled) { let prevResources = this.state.resources; + let multiCreateCount = this.state.multiCreateCount; let resourceList = []; let resources = []; if (resources) { @@ -378,7 +388,8 @@ export class CycleCreate extends Component { errors: {}, dialogVisible: false, resources: resources, - resourceList: resourceList + resourceList: resourceList, + multiCreateCount: ++multiCreateCount }); } else { this.setState({redirect: `/cycle/edit/${this.state.cycle.name}`}) @@ -509,12 +520,22 @@ export class CycleCreate extends Component { } </div> </div> + <div className="p-col-1.5" style={{marginTop: '1em', marginLeft: '0.75em'}}> + <Checkbox inputId="trigger" role="trigger" data-testid="createAnother" + tooltip="Select checkbox to create another Cycle after saving this cycle" + tooltipOptions={this.tooltipOptions} + checked={this.state.createAnother} + onChange={e => this.setState({'createAnother': e.target.checked})} + style={{marginRight: '0.25em',}}></Checkbox> + <label style={{display: 'inline'}}> Create another? </label> + + </div> <div className="p-grid p-justify-start act-btn-grp"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveCycle} disabled={!this.state.validForm} /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </> @@ -522,26 +543,12 @@ export class CycleCreate extends Component { {/* Dialog component to show messages and get input */} <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '25vw'}} inputid="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false, redirect: `/cycle/view/${this.state.cycle.name}`})}} label="No" /> - <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2"> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - <span style={{marginTop:"5px"}}>{this.state.dialog.detail}</span> - </div> - </div> - </Dialog> - - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Add Cycle'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + <CustomDialog type="confirmation" visible={this.state.showDialog} width="45vw" + header={'Add Cycle'} message={'Do you want to discard your changes? A new cycle will not be created'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} + > </CustomDialog> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.test.js index 158b9d44b5b46e6fc8936fa7673c376be507cb85..93e4bfabfe85ec2f6abcc6b695cd4bc93deabeaa 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/create.test.js @@ -115,9 +115,10 @@ it("save cycle with mandatory fields", async () => { content = render(<Router><CycleCreate location={{pathname: '/cycle/create'}} unitMap={UnitConverter.resourceUnitMap} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); - + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); fireEvent.change(nameInput, { target: { value: 'Cycle-12' } }); expect(nameInput.value).toBe("Cycle-12"); fireEvent.change(descInput, { target: { value: 'Cycle-12' } }); @@ -140,11 +141,13 @@ it("save cycle with mandatory fields", async () => { await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); - - // After saving cycle, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('cycleId').value).toBe("http://localhost:3000/api/cycle/Cycle-12"); - expect(content.queryByText("Success")).not.toBe(null); + console.log("Checking service calls.."); + expect(saveCycleSpy).toHaveBeenCalled(); + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); }); it("save cycle with default resources", async () => { @@ -154,10 +157,11 @@ it("save cycle with default resources", async () => { content = render(<Router><CycleCreate location={{pathname: '/cycle/create'}} unitMap={UnitConverter.resourceUnitMap} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); const spinButtons = content.queryAllByRole("spinbutton"); - + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); fireEvent.change(nameInput, { target: { value: 'Cycle-12' } }); expect(nameInput.value).toBe("Cycle-12"); fireEvent.change(descInput, { target: { value: 'Cycle-12' } }); @@ -205,11 +209,13 @@ it("save cycle with default resources", async () => { await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); - - // After saving cycle, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('cycleId').value).toBe("http://localhost:3000/api/cycle/Cycle-12"); - expect(content.queryByText("Success")).not.toBe(null); + console.log("Checking service calls.."); + expect(saveCycleSpy).toHaveBeenCalled(); + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); }); it("save cycle with added resources", async () => { @@ -219,10 +225,11 @@ it("save cycle with added resources", async () => { content = render(<Router><CycleCreate location={{pathname: '/cycle/create'}} unitMap={UnitConverter.resourceUnitMap} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); let spinButtons = content.queryAllByRole("spinbutton"); - + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); fireEvent.change(nameInput, { target: { value: 'Cycle-12' } }); expect(nameInput.value).toBe("Cycle-12"); fireEvent.change(descInput, { target: { value: 'Cycle-12' } }); @@ -288,14 +295,15 @@ it("save cycle with added resources", async () => { fireEvent.change(newResourceInput, { target: { value: 30 } }); expect(newResourceInput.value).toBe('30'); - await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); - - // After saving cycle, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('cycleId').value).toBe("http://localhost:3000/api/cycle/Cycle-12"); - expect(content.queryByText("Success")).not.toBe(null); + console.log("Checking service calls.."); + expect(saveCycleSpy).toHaveBeenCalled(); + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); }); it("remove default resource and added resource", async () => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js index f78d58cb81b380386e333d2951ccf54cf59b765c..2ee83ace3b38ce85ee40d29d2d9cafdaf10fff89 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js @@ -19,8 +19,7 @@ import PageHeader from '../../layout/components/PageHeader'; import CycleService from '../../services/cycle.service'; import UnitConverter from '../../utils/unit.converter'; import UIConstants from '../../utils/ui.constants'; - - +import { appGrowl } from '../../layout/components/AppGrowl'; export class CycleEdit extends Component { constructor(props) { @@ -236,14 +235,15 @@ export class CycleEdit extends Component { setCycleQuotaParams(key, event) { let cycleQuota = _.cloneDeep(this.state.cycleQuota); const previousValue = cycleQuota[key]; - if (event.target.value) { + const eventValue = event.target?.value || event.value; + if (eventValue) { let resource = _.find(this.state.resources, {'name': key}); let newValue = 0; if (this.resourceUnitMap[resource.quantity_value] && - event.target.value.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { - newValue = _.trim(event.target.value.replace(this.resourceUnitMap[resource.quantity_value].display,'')); + eventValue.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { + newValue = _.trim(eventValue.replace(this.resourceUnitMap[resource.quantity_value].display,'')); } else { - newValue = _.trim(event.target.value); + newValue = _.trim(eventValue); } //If value entered is not one decimal number then don't change the value and keep previous value. cycleQuota[key] = (newValue==="NaN" || !/^[0-9]*(\.[0-9]{0,1})?$/.exec(newValue))?previousValue:(newValue); @@ -332,7 +332,7 @@ export class CycleEdit extends Component { .then(async (cycle) => { if (cycle && this.state.cycle.updated_at !== cycle.updated_at) { this.saveCycleQuota(cycle); - this.props.history.length>2 && this.state.isDirty? this.props.history.go(-2):this.props.history.go(-1); + //this.props.history.length>2 && this.state.isDirty? this.props.history.go(-2):this.props.history.go(-1); } else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to update Cycle'}); //this.setState({errors: cycle}); @@ -345,11 +345,9 @@ export class CycleEdit extends Component { * Function to Create, Update & Delete cycle_quota for the cycle */ async saveCycleQuota(cycle) { - let dialog = {}; let quotaError = {}; let updatingCycleQuota = []; let newCycleQuota = []; - let deletingCycleQuota = []; for (const resource in this.state.cycleQuota) { const resourceType = _.find(this.state.resources, {'name': resource}); const conversionFactor = this.resourceUnitMap[resourceType.quantity_value]?this.resourceUnitMap[resourceType.quantity_value].conversionFactor:1 @@ -385,11 +383,11 @@ export class CycleEdit extends Component { } } if (_.keys(quotaError).length === 0) { - dialog = {header: 'Success', detail: 'Cycle updated successfully.'}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Cycle updated successfully.'}); } else { - dialog = {header: 'Error', detail: 'Cycle updated successfully but resource allocation not updated properly.'}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Cycle updated successfully but resource allocation not updated properly.'}); } - this.setState({dialogVisible: true, dialog: dialog}); + this.setState({redirect: `/cycle/view/${cycle.name}`}); } /** @@ -535,7 +533,7 @@ export class CycleEdit extends Component { <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveCycle} disabled={!this.state.validForm} /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> @@ -543,28 +541,12 @@ export class CycleEdit extends Component { } {/* Dialog component to show messages and get input */} <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '30vw'}} inputId="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelEdit();}} label="Ok" /> - {/* <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> */} - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2"> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - <span style={{marginTop:"5px"}}>{this.state.dialog.detail}</span> - </div> - </div> - </Dialog> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Cycle'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Edit Cycle'} message={'Do you want to discard your changes? Your changes to the cycle will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> - </div> </React.Fragment> ); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js index bc1ab25b9011ef24702fca9b424bf197c27012a1..05fa5f4bfd55754a223e54101833811730e01117 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js @@ -9,11 +9,10 @@ import { Checkbox } from 'primereact/checkbox'; import { Dropdown } from 'primereact/dropdown'; import { MultiSelect } from 'primereact/multiselect'; import { Button } from 'primereact/button'; -import { Dialog } from 'primereact/dialog'; import { Toast } from 'primereact/toast'; import { CustomDialog } from '../../layout/components/CustomDialog'; import { ResourceInputList } from '../../components/Resources/ResourceInputList'; - +import { appGrowl, setAppGrowl } from '../../layout/components/AppGrowl'; import AppLoader from '../../layout/components/AppLoader'; import PageHeader from '../../layout/components/PageHeader'; import CycleService from '../../services/cycle.service'; @@ -45,7 +44,8 @@ export class ProjectCreate extends Component { auto_pin: false, auto_ingest: false, piggyback_allowed_tbb: false, - piggyback_allowed_aartfaac: false + piggyback_allowed_aartfaac: false, + createAnother: false }, projectQuota: {}, // Resource Allocations validFields: {}, // For Validation @@ -57,7 +57,8 @@ export class ProjectCreate extends Component { resourceList: [], // Available Resources for Allocation cycles: [], archive_location:[], - cluster:[] + cluster:[], + multiCreateCount: 1 } // Validateion Rules this.formRules = { @@ -257,14 +258,15 @@ export class ProjectCreate extends Component { setProjectQuotaParams(key, event) { let projectQuota = this.state.projectQuota; const previousValue = projectQuota[key]; - if (event.target.value) { + const eventValue = event.target?.value || event.value + if (eventValue) { let resource = _.find(this.state.resources, {'name': key}); let newValue = 0; if (this.resourceUnitMap[resource.quantity_value] && - event.target.value.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { - newValue = _.trim(event.target.value.replace(this.resourceUnitMap[resource.quantity_value].display,'')); + eventValue.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { + newValue = _.trim(eventValue.replace(this.resourceUnitMap[resource.quantity_value].display,'')); } else { - newValue = _.trim(event.target.value); + newValue = _.trim(eventValue); } //If value entered is not one decimal number then don't change the value and keep previous value. projectQuota[key] = (newValue==="NaN" || !/^[0-9]*(\.[0-9]{0,1})?$/.exec(newValue))?previousValue:(newValue); @@ -348,19 +350,28 @@ export class ProjectCreate extends Component { ProjectService.saveProject(project, this.defaultResourcesEnabled?projectQuota:[]) .then(project => { if (project.url) { - let dialog = {}; + //let dialog = {}; + if (appGrowl === null) { + setAppGrowl(this.growl) + } if (project.isQuotaCreated) { if (this.defaultResourcesEnabled) { - dialog = {header: 'Success', detail: 'Project saved successfully. Do you want to create another project?'}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Project saved successfully.'}); } else { - dialog = {header: 'Success', detail: 'Project saved successfully with default Resource allocations. Do you want to view and edit them?'}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Project saved successfully with default Resource allocations.'}); } } else { - dialog = {header: 'Warning', detail: 'Project saved successfully, but resource allocation not saved.'}; + appGrowl.show({severity: 'info', summary: 'Success', detail: 'Project saved successfully, but resource allocation not saved.'}); + } + if (this.state.createAnother) { + this.setState({project:project, isDirty: false},() => publish('edit-dirty', false)); + this.reset(); + } else { + this.setState({project:project, isDirty: false, redirect: `/project`},() => publish('edit-dirty', false)); } - this.setState({project:project, dialogVisible: true, dialog: dialog, isDirty: false},() => publish('edit-dirty', false)); + } else { - this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Project'}); + appGrowl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Project'}); this.setState({errors: project, isDirty: false}); publish('edit-dirty', false); } @@ -388,7 +399,11 @@ export class ProjectCreate extends Component { */ cancelCreate() { publish('edit-dirty', false); - this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/project`); + if (this.state.multiCreateCount > 1) { + this.props.history.go(this.state.multiCreateCount*-1); + } else { + this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/project`); + } this.setState({showDialog: false}); } @@ -398,6 +413,7 @@ export class ProjectCreate extends Component { reset() { if (this.defaultResourcesEnabled) { let prevResources = this.state.resources; + let multiCreateCount = this.state.multiCreateCount; let resourceList = []; let resources = []; if (resources) { @@ -431,7 +447,8 @@ export class ProjectCreate extends Component { errors: {}, dialogVisible: false, resources: resources, - resourceList: resourceList + resourceList: resourceList, + multiCreateCount: ++multiCreateCount }); } else { this.setState({redirect: `/project/edit/${this.state.project.name}`}) @@ -627,12 +644,22 @@ export class ProjectCreate extends Component { <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> </div> </div> + <div className="p-col-1.5" style={{marginTop: '1em', marginLeft: '0.75em'}}> + <Checkbox inputId="trigger" role="trigger" data-testid="createAnother" + tooltip="Select checkbox to create another Project after saving this project" + tooltipOptions={this.tooltipOptions} + checked={this.state.createAnother} + onChange={e => this.setState({'createAnother': e.target.checked})} + style={{marginRight: '0.25em',}}></Checkbox> + <label style={{display: 'inline'}}> Create another? </label> + + </div> <div className="p-grid p-justify-start act-btn-grp"> - <div className="col-lg-1 col-md-2 col-sm-6"> + <div className="p-col-1"> <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} /> </div> - <div className="col-lg-1 col-md-2 col-sm-6"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <div className="p-col-1"> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </> @@ -640,26 +667,11 @@ export class ProjectCreate extends Component { {/* Dialog component to show messages and get input */} <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '25vw'}} inputId="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false, redirect: `/project/view/${this.state.project.name}`})}} label="No" /> - <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2"> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - {this.state.dialog.detail} - </div> - </div> - </Dialog> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" data-testid='dialog' - header={'Add Project'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Add Project'} message={'Do you want to discard your changes? A new project will not be created.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.test.js index 1a7f6d1745447d1c72d75661c877b235afccadce..f80dd87b76e3f5598d45516f6f4c49277c46a34f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.test.js @@ -200,8 +200,11 @@ it("save project with mandatory fields", async () => { content = render(<Router><ProjectCreate location={{pathname: "/project/create"}} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const spinButtons = content.queryAllByRole("spinbutton"); const rankInput = spinButtons.filter(function(element) { return element.id==="proj-rank"})[0]; @@ -215,12 +218,17 @@ it("save project with mandatory fields", async () => { expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); expect(content.queryByTestId('projectId').value).toBe(""); expect(content.queryByText("Success")).toBe(null); + await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); - // After saving project, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('projectId').value).toBe("http://localhost:3000/api/project/OSR"); - //expect(content.queryByText("Success")).not.toBe(null); + console.log("Checking service calls.."); + expect(saveProjectSpy).toHaveBeenCalled(); + //After project saved successful + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); }); it("save project with default resources", async () => { @@ -230,8 +238,10 @@ it("save project with default resources", async () => { content = render(<Router><ProjectCreate location={{pathname: "/project/create"}} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); const spinButtons = content.queryAllByRole("spinbutton"); const rankInput = spinButtons.filter(function(element) { return element.id==="proj-rank"})[0]; @@ -275,13 +285,19 @@ it("save project with default resources", async () => { const lofarSupTimeInput = _.filter(spinButtons, {"id": "LOFAR Support Time"})[0]; fireEvent.change(lofarSupTimeInput, { target: { value: 25 } }); expect(lofarSupTimeInput.value).toBe('25'); + await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); + console.log("Checking service calls.."); + expect(saveProjectSpy).toHaveBeenCalled(); - // After saving project, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('projectId').value).toBe("http://localhost:3000/api/project/OSR"); - //expect(content.queryByText("Success")).not.toBe(null); + //After project saved successful + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); + }); it("save project with added resources", async () => { @@ -291,8 +307,10 @@ it("save project with added resources", async () => { content = render(<Router><ProjectCreate location={{pathname: "/project/create"}} /></Router>); }); - const nameInput = content.queryByTestId('name'); + let nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); let spinButtons = content.queryAllByRole("spinbutton"); const rankInput = spinButtons.filter(function(element) { return element.id==="proj-rank"})[0]; @@ -356,14 +374,18 @@ it("save project with added resources", async () => { fireEvent.change(newResourceInput, { target: { value: 30 } }); expect(newResourceInput.value).toBe('30'); - await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); + await new Promise((r) => setTimeout(r, 1000)); }); + console.log("Checking service calls.."); + expect(saveProjectSpy).toHaveBeenCalled(); - // After saving project, URL should be available and Success dialog should be displayed - expect(content.queryByTestId('projectId').value).toBe("http://localhost:3000/api/project/OSR"); - //expect(content.queryByText("Success")).not.toBe(null); + //After project saved successful + nameInput = content.queryByTestId('name'); + expect(nameInput.value).toBe(""); + expect(content.queryByTestId('save-btn')).toHaveAttribute("disabled"); + }); it("remove default resource and added resource", async () => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js index 79c4f2cfef1816ac54007d197da5e69770d6fbdd..41b300a7c5638d5dfd424002e77ef285922d8255 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js @@ -24,6 +24,7 @@ import UIConstants from '../../utils/ui.constants'; import ReactTooltip from "react-tooltip"; import AuthUtil from '../../utils/auth.util'; import AccessDenied from '../../layout/components/AccessDenied'; +import { appGrowl, setAppGrowl } from '../../layout/components/AppGrowl'; export class ProjectEdit extends Component { constructor(props) { @@ -298,15 +299,16 @@ export class ProjectEdit extends Component { setProjectQuotaParams(key, event) { let projectQuota = this.state.projectQuota; const previousValue = projectQuota[key] || ""; - if (event.target.value) { + const eventValue = event.target?.value || event.value; + if (eventValue) { let resource = _.find(this.state.resources, {'name': key}); let newValue = 0; if (this.resourceUnitMap[resource.quantity_value] && - event.target.value.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { - newValue = _.trim(event.target.value.replace(this.resourceUnitMap[resource.quantity_value].display,'')); + eventValue.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) { + newValue = _.trim(eventValue.replace(this.resourceUnitMap[resource.quantity_value].display,'')); } else { - newValue = _.trim(event.target.value); + newValue = _.trim(eventValue); } //If value entered is not one decimal number then don't change the value and keep previous value. projectQuota[key] = (newValue==="NaN" || !/^[0-9]*(\.[0-9]{0,1})?$/.exec(newValue))?previousValue:(newValue); @@ -401,7 +403,6 @@ export class ProjectEdit extends Component { * Function to Create, Update & Delete project_quota for the project */ async saveProjectQuota(project) { - this.setState({isDirty: false}); publish('edit-dirty', false); let dialog = {}; let quotaError = {}; @@ -475,12 +476,14 @@ export class ProjectEdit extends Component { project.quota.push(createdProjectQuota.data.url); } } + if (appGrowl === null) { + setAppGrowl(this.growl) + } if (_.keys(quotaError).length === 0) { - dialog = {header: 'Success', detail: 'Project updated successfully.'}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Project updated successfully.'}); } else { - dialog = {header: 'Error', detail: 'Project updated successfully but resource allocation not updated properly.'}; + appGrowl.show({severity: 'error', summary: 'Error', detail: 'Project updated successfully but resource allocation not updated properly.'}); } - this.setState({dialogVisible: true, dialog: dialog, project: project}); } /** @@ -704,7 +707,7 @@ export class ProjectEdit extends Component { <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> @@ -712,26 +715,11 @@ export class ProjectEdit extends Component { } {/* Dialog component to show messages and get input */} <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '30vw'}} inputId="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelEdit();}} label="Ok" /> - {/* <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> */} - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2"> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - <span style={{marginTop:"5px"}}>{this.state.dialog.detail}</span> - </div> - </div> - </Dialog> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Project'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Edit Project'} message={'Do you want to discard your changes? Your changes to the project will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]}> </CustomDialog> </div> </>: <AccessDenied/>} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.test.js index 0ddb70d4f42ca53674cf7ebe1ec729b947cde75b..1f1aa0704a11470e8325a417cfcd67c10e92fb53 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.test.js @@ -311,5 +311,4 @@ it("save Project after adding, modifying and deleting resources", async () => { expect(savePQSpy).toHaveBeenCalledTimes(1); expect(updatePQSpy).toHaveBeenCalledTimes(1); expect(deletePQSpy).toHaveBeenCalledTimes(1); - expect(content.queryByText("Success")).not.toBe(null); }); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js index 5edad2e81c9c0c77829632ba939564dccedca9fe..1a26df0e13ff596fd58897a2d2d068844f081137 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/list.js @@ -421,7 +421,7 @@ export class ProjectList extends Component { const footer = ( <div > <Button label="Save" className="p-button-primary p-mr-2" icon="pi pi-check" disabled={!this.state.changedStatus} onClick={this.confirmStatusChange } data-testid="save-btn" /> - <Button label="Cancel" className="p-button-danger mr-0" icon="pi pi-times" onClick={() => this.setState({showStatusUpdateDialog: false})} /> + <Button label="Cancel" className="act-btn-cancel mr-0" icon="pi pi-times" onClick={() => this.setState({showStatusUpdateDialog: false})} /> </div> ); return ( diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js index 96ac7564b127f763f1472347740b50c6e7536631..8cee54e094d4b16cd9fec546f08099528dc8534c 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/view.js @@ -185,7 +185,7 @@ export class ProjectView extends Component { this.submitStatusChange(); } }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + { id: 'no', title: 'No',className:'act-btn-cancel' , callback: this.closeDialog }]; dialog.onSubmit = () => {} dialog.width = '25vw'; dialog.showIcon = false; @@ -230,7 +230,7 @@ export class ProjectView extends Component { dialog.header = `Project Status Update Info`; dialog.content = this.getSUResponseDialogContent; dialog.actions = [{ - id: 'yes', title: 'Close', callback: () => { + id: 'yes', title: 'Close',className:'act-btn-cancel', callback: () => { this.closeDialog(); this.setState({showStatusUpdateDialog: false}); } @@ -257,7 +257,7 @@ export class ProjectView extends Component { const footer = ( <div> <Button label="Save" className="p-button-primary p-mr-2" icon="pi pi-check" disabled={!this.state.changedStatus} onClick={this.confirmStatusChange } data-testid="save-btn" /> - <Button label="Cancel" className="p-button-danger mr-0" icon="pi pi-times" onClick={() => this.setState({showStatusUpdateDialog: false, changedStatus: {}})} /> + <Button label="Cancel" className="act-btn-cancel mr-0" icon="pi pi-times" onClick={() => this.setState({showStatusUpdateDialog: false, changedStatus: {}})} /> </div> ); if (this.state.redirect) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.create.js index f5cd88f8ea17c8dcc813284932b9bf6a11dedc36..da884d62e34680ea0728348ea4d66e05b2790a4d 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.create.js @@ -21,6 +21,8 @@ import ReservationService from '../../services/reservation.service'; import Jeditor from '../../components/JSONEditor/JEditor'; import UtilService from '../../services/util.service'; import UnitConverter from '../../utils/unit.converter'; +import { appGrowl } from '../../layout/components/AppGrowl'; +import { Checkbox } from 'primereact/checkbox'; import "flatpickr/dist/flatpickr.css"; @@ -54,7 +56,9 @@ export class ReservationCreate extends Component { errors: {}, // Validation Errors validFields: {}, // For Validation validForm: false, // To enable Save Button - validEditor: false + validEditor: false, + createAnother: false, + multiCreateCount: 1 }; this.projects = []; // All projects to load project dropdown this.reservationTemplates = []; @@ -326,8 +330,15 @@ export class ReservationCreate extends Component { reservation['specifications_doc'] = this.paramsOutput; reservation = await ReservationService.saveReservation(reservation); if (reservation && reservation.id) { - const dialog = { header: 'Success', detail: 'Reservation is created successfully. Do you want to create another Reservation?' }; - this.setState({ dialogVisible: true, dialog: dialog, paramsOutput: {}, showDialog: false, isDirty: false }) + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Reservation is created successfully.'}); + //const dialog = { header: 'Success', detail: 'Reservation is created successfully. Do you want to create another Reservation?' }; + if (this.state.createAnother) { + this.setState({paramsOutput: {}, showDialog: false, isDirty: false }); + this.reset(); + } else { + this.setState({paramsOutput: {}, showDialog: false, isDirty: false, redirect: '/reservation/list' }); + } + publish('edit-dirty', false); }/* else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Reservation', showDialog: false, isDirty: false}); @@ -345,6 +356,7 @@ export class ReservationCreate extends Component { stop_time: '', project: '', } + let multiCreateCount = this.state.multiCreateCount; this.setState({ dialogVisible: false, dialog: { header: '', detail: '' }, @@ -360,7 +372,8 @@ export class ReservationCreate extends Component { touched: false, stationGroup: [], showDialog: false, - isDirty: false + isDirty: false, + multiCreateCount: ++multiCreateCount }); this.initReservation(); } @@ -370,7 +383,11 @@ export class ReservationCreate extends Component { */ cancelCreate() { publish('edit-dirty', false); - this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/su/timelineview`); + if (this.state.multiCreateCount > 1) { + this.props.history.go(this.state.multiCreateCount*-1); + } else { + this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/su/timelineview`); + } this.setState({ showDialog: false }); } @@ -640,15 +657,22 @@ export class ReservationCreate extends Component { </div> </div> </div> - - - <div className="p-grid p-justify-start"> + <div className="p-col-1.5" style={{marginTop: '1em', marginLeft: '0.75em'}}> + <Checkbox inputId="trigger" role="trigger" + tooltip="Select checkbox to create another reservation after saving this reservation" + tooltipOptions={this.tooltipOptions} + checked={this.state.createAnother} + onChange={e => this.setState({'createAnother': e.target.checked})} + style={{marginRight: '0.25em',}}></Checkbox> + <label style={{display: 'inline'}}> Create another? </label> + </div> + <div className="p-grid p-justify-start act-btn-grp"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveReservation} disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </div> @@ -657,26 +681,11 @@ export class ReservationCreate extends Component { {/* Dialog component to show messages and get input */} <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{ width: '25vw' }} inputId="confirm_dialog" - modal={true} onHide={() => { this.setState({ dialogVisible: false }) }} - footer={<div> - <Button key="back" onClick={() => { this.setState({ dialogVisible: false, redirect: `/reservation/list` }); }} label="No" /> - <Button key="submit" type="primary" onClick={this.reset} label="Yes" /> - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2" style={{ margin: 'auto' }}> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - {this.state.dialog.detail} - </div> - </div> - </Dialog> - <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Add Reservation'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Add Reservation'} message={'Do you want to discard your changes? A new Reservation will not be created.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> </div> </React.Fragment> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.edit.js index 997cbf4328419a581de81c1d723844a77a8137f2..0d7d4fd1cae983eb96d2e4244dd84abe93331802 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.edit.js @@ -620,13 +620,13 @@ export class ReservationEdit extends Component { </div> </div> - <div className="p-grid p-justify-start"> + <div className="p-grid p-justify-start act-btn-grp"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveReservation} disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </div> @@ -635,8 +635,10 @@ export class ReservationEdit extends Component { } <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Reservation'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Edit Reservation'} message={'Do you want to discard your changes? Your changes to the Reservation will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> </React.Fragment> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js index f63f26731d2d5393d4bad9040dd561759a4a9555..150788a7c9be786c8a7032e1a62b351c05f86f45 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.list.js @@ -416,8 +416,8 @@ export class ReservationList extends Component{ dialog.header= "Confirm to Delete Reservation(s)"; dialog.detail = "Do you want to delete the selected Reservation(s)?"; dialog.content = this.getReservationDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.deleteReservations}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Delete', callback: this.deleteReservations, className:'act-btn-dispose'}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.deleteReservations; dialog.width = '55vw'; dialog.showIcon = false; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.view.js index 2857ba915e294fd9cf34f4171e6be05396fbcb27..fb7edf683c8b29feb58a5152bd2afb471f25d432 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.view.js @@ -274,8 +274,9 @@ export class ReservationView extends Component { } <CustomDialog type={this.dialogType} visible={this.state.confirmDialogVisible} width={this.dialogWidth} header={this.dialogHeader} message={this.dialogMsg} - content={this.dialogContent} onClose={this.onClose} onCancel={this.onCancel} onSubmit={this.callBackFunction} - showIcon={this.showIcon} actions={this.actions}> + content={this.dialogContent} onClose={this.onClose} + actions={ [ {id:"yes", title: 'Delete', callback: this.callBackFunction, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.onCancel} ]} > </CustomDialog> {!this.state.isLoading && <Websocket ref={websocket => this.websocket = websocket} url={process.env.REACT_APP_WEBSOCKET_URL} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js index 801463685b7cdecb72bf518c3ee4e0e2a58ee48e..9bdaa3cdd3abbaaad3a88216b0093bd4e8e88769 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.Constraints.js @@ -38,8 +38,10 @@ export default (props) => { propertyValue.format= 'checkbox'; propertyValue.skipFormat = true; } - if (propertyKey === 'require_night' || propertyKey === 'require_day' || propertyKey === 'avoid_twilight' ){ + if ((propertyKey === 'require_night' || propertyKey === 'require_day' || propertyKey === 'avoid_twilight') && typeof(propertyValue)!=="boolean" ){ propertyValue.propertyOrder=6; + propertyValue.format= 'checkbox'; + propertyValue.skipFormat = true; } if(propertyKey === 'sky'){ propertyValue.propertyOrder=7; @@ -71,10 +73,6 @@ export default (props) => { propertyValue.propertyOrder=12; propertyValue.validationType= 'distanceOnSky'; } - if(propertyKey === 'avoid_twilight' || propertyKey === 'require_day' || propertyKey === 'require_night'){ - propertyValue.format= 'checkbox'; - propertyValue.skipFormat = true; - } } }; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js index 47d8c6e1de239789a17a616e25599ce05f86648c..e8ff4c2c3c673ab6b8084c1df32d1d95e326e872 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/Scheduling.task.relation.js @@ -204,14 +204,14 @@ export default (props) => { dialog.header = `Confirm Dataproduct(s) to Ingest`; dialog.content = getSUDialogContent; dialog.actions = [{ - id: 'yes', title: 'Yes', callback: () => { + id: 'yes', title: 'Change Ingest', callback: () => { closeDialog(); props.submitTaskRelationToIngest({ 'ingestGroup': ingestRelation, 'taskRelationDraft': taskRelationDraft }); setSaveDisabled(true); setOriginIngestRelation(_.cloneDeep(ingestRelation)); } }, - { id: 'no', title: 'No', callback: closeDialog }]; + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: closeDialog }]; dialog.onSubmit = () => { closeDialog(); props.submitTaskRelationToIngest({ 'ingestGroup': ingestRelation, 'taskRelationDraft': taskRelationDraft }); @@ -242,7 +242,7 @@ export default (props) => { const footer = ( <div > <Button label="Save" className="p-button-primary p-mr-2" icon="pi pi-check" disabled={isSaveDisabled} onClick={submitToIngest} data-testid="save-btn" /> - <Button label="Cancel" className="p-button-danger mr-0" icon="pi pi-times" onClick={props.toggle} /> + <Button label="Cancel" className="act-btn-cancel mr-0" icon="pi pi-times" onClick={props.toggle} /> </div> ); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js index 5718a2c603b3b5e1a56442e853a2bc1ccebc8452..4215f44e9d662d7c1dc7df7f5590bb794e62d7b0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/SchedulingUnitList.js @@ -948,8 +948,8 @@ class SchedulingUnitList extends Component{ dialog.detail = "Do you want to delete the selected Scheduling Unit Draft/Blueprint?"; } dialog.content = this.getSchedulingDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.deleteSchedulingUnit, className:(this.props.project)?"dialog-btn": ""}, - {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'yes', title: hasInvalidSUD? 'Delete others':'Delete', callback: this.deleteSchedulingUnit, className:(this.props.project)?"dialog-btn": "", style:{"background": "#e91224","border": "1px solid #e91224"}}, + {id: 'no', title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.deleteSchedulingUnit; dialog.width = '55vw'; dialog.showIcon = false; @@ -1034,7 +1034,7 @@ class SchedulingUnitList extends Component{ let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Delete Scheduling Unit(s) Status"; - dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "" }]; + dialog.actions = [{ id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel" }]; dialog.detail = "" dialog.content = this.getSUDeleteStatusContent; dialog.submit = this.closeDialog; @@ -1139,12 +1139,13 @@ class SchedulingUnitList extends Component{ dialog.content = this.getSUCopyStatusContent; dialog.actions = [{ id: 'yes', - title: 'Yes', + title: 'Copy', callback: this.copySchedulingUnit }, { id: 'no', - title: 'No', + title: 'Cancel', + className:'act-btn-cancel', callback: this.closeDialog } ]; @@ -1211,7 +1212,7 @@ class SchedulingUnitList extends Component{ id: 'no', title: 'Close', callback: this.closeDialog, - className: (this.props.project) ? "dialog-btn" : "" + className: (this.props.project) ? "dialog-btn" : "act-btn-cancel" }]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; @@ -1242,8 +1243,8 @@ class SchedulingUnitList extends Component{ let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Scheduling Unit(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSchedulingUnit, className:(this.props.project)?"dialog-btn": "" }, - { id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "" }]; + dialog.actions = [{ id: 'yes', title: 'Cancel SU', callback: this.cancelSchedulingUnit, className:(this.props.project)?"dialog-btn": "", style:{"background": "#e91224","border": "1px solid #e91224"} }, + { id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel" }]; dialog.detail = "Cancelling the scheduling unit means it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled scheduling unit(s) will be ignored. Do you want to proceed?"; dialog.content = this.getSUCancelConfirmContent; dialog.submit = this.cancelSchedulingUnit; @@ -1277,7 +1278,7 @@ class SchedulingUnitList extends Component{ let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Cancel Scheduling Unit(s) Status"; - dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "" }]; + dialog.actions = [{ id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel" }]; dialog.detail = "" dialog.content = this.getSUCancelStatusContent; dialog.submit = this.closeDialog; @@ -1490,8 +1491,8 @@ class SchedulingUnitList extends Component{ dialog.header= "Confirm to Create Clean-up Task(s)"; dialog.detail = "Do you want to create clean-up task(s) for selected Scheduling Unit Blueprint(s)?"; dialog.content = this.getCleanUpDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.createSUCleanupTask, className:(this.props.project)?"dialog-btn": ""}, - {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'yes', title: 'Create Clean-up Task', callback: this.createSUCleanupTask, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.createSUCleanupTask; dialog.width = '55vw'; dialog.showIcon = false; @@ -1551,7 +1552,7 @@ class SchedulingUnitList extends Component{ dialog.header= "Create Clean-up Task(s) Status"; dialog.detail = ""; dialog.content = this.getStatusContent; - dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -1648,8 +1649,8 @@ class SchedulingUnitList extends Component{ dialog.header= "Confirm Pin/Unpin data after ingest"; dialog.detail = "Do you want to Pin/Unpin data after ingest for selected Scheduling Units?"; dialog.content = this.getAutoDeletionDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.setAutoDeletion, className:(this.props.project)?"dialog-btn": ""}, - {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'yes', title: 'Pin/Unpin data', callback: this.setAutoDeletion, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.setAutoDeletion; dialog.width = '55vw'; dialog.showIcon = false; @@ -1675,7 +1676,7 @@ class SchedulingUnitList extends Component{ dialog.header= "Pin/Unpin data Status"; dialog.detail = ""; dialog.content = this.getStatusContent; - dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -1728,8 +1729,8 @@ class SchedulingUnitList extends Component{ dialog.header= "Confirm to re-run all failed tasks"; dialog.detail = "Do you want to re-run all failed tasks of the selected Scheduling Unit Blueprint(s)?"; dialog.content = this.getCopyFailedTasksDlgContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.copyFailedTasks, className:(this.props.project)?"dialog-btn": ""}, - {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'yes', title: 'Re-run', callback: this.copyFailedTasks, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.copyFailedTasks; dialog.width = '55vw'; dialog.showIcon = false; @@ -1804,7 +1805,7 @@ class SchedulingUnitList extends Component{ dialog.header= "Re-run all failed tasks - Status"; dialog.detail = ""; dialog.content = this.getStatusContent; - dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -1824,8 +1825,8 @@ class SchedulingUnitList extends Component{ dialog.header= "Confirm to update blueprints from draft"; dialog.detail = "Do you want to update selected scheduling unit blueprint(s) according to the changed and added task drafts?"; dialog.content = this.getUpdateSUBDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.updateSUB, className:(this.props.project)?"dialog-btn": ""}, - {id: 'no', title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'yes', title: 'Update Blueprint(s)', callback: this.updateSUB, className:(this.props.project)?"dialog-btn": ""}, + {id: 'no', title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.updateSUB; dialog.width = '55vw'; dialog.showIcon = false; @@ -1900,7 +1901,7 @@ class SchedulingUnitList extends Component{ dialog.header= "Update blueprints from draft - Status"; dialog.detail = ""; dialog.content = this.getStatusContent; - dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; @@ -1922,8 +1923,8 @@ class SchedulingUnitList extends Component{ dialog.header= "Confirm to create draft copy for failed tasks"; dialog.detail = "Do you want to create draft copy for failed tasks of the selected Scheduling Unit Blueprint(s)?"; dialog.content = this.getCopySpecDlgContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.copySpecAndFailedTasks}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Create draft tasks', callback: this.copySpecAndFailedTasks}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.copySpecAndFailedTasks; dialog.width = '55vw'; dialog.showIcon = false; @@ -1998,7 +1999,7 @@ class SchedulingUnitList extends Component{ dialog.header= "Create copy for failed tasks - Status"; dialog.detail = ""; dialog.content = this.getStatusContent; - dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""}]; + dialog.actions = [{id: 'no', title: 'Close', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"}]; dialog.onSubmit = this.closeDialog; dialog.width = '55vw'; dialog.showIcon = false; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js index c09d74bc404a2781b3139eee89c0314a9afca96c..d235f3a4698bf760775b63a26329caec2df65519 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/ViewSchedulingUnit.js @@ -39,7 +39,7 @@ class ViewSchedulingUnit extends Component { defaultSortColumn = []; ignoreSorting = ['status logs']; SU_NOT_STARTED_STATUSES = ['defined', 'schedulable', 'scheduled', 'unschedulable']; - SU_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting']; + SU_ACTIVE_STATUSES = ['started', 'observing', 'observed', 'processing', 'processed', 'ingesting', 'ingested']; SU_END_STATUSES = ['finished', 'error', 'cancelled']; TASK_END_STATUSES = ['finished', 'error', 'cancelled']; SU_BLUEPRINT_EXPAND= 'draft.scheduling_constraints_template,draft,draft.scheduling_set,task_blueprints.specifications_template,task_blueprints,task_blueprints.subtasks,draft.observation_strategy_template,task_blueprints.draft' @@ -221,7 +221,7 @@ class ViewSchedulingUnit extends Component { stationGroup: [], stationGroups: {}, stationsCollapsed: false, - dialog: { header: 'Confirm', detail: 'Do you want to create a Scheduling Unit Blueprint?' }, + dialog: { header: 'Confirm', detail: 'Do you want to create a Scheduling Unit Blueprint? The Scheduling Unit may be scheduled immediately.' }, dialogVisible: false, actions: [], dataformat: ['MeasurementSet'], @@ -856,14 +856,14 @@ class ViewSchedulingUnit extends Component { dialog.header = "Confirm"; dialog.onSubmit = this.createBlueprintTree; dialog.content = null; - dialog.width = null; + dialog.width = "40vw"; if (this.state.scheduleunit.scheduling_unit_blueprints_ids.length > 0) { dialog.detail = "Blueprint(s) already exist for this Scheduling Unit. Do you want to create another one?"; } else { - dialog.detail = "Do you want to create a Scheduling Unit Blueprint?"; + dialog.detail = "Do you want to create a Scheduling Unit Blueprint? The Scheduling Unit may be scheduled immediately."; } - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.createBlueprintTree }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Create', callback: this.createBlueprintTree }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; this.setState({ dialogVisible: true, dialog: dialog }); } } @@ -893,12 +893,13 @@ class ViewSchedulingUnit extends Component { dialog.content = this.getSUCopyStatusContent; dialog.actions = [{ id: 'yes', - title: 'Yes', + title: 'Copy', callback: this.copySchedulingUnit }, { id: 'no', - title: 'No', + title: 'Cancel', + className:'act-btn-cancel', callback: this.closeDialog } ]; @@ -943,6 +944,7 @@ class ViewSchedulingUnit extends Component { { id: 'no', title: 'No', + className:'act-btn-cancel', callback: this.closeDialog } ]; @@ -950,6 +952,7 @@ class ViewSchedulingUnit extends Component { dialog.actions = [{ id: 'no', title: 'No', + className:'act-btn-cancel', callback: this.closeDialog }]; } @@ -1024,8 +1027,8 @@ class ViewSchedulingUnit extends Component { dialog.header = "Confirm to Delete Task(s)"; dialog.detail = "Do you want to delete the selected Task(s)?"; dialog.content = this.getTaskDeleteDialogContent; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Delete Tasks', callback: this.deleteTasks, style:{"background": "#e91224","border": "1px solid #e91224"} }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.onSubmit = this.deleteTasks; dialog.width = '55vw'; dialog.showIcon = false; @@ -1039,8 +1042,8 @@ class ViewSchedulingUnit extends Component { dialog.header = "Confirm to Delete Scheduling Unit"; dialog.detail = "Do you want to delete this Scheduling Unit?"; dialog.content = this.getSUDialogContent; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteSchedulingUnit }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Delete', callback: this.deleteSchedulingUnit, style:{"background": "#e91224","border": "1px solid #e91224"} }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.onSubmit = this.deleteSchedulingUnit; dialog.width = '55vw'; dialog.showIcon = false; @@ -1055,8 +1058,8 @@ class ViewSchedulingUnit extends Component { dialog.detail = ''; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Scheduling Unit"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSchedulingUnit }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Cancel SU', callback: this.cancelSchedulingUnit, style:{"background": "#e91224","border": "1px solid #e91224"} }, + { id: 'no', title: 'No', className:'act-btn-cancel', callback: this.closeDialog }]; if (this.SU_NOT_STARTED_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) { dialog.detail = "Cancelling this scheduling unit means it will no longer be executed. This action cannot be undone. Do you want to proceed?"; } else if (this.SU_ACTIVE_STATUSES.indexOf(this.state.scheduleunit.status) >= 0) { @@ -1547,8 +1550,8 @@ class ViewSchedulingUnit extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Task(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Cancel Tasks', callback: this.cancelTasks, style:{"background": "#e91224","border": "1px solid #e91224"} }, + { id: 'no', title: 'No', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Cancelling the task means it will no longer be executed / will be aborted. This action cannot be undone. Do you want to proceed?"; dialog.content = this.getTaskCancelConfirmContent; dialog.submit = this.cancelTasks; @@ -1581,8 +1584,8 @@ class ViewSchedulingUnit extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to mark task(s) as obsolete"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markObsoleteTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Mark as obsolete', className:'act-btn-dispose', callback: this.markObsoleteTasks }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Warning: Obsolete task(s) will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task(s) obsolete?"; dialog.content = this.getObsTaskConfirmDlgContent; dialog.submit = this.markObsoleteTasks; @@ -1673,8 +1676,8 @@ class ViewSchedulingUnit extends Component { dialog.header= "Confirm to Create Clean-up Task"; dialog.detail = "Do you want to create Clean-up task for this Scheduling Unit Blueprint?"; dialog.content = this.getCleanUpDialogContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.createSUCleanupTask}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Create Clean-up Task', callback: this.createSUCleanupTask}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.createSUCleanupTask; dialog.width = '55vw'; dialog.showIcon = false; @@ -1770,8 +1773,9 @@ class ViewSchedulingUnit extends Component { dialog.header= "Confirm Pin/Unpin data after ingest"; dialog.detail = this.state.scheduleunit.output_pinned ? 'Do you want to Unpin data after ingest?' : 'Do you want to Pin data after ingest?'; dialog.content = ''; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.setAutoDeletion}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: this.state.scheduleunit.output_pinned?'Unpin data': 'Pin data', + className: this.state.scheduleunit.output_pinned?'act-btn-dispose':'', callback: this.setAutoDeletion}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.setAutoDeletion; dialog.width = '35vw'; dialog.showIcon = false; @@ -1808,8 +1812,8 @@ class ViewSchedulingUnit extends Component { dialog.header= "Confirm to re-run all failed tasks"; dialog.detail = "Do you want to re-run all failed tasks of this Scheduling Unit Blueprint?"; dialog.content = this.getCopyFailedTasksDlgContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.copyFailedTasks}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Re-run', callback: this.copyFailedTasks}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.copyFailedTasks; dialog.width = '55vw'; dialog.showIcon = false; @@ -1874,8 +1878,8 @@ class ViewSchedulingUnit extends Component { dialog.header= "Confirm to update blueprints from draft"; dialog.detail = "Do you want to update blueprints of the scheduling unit according to the changed and added task drafts?"; dialog.content = this.getCopyFailedTasksDlgContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.updateSUB}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Update Blueprint', callback: this.updateSUB}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.updateSUB; dialog.width = '55vw'; dialog.showIcon = false; @@ -1917,8 +1921,8 @@ class ViewSchedulingUnit extends Component { dialog.header= "Confirm to create draft copy for failed tasks"; dialog.detail = "Do you want to create draft copy for failed tasks of this Scheduling Unit Blueprint?"; dialog.content = this.getCopyFailedTasksDlgContent; - dialog.actions = [{id: 'yes', title: 'Yes', callback: this.copySpecAndFailedTasks}, - {id: 'no', title: 'No', callback: this.closeDialog}]; + dialog.actions = [{id: 'yes', title: 'Create draft tasks', callback: this.copySpecAndFailedTasks}, + {id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog}]; dialog.onSubmit = this.copySpecAndFailedTasks; dialog.width = '55vw'; dialog.showIcon = false; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js index 65dfd0bd3db86aa4131cb3ef655e80c598abeb56..9abefd99bf654d9fb6226f3b9f4c7b03b2a67d2f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.js @@ -30,6 +30,7 @@ import UtilService from '../../services/util.service'; import ReactTooltip from "react-tooltip"; import AuthUtil from '../../utils/auth.util'; import { MultiSelect } from 'primereact/multiselect'; +import { appGrowl, setAppGrowl } from '../../layout/components/AppGrowl'; /** * Component to create a new SchedulingUnit from Observation strategy template @@ -76,6 +77,8 @@ export class SchedulingUnitCreate extends Component { validFields: {}, // For Form Validation newSet: null, observationIdSet: [], + createAnother: false, + multiCreateCount: 1 }; this.defaultConstraints = {} this.taskFilters = []; // To get Short_Description details @@ -202,7 +205,7 @@ export class SchedulingUnitCreate extends Component { * @param {number} strategyId */ async changeStrategy (strategyId) { - const observStrategy = _.find(this.observStrategies, {'id': strategyId}); + const observStrategy = _.cloneDeep(_.find(this.observStrategies, {'id': strategyId})); if (ParserUtility.addStationParameters(observStrategy)) { this.setState({observStrategies: this.observStrategies}); } @@ -366,10 +369,12 @@ export class SchedulingUnitCreate extends Component { } this.constraintValidEditor = err.length === 0; if ( !this.state.isDirty && this.state.constraintParamsOutput && !_.isEqual(this.state.constraintParamsOutput, jsonOutput) ) { - if(!this.state.isDirty) { - publish('edit-dirty', true); + if (!_.isEmpty(this.state.observStrategy)) { + if(!this.state.isDirty) { + publish('edit-dirty', true); + } + this.setState({ constraintParamsOutput: jsonOutput, constraintValidEditor: err.length === 0, validForm: this.validateForm(), isDirty: true}); } - this.setState({ constraintParamsOutput: jsonOutput, constraintValidEditor: err.length === 0, validForm: this.validateForm(), isDirty: true}); } else { this.setState({ constraintParamsOutput: jsonOutput, constraintValidEditor: err.length === 0, validForm: this.validateForm()}); } @@ -673,9 +678,16 @@ export class SchedulingUnitCreate extends Component { schedulingUnit.rank = schedulingUnit.rank === undefined || schedulingUnit.rank === '' ? 0: schedulingUnit.rank.toFixed(4); schedulingUnit = await ScheduleService.saveSchedulingUnit(schedulingUnit, newSpecificationDoc); if (!schedulingUnit.error) { - // this.growl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Unit and tasks created successfully!'}); - const dialog = {header: 'Success', detail: 'Scheduling Unit and Tasks are created successfully. Do you want to create another Scheduling Unit?'}; - this.setState({schedulingUnit: schedulingUnit.data, dialogVisible: true, dialog: dialog, isDirty: false}); + if (appGrowl === null) { + setAppGrowl(this.growl); + } + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Unit and Tasks are created successfully'}); + if (this.state.createAnother) { + this.setState({schedulingUnit: schedulingUnit.data, isDirty: false}); + this.reset(); + } else { + this.setState({schedulingUnit: schedulingUnit.data, isDirty: false, redirect: `/schedulingunit/view/draft/${schedulingUnit.data.id}`}); + } publish('edit-dirty', false); } else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: schedulingUnit.message || 'Unable to save Scheduling Unit/Tasks'}); @@ -702,7 +714,15 @@ export class SchedulingUnitCreate extends Component { */ cancelCreate() { publish('edit-dirty', false); - this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/schedulingunit`); + if (this.state.multiCreateCount > 1) { + if (this.state.isDirty) { + this.props.history.push(`/schedulingunit`); + } else { + this.props.history.go(this.state.multiCreateCount*-1); + } + } else { + this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/schedulingunit`); + } this.setState({showDialog: false}); } @@ -721,9 +741,8 @@ export class SchedulingUnitCreate extends Component { const schedulingSets = this.state.schedulingSets; this.nameInput.focus(); const selectedProject = this.props.match?.params?.project; - + let multiCreateCount = this.state.multiCreateCount; this.setState({ - dialogVisible: false, isDirty: false, dialog: { header: '', detail: ''}, errors: [], @@ -738,6 +757,7 @@ export class SchedulingUnitCreate extends Component { }, projectDisabled: (this.props.match?.params?.project? true:false), observStrategy: {}, + paramsSchema: null, paramsOutput: null, validEditor: false, validFields: {}, @@ -747,7 +767,8 @@ export class SchedulingUnitCreate extends Component { selectedStationsOfTask: {}, customStationsOfTask: {}, touched:false, - stationGroups: {} + stationGroups: {}, + multiCreateCount: ++multiCreateCount }, () => { this.constraintStrategy(this.constraintTemplates[0]); }); @@ -1061,7 +1082,7 @@ export class SchedulingUnitCreate extends Component { callback={this.setConstraintsEditorOutput} parentFunction={this.setConstraintEditorFun}/> </div> </fieldset> - <div className="p-col-12" style={{marginLeft: '0.2em', marginTop: '-10px', paddingTop:'0px'}}> + <div className="p-col-12" style={{marginLeft: '0.4em', marginTop: '-10px', paddingTop:'0px'}}> <label className={this.state.validConstraints ? "info" : "error"}> {this.state.validConstraintMessage ? this.state.validConstraintMessage : ""} </label> @@ -1077,20 +1098,30 @@ export class SchedulingUnitCreate extends Component { </div> </div> </fieldset> - <div className="p-col-12" style={{marginLeft: '0.2em', marginTop: '-10px', paddingTop:'0px'}}> + <div className="p-col-12" style={{marginLeft: '0.4em', marginTop: '-10px', paddingTop:'0px'}}> <label className={this.state.validSpecification ? "info" : "error"}> {this.state.validSpecificationMessage ? this.state.validSpecificationMessage : ""} </label> </div> </div> } - <div className="p-grid p-justify-start"> - <div className="p-col-1"> + <div className="p-col-1.5" style={{marginTop: '1em', marginLeft: '0.9em'}}> + <Checkbox inputId="trigger" role="trigger" data-testid="createAnother" + tooltip="Select checkbox to create another Scheduling Unit after saving this Scheduling Unit" + tooltipOptions={this.tooltipOptions} + checked={this.state.createAnother} + onChange={e => this.setState({'createAnother': e.target.checked})} + style={{marginRight: '0.25em',}}></Checkbox> + <label style={{display: 'inline'}}> Create another? </label> + + </div> + <div className="p-grid p-justify-start act-btn-grp"> + <div className="p-col-1" style={{marginLeft: '0.15em'}}> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSchedulingUnit} disabled={!this.state.constraintValidEditor || !this.state.validEditor || !this.state.validForm || !this.state.validConstraints || !this.state.validSpecification} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </div> @@ -1099,30 +1130,15 @@ export class SchedulingUnitCreate extends Component { } {/* Dialog component to show messages and get input */} - <div className="p-grid" data-testid="confirm_dialog"> - <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '25vw'}} inputId="confirm_dialog" - modal={true} onHide={() => {this.setState({dialogVisible: false})}} - footer={<div> - <Button key="back" onClick={() => {this.setState({dialogVisible: false, redirect: `/schedulingunit/view/draft/${this.state.schedulingUnit.id}`});}} label="No" /> - <Button key="submit" type="primary" onClick={this.reset} label="Yes" data-testid="yes-btn"/> - </div> - } > - <div className="p-grid"> - <div className="col-lg-2 col-md-2 col-sm-2" style={{margin: 'auto'}}> - <i className="pi pi-check-circle pi-large pi-success"></i> - </div> - <div className="col-lg-10 col-md-10 col-sm-10"> - {this.state.dialog.detail} - </div> - </div> - </Dialog> - + <div className="p-grid" data-testid="confirm_dialog"> {this.state.showAddSet && <SchedulingSet callbackFunction={this.setSUSet} project={this.state.selectedProject[0]} onCancel={this.refreshSchedulingSet} /> } <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Add Scheduling Unit'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Add Scheduling Unit'} message={'Do you want to discard your changes? A new Scheduling Unit will not be created'} + content={''} onClose={this.close} actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} + > </CustomDialog> </div> </React.Fragment> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js index 4d76bafcb9642ba93c180453d015900c874f7100..3dc3ebbe78bf74c850e17a4c2b1fd0bc3f32d43b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/create.test.js @@ -227,7 +227,10 @@ it(" 3. Test IM LBA Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); - // const projInput = content.getAllByRole("listbox")[0].children[0] ; + + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + let strategyCount = 0; const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().LBA_Strategy_Group); for (const strategy of observationStrategTemplate) { @@ -241,7 +244,7 @@ it(" 3. Test IM LBA Strategy Group(s) ", async() => { strategyCount++; const optionLabel = `${strategy.name} (${strategy.purpose}, ${strategy.state}, v${strategy.version})`; console.log("************************ (",strategyCount,") Testing the Strategy - ", optionLabel,' ***********************') - + // Set values for all mandatory input fireEvent.change(nameInput, { target: { value: strategy.name } }); fireEvent.change(descInput, { target: { value: strategy.name } }); @@ -255,6 +258,7 @@ it(" 3. Test IM LBA Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -267,8 +271,8 @@ it(" 3. Test IM LBA Strategy Group(s) ", async() => { await act( async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); - }); - + }); + // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); const observStrategyInput = screen.getAllByText(optionLabel); @@ -369,12 +373,6 @@ it(" 3. Test IM LBA Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -409,6 +407,9 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().Simple_Strategy_Group); let strategyCount = 0; for (const strategy of observationStrategTemplate) { @@ -448,7 +449,7 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); }); - + // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); const observStrategyInput = screen.getAllByText(optionLabel); @@ -517,7 +518,6 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { } } } - } else { const id = `specification[${parameter.name}]`; expect(screen.getByLabelText(id).value).toBe(dataMapper[parameter.name]); @@ -529,7 +529,7 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); + //expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -537,12 +537,6 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -558,7 +552,7 @@ it(" 4. Test Simple Observation Strategy Group(s)", async() => { } // tmp => strategy name check } //Strategy Loop } -}); +}); it(" 5. Test IM HBA Strategy Group(s) ", async() => { const observationStrategTemplate = await Promise.resolve(SUServiceMock.getObservStrategies()); @@ -576,6 +570,9 @@ it(" 5. Test IM HBA Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().HBA_Strategy_Group); let strategyCount = 0; for (const strategy of observationStrategTemplate) { @@ -603,6 +600,7 @@ it(" 5. Test IM HBA Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -616,7 +614,6 @@ it(" 5. Test IM HBA Strategy Group(s) ", async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); }); - // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); const observStrategyInput = screen.getAllByText(optionLabel); @@ -697,20 +694,14 @@ it(" 5. Test IM HBA Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); await new Promise((r) => setTimeout(r, 1000)); }); + console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -721,14 +712,12 @@ it(" 5. Test IM HBA Strategy Group(s) ", async() => { fireEvent.click(screen.getAllByText(strategy.state)[0]); await new Promise((r) => setTimeout(r, 1000)); }); - console.log("************************ (",strategyCount,") Tested the Strategy - ", optionLabel,' ***********************') } } // tmp => strategy name check } //Strategy Loop } -}); - +}); it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { const observationStrategTemplate = await Promise.resolve(SUServiceMock.getObservStrategies()); @@ -746,6 +735,9 @@ it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().Pulsar_Strategy_Group); let strategyCount = 0; for (const strategy of observationStrategTemplate) { @@ -773,6 +765,7 @@ it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -867,7 +860,6 @@ it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -875,12 +867,6 @@ it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -891,7 +877,6 @@ it(" 6. Test Pulsar Timing Strategy Group(s) ", async() => { fireEvent.click(screen.getAllByText(strategy.state)[0]); await new Promise((r) => setTimeout(r, 1000)); }); - console.log("************************ (",strategyCount,") Tested the Strategy - ", optionLabel,' ***********************') } } // tmp => strategy name check @@ -915,6 +900,9 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().BF_Strategy_Group); let strategyCount = 0; for (const strategy of observationStrategTemplate) { @@ -941,6 +929,7 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -953,7 +942,7 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { await act( async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); - }); + }); // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); @@ -1023,7 +1012,6 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { } } } - } else { const id = `specification[${parameter.name}]`; expect(screen.getByLabelText(id).value).toBe(dataMapper[parameter.name]); @@ -1039,7 +1027,6 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -1047,12 +1034,6 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -1070,6 +1051,7 @@ it(" 7. Test Beamforming Strategy Group(s) ", async() => { } }); + it(" 8. Test COBALT Strategy Group(s) ", async() => { const observationStrategTemplate = await Promise.resolve(SUServiceMock.getObservStrategies()); if (observationStrategTemplate) { @@ -1086,6 +1068,9 @@ it(" 8. Test COBALT Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + const strategy_Templates_Group = await Promise.resolve(SUServiceMock.getObservStrategiesDataMapper().COBALT_Strategy_Group); let strategyCount = 0; for (const strategy of observationStrategTemplate) { @@ -1112,6 +1097,7 @@ it(" 8. Test COBALT Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -1124,7 +1110,7 @@ it(" 8. Test COBALT Strategy Group(s) ", async() => { await act( async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); - }); + }); // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); @@ -1210,7 +1196,6 @@ it(" 8. Test COBALT Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -1218,12 +1203,6 @@ it(" 8. Test COBALT Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -1259,6 +1238,9 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + let strategyCount = 0; for (const strategy of observationStrategTemplate) { if (strategy.name === 'IM HBA - 1 Beam' ) { @@ -1285,6 +1267,7 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -1298,7 +1281,7 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); }); - + // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); const observStrategyInput = screen.getAllByText(optionLabel); @@ -1379,7 +1362,6 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -1387,12 +1369,6 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -1402,8 +1378,7 @@ it(" 3.1 Test IM HBA Strategy Group(s) ", async() => { await new Promise((r) => setTimeout(r, 200)); fireEvent.click(screen.getAllByText(strategy.state)[0]); await new Promise((r) => setTimeout(r, 1000)); - }); - + }); console.log("************************ (",strategyCount,") Tested the Strategy - ", optionLabel,' ***********************') } } // tmp => strategy name check @@ -1426,6 +1401,9 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { expect(screen.getByLabelText("specification[time][before]").value).toBe(""); const nameInput = content.queryByTestId('name'); const descInput = content.queryByTestId('description'); + const createAnother = content.queryByTestId('createAnother'); + fireEvent.click(createAnother); + let strategyCount = 0; for (const strategy of observationStrategTemplate) { if (strategy.name === 'IM RT HBA LoTSS' ) { @@ -1452,6 +1430,7 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { await act(async () => { fireEvent.click(schedulingSetInput); }); + fireEvent.click(screen.getAllByText("Purpose")[0]); const templatePurpose = screen.getAllByText(strategy.purpose)[0]; await act( async() => { @@ -1464,8 +1443,8 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { await act( async() => { fireEvent.click(templateState); await new Promise((r) => setTimeout(r, 200)); - }); - + }); + // expect(content.queryAllByText(optionLabel).length).toBe(1); fireEvent.click(screen.getAllByText("Select Strategy")[0]); const observStrategyInput = screen.getAllByText(optionLabel); @@ -1546,7 +1525,6 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { // Check save button state and click to save console.log("Checking Save button..."); expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy(); - expect(content.queryByText('Success')).not.toBeInTheDocument(); console.log("Firing Click Event..."); await act(async () => { fireEvent.click(content.queryByTestId('save-btn')); @@ -1554,12 +1532,6 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { }); console.log("Checking service calls.."); expect(saveSUFromStrategySpy).toHaveBeenCalled(); - expect(content.queryByText('Success')).toBeInTheDocument(); - - await act(async () => { - fireEvent.click(content.queryByTestId('yes-btn')); - await new Promise((r) => setTimeout(r, 1000)); - }); // Reset the purpose and state filter selection await act( async() => { fireEvent.click(screen.getAllByText(strategy.purpose)[0]); @@ -1570,10 +1542,9 @@ it(" 3.2 Test IM HBA Strategy Group(s) ", async() => { fireEvent.click(screen.getAllByText(strategy.state)[0]); await new Promise((r) => setTimeout(r, 1000)); }); - console.log("************************ (",strategyCount,") Tested the Strategy - ", optionLabel,' ***********************') } } // tmp => strategy name check } //Strategy Loop } -}); +}); \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js index 7726296b857c88452cf7b603c632f480e174c77a..606c175560755db4eda4398332a659592df92e1d 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/edit.js @@ -29,7 +29,7 @@ import UtilService from '../../services/util.service'; import ReactTooltip from "react-tooltip"; import AuthUtil from '../../utils/auth.util'; import AccessDenied from '../../layout/components/AccessDenied'; -import UnitConverter from '../../utils/unit.converter'; +import { appGrowl } from '../../layout/components/AppGrowl'; /** * Compoenent to edit scheduling unit draft @@ -596,13 +596,13 @@ export class EditSchedulingUnit extends Component { schUnit['specifications_doc'] = observStrategy.template; schedulingUnit = await ScheduleService.updateSchedulingUnitDraft(schUnit); if (!schedulingUnit.error) { - this.growl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Unit and Tasks updated successfully!'}); + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Unit and Tasks updated successfully!'}); this.props.history.length>2 ? this.props.history.go(-2): this.props.history.push({ pathname: `/schedulingunit/view/draft/${this.props.match.params.id}`, }); } else { - this.growl.show({severity: 'error', summary: 'Error Occured', detail: schedulingUnit.message || 'Unable to Update Scheduling Unit/Tasks'}); + appGrowl.show({severity: 'error', summary: 'Error Occured', detail: schedulingUnit.message || 'Unable to Update Scheduling Unit/Tasks'}); } this.setState({isDirty: false}); publish('edit-dirty', false); @@ -870,26 +870,32 @@ export class EditSchedulingUnit extends Component { </div> } <ReactTooltip id="reacttooltip" place={'left'} type={'dark'} effect={'solid'} multiline={true} /> - <div className="p-grid p-justify-start"> - <div className="p-col-1"> + <div className="p-grid p-justify-start" > + <div className="p-col-1" style={{paddingLeft: '0.5em !important'}}> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSchedulingUnit} disabled={!this.state.constraintValidEditor || !this.state.validEditor || !this.state.validForm || !this.state.validConstraints || !this.state.validSpecification} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> <div className="p-grid" data-testid="confirm_dialog"> <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Scheduling Unit'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Edit Scheduling Unit'} message={'Do you want to discard your changes? Your changes to the scheduling unit will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} + > </CustomDialog> </div> </div> <CustomDialog type="confirmation" visible={this.state.errorDialog} width="40vw" - header={'Error'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Error'} message={'Do you want to discard your changes? Your changes to the scheduling unit will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose' }, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} + > </CustomDialog> </> } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js index 71c4e90b7d13bc6a1b53e2c7ec8b1c5dbcb2770c..2b38abd458425e88dde0895e530457dd5f57d664 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/excelview.schedulingset.js @@ -288,7 +288,7 @@ export class SchedulingSetCreate extends Component { onProjectChange(projectName) { if (this.state.isDirty) { this.showWarning(() =>{ - this. changeProject(projectName); + this.changeProject(projectName); }); } else { this.changeProject(projectName); @@ -2405,7 +2405,7 @@ export class SchedulingSetCreate extends Component { this.actions = [ {id: "copy_warning_btn", style: {float: 'left', backgroundColor: '#ffbb08', borderColor: '#ffbb08'}, title: "Copy Warning", callback: this.copyWarningContent}, {id: "yes_btn", title: "Yes", callback: this.saveSU}, - {id: "cancel_btn", title: "No", callback: this.onCancel},]; + {id: "cancel_btn", title: "No", className:'act-btn-cancel', callback: this.onCancel},]; this.dialogType = "confirmation"; this.dialogHeader = "Save Scheduling Unit(s)"; this.dialogMsg = "Some of the Scheduling Unit(s) has invalid data, Do you want to ignore and save valid Scheduling Unit(s) only?"; @@ -3061,12 +3061,14 @@ export class SchedulingSetCreate extends Component { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Add Multiple Scheduling Unit(s)"; - this.dialogMsg = "Do you want to leave this page? Your changes may not be saved."; + this.dialogMsg = "Do you want to discard your changes? Your changes to the Scheduling Unit(s) will not be saved."; this.dialogContent = ""; this.dialogHeight = '5em'; - this.callBackFunction = this.cancelCreate; + this.actions=[ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose' }, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]; + // this.callBackFunction = this.cancelCreate; this.onClose = this.close; - this.onCancel = this.close; + //this.onCancel = this.close; this.setState({ confirmDialogVisible: true, }); @@ -3276,7 +3278,7 @@ export class SchedulingSetCreate extends Component { this.dialogContent = this.pasteTextField; this.actions = [ {id: "ok_btn", title: "Ok", callback: this.copyFromClipboard}, - {id: "cancel_btn", title: "Cancel", callback: this.onCancel}]; + {id: "cancel_btn", title: "Cancel", className:'act-btn-cancel', callback: this.onCancel}]; await this.setState({confirmDialogVisible: true}); if(document.getElementById("clipTextField")) { document.getElementById("clipTextField").focus(); @@ -3458,8 +3460,11 @@ export class SchedulingSetCreate extends Component { this.showIcon = true; this.dialogType = "confirmation"; this.dialogHeader = "Add Multiple Scheduling Unit(s)"; - this.dialogMsg = "Do you want to leave the changes? Your changes may not be saved."; + this.dialogMsg = "Do you want to discard your changes? Your changes to the Scheduling Unit(s) will not be saved."; this.dialogContent = ""; + this.dialogHeight = '5em'; + this.actions=[ {id:"yes", title: 'Discard', callback: functionName, className:'act-btn-dispose' }, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]; this.callBackFunction = functionName; this.onClose = this.close; this.onCancel = this.close; @@ -3980,13 +3985,13 @@ export class SchedulingSetCreate extends Component { <i class="fa fa-exclamation-circle validationError" aria-hidden="true" onClick={this.showSpecificationError}></i> </div> } - <div className="p-grid p-justify-start"> + <div className="p-grid p-justify-start act-btn-grp"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" icon="pi pi-check" disabled={!(!this.state.isObsoletStrategy && scheduleunit_draft.create && scheduleunit_draft.edit) || this.state.isFetchingData} onClick={this.saveSchedulingUnit} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </div> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingset.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingset.create.js index 127b91a52ffc9fb4e649feddfed87a255e7eff7b..d50f7cf2fbafa5a6cb1c7973f4e73110b21765d3 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingset.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/schedulingset.create.js @@ -43,7 +43,7 @@ export class SchedulingSet extends Component { } } }}, - {id:"no", title: 'Cancel', callback: this.props.onCancel} ]; + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.props.onCancel} ]; this.formRules = { // Form validation rules name: {required: true, message: "Name can not be empty"}, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/sub.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/sub.create.js index b2080ce0f8a9b71ba8a82e716f71c8dedd5cc800..aa5ee5ec73ac7d6312b99cefc24761cac4a61ed1 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/sub.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/sub.create.js @@ -93,8 +93,8 @@ export default class SUBCreator extends Component { if (schedulingUnitsWithBlueprint.length === suList.selectedRows.length) { appGrowl.show({severity: 'warn', summary: 'Already Exists', detail: 'Blueprint(s) already exist for selected Scheduling Unit Draft(s)'}); } else { - dialog.actions = [ {id:"yes", title: 'Yes', callback: this.createBlueprintTreeNewOnly,className:(this.props.project)?"dialog-btn": ""}, - {id:"no", title: 'No', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": ""} ] + dialog.actions = [ {id:"yes", title: 'Create Blueprints', callback: this.createBlueprintTreeNewOnly,className:(this.props.project)?"dialog-btn": ""}, + {id:"no", title: 'Cancel', callback: this.closeDialog, className:(this.props.project)?"dialog-btn": "act-btn-cancel"} ] /* Add this action only when both new and old drafts are selected */ // if (schedulingUnitsWithBlueprint.length > 0 && suList.selectedRows.length>schedulingUnitsWithBlueprint.length) { // dialog.actions.unshift({id:"newOnly", title: 'Create Only New', callback: this.createBlueprintTreeNewOnly}); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js index 57e28b6629a8506938a5e60e1ddff6b37b6677cd..27e3bd263b0a7af44e55db25a7847894eadb6940 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/summary.js @@ -341,7 +341,7 @@ export class SchedulingUnitSummary extends Component { } } /> - <Button key="cancel" className="p-button-danger" icon="pi pi-times" label="Cancel" onClick={() => { + <Button key="cancel" className="act-btn-cancel" icon="pi pi-times" label="Cancel" onClick={() => { if (this.state.editCheck < 3) { this.setState({ editCheck:1 @@ -367,7 +367,7 @@ export class SchedulingUnitSummary extends Component { {/* moder for Constraints End */} <CustomDialog type="confirmation" visible={this.props.cancelStatus} width="40vw" - header={'Edit Scheduling Constraints'} message={'Do you want to leave this page? Your changes may not be saved'} + header={'Edit Scheduling Constraints'} message={'Do you want to discard your changes? Your changes to the Constraints will not be saved.'} content={''} onClose = { () => { @@ -385,31 +385,28 @@ export class SchedulingUnitSummary extends Component { } } } - onSubmit = { - () =>{ - this.setState({ - editCheck: 1, - close:true - }, this.props.onCloseFn(false)) - return false; - } - } - onCancel = { - () => { - if (this.state.editCheck < 3 && this.state.close === true) { - this.setState({ - editCheck: 1, - close: false - }, this.props.cancelCheckstatus(false)); - } else if (this.state.editCheck < 3 && this.state.close === false) { - this.setState({ - editCheck: 3 - }, this.props.cancelCheckstatus(false)); - } else { - this.props.cancelCheckstatus(false) - } - } - } + + actions={ [ {id:"yes", title: 'Discard', callback: () =>{ + this.setState({ + editCheck: 1, + close:true + }, this.props.onCloseFn(false)) + return false; + }, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: () => { + if (this.state.editCheck < 3 && this.state.close === true) { + this.setState({ + editCheck: 1, + close: false + }, this.props.cancelCheckstatus(false)); + } else if (this.state.editCheck < 3 && this.state.close === false) { + this.setState({ + editCheck: 3 + }, this.props.cancelCheckstatus(false)); + } else { + this.props.cancelCheckstatus(false) + } + }} ]} > </CustomDialog> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/existing.system.event.list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/existing.system.event.list.js index 998a8a0721c54f3f40ba428d64125c777697b3b6..ce64a92bf57b9553ee7364629e71d352b8379a77 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/existing.system.event.list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/existing.system.event.list.js @@ -484,7 +484,7 @@ export class ExistingSystemEventList extends Component{ maximizable maximized={false} onHide={this.closeAddTaskDialog} inputId="confirm_dialog" footer={<div> <Button label="Save" icon="pi pi-check" title='Save the System Event with updated Task' onClick={() => {this.addTaskToSystemEvent()}} style={{width: '9em'}} disabled={!this.state.isPickListUpdated}/> - <Button className="p-button-danger" icon="pi pi-times" label="Cancel" onClick={this.closeAddTaskDialog} /> + <Button className="act-btn-cancel" icon="pi pi-times" label="Cancel" onClick={this.closeAddTaskDialog} /> </div> } > diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.create.js index 24bd5c8f6cbb93f57f9a0555ab62aae5e4e0ee24..f4d9e19fc8001f1050f94467865276b0fb26be59 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.create.js @@ -23,6 +23,7 @@ import {AffectedTasksSelector} from './affeted.tasks.selector'; import { Chips } from 'primereact/chips'; import { Link } from 'react-router-dom'; import { publish } from '../../App'; +import { Checkbox } from 'primereact/checkbox'; /** * Component to create a new System Event @@ -63,7 +64,9 @@ export class SystemEventCreate extends Component { validFields: {}, // For Validation validForm: false, // To enable Save Button validEditor: false, - dialogFilters: {} + dialogFilters: {}, + createAnother: false, + multiCreateCount: 1 }; this.tmpSystemEvent = {}; this.regex = "/^[-,0-9 ]+$/"; @@ -461,7 +464,7 @@ export class SystemEventCreate extends Component { async saveSystemEvent() { let systemevent = this.state.systemevent; systemevent['start'] = moment(systemevent['start']).format(UIConstants.CALENDAR_DATETIME_FORMAT); - systemevent['stop'] = systemevent['stop'] ? moment(systemevent['stop_time']).format(UIConstants.CALENDAR_DATETIME_FORMAT) : null; + systemevent['stop'] = systemevent['stop'] ? moment(systemevent['stop']).format(UIConstants.CALENDAR_DATETIME_FORMAT) : null; systemevent['affected_hardware_template'] = this.state.seAffectedHardwareTemplate.url; systemevent['affected_hardware_doc'] = this.paramsOutput; let affected_tasks = []; @@ -479,12 +482,16 @@ export class SystemEventCreate extends Component { systemevent = await SystemEventService.saveSystemEvent(systemevent); if (systemevent && systemevent.id) { - const dialog = { header: 'Success', detail: 'System Event is created successfully. Do you want to create another System Event?' - , style: {width: '25vw', height: 'auto'}, showAffectedTasks: false, cancelLabel: 'No', submitLabel: 'Yes', - onCancel: this.closeAndRedirect, onsubmit: this.reset}; + appGrowl.show({severity: 'success', summary: 'Success', detail: 'System Event is created successfully.'}); this.affectedTaskList = []; publish('edit-dirty', false); - this.setState({affectedTaskChips: [], dialogVisible: true, dialog: dialog, paramsOutput: {}, showDialog: false, isDirty: false }) + if (this.state.createAnother) { + this.setState({affectedTaskChips: [], paramsOutput: {}, showDialog: false, isDirty: false }); + this.reset(); + } else { + this.setState({affectedTaskChips: [], paramsOutput: {}, showDialog: false, isDirty: false, redirect: '/systemevent/list' }); + } + } else { appGrowl.show({severity: 'error', summary: 'Error', detail: 'Unable to add new System Event'}); } @@ -511,9 +518,8 @@ export class SystemEventCreate extends Component { affected_hardware_doc: '', } + let multiCreateCount = this.state.multiCreateCount; this.setState({ - dialogVisible: false, - dialog: { header: '', detail: '' }, errors: [], systemevent: this.tmpSystemevent, paramsSchema: null, @@ -524,7 +530,8 @@ export class SystemEventCreate extends Component { stationGroup: [], showDialog: false, isDirty: false, - affectedTaskChips: [] + affectedTaskChips: [], + multiCreateCount: ++multiCreateCount }); this.initSystemEvent(); } @@ -533,8 +540,12 @@ export class SystemEventCreate extends Component { * Cancel System Event creation and redirect */ cancelCreate() { - publish('edit-dirty', false); - this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/systemevent/list`); + publish('edit-dirty', false); + if (this.state.multiCreateCount > 1) { + this.props.history.go(this.state.multiCreateCount*-1); + } else { + this.props.history.length>1?this.state.isDirty?this.props.history.go(-2):this.props.history.go(-1):this.props.history.push(`/systemevent/list`); + } this.setState({ showDialog: false }); } @@ -900,7 +911,7 @@ export class SystemEventCreate extends Component { </div> </div> - <div className="p-grid"> + <div className="p-grid" style={{marginLeft: '0.1em'}}> <fieldset className="border-style"> <div className="p-col-12"> {this.state.paramsSchema ? jeditor : ""} @@ -910,15 +921,26 @@ export class SystemEventCreate extends Component { </div> {!this.props.systemevent && - <div className="p-grid p-justify-start"> - <div className="p-col-1"> + <> + <div className="p-col-1.5" style={{marginTop: '1em', marginLeft: '0.9em'}}> + <Checkbox inputId="trigger" role="trigger" + tooltip="Select checkbox to create another system event after saving this system event" + tooltipOptions={this.tooltipOptions} + checked={this.state.createAnother} + onChange={e => this.setState({'createAnother': e.target.checked})} + style={{marginRight: '0.25em',}}></Checkbox> + <label style={{display: 'inline'}}> Create another? </label> + </div> + <div className="p-grid p-justify-start act-btn-grp"> + <div className="p-col-1" style={{marginLeft: '0.15em'}}> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveSystemEvent} disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> + </> } </div> </> @@ -932,7 +954,7 @@ export class SystemEventCreate extends Component { {(this.state.dialog.submitLabel === 'Apply') && <> <Button key="submit" type="primary" onClick={this.state.dialog.onsubmit} label={this.state.dialog.submitLabel} /> - <Button key="back" className="p-button-danger" onClick={() => {this.state.dialog.onCancel?this.state.dialog.onCancel(): this.setState({ dialogVisible: false }) }} label={this.state.dialog.cancelLabel} /> + <Button key="back" className="act-btn-cancel" onClick={() => {this.state.dialog.onCancel?this.state.dialog.onCancel(): this.setState({ dialogVisible: false }) }} label={this.state.dialog.cancelLabel} /> </> } {(this.state.dialog.submitLabel !== 'Apply') && @@ -967,8 +989,10 @@ export class SystemEventCreate extends Component { </Dialog> <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Add System Event'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Add System Event'} message={'Do you want to discard your changes? A new System Event will not be created.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose' }, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> </div> </React.Fragment> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.edit.js index 545d262fa9bce166cfc966471bcda45112d6e3ff..48094cb62e6fe31c661f459b3b73a6a802870fca 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/SystemEvent/system.event.edit.js @@ -772,7 +772,7 @@ export class SystemEventEdit extends Component { /> </div> </div> - <div className="p-grid"> + <div className="p-grid" style={{marginLeft: '0.1em'}}> <fieldset className="border-style"> <div className="p-col-12"> {this.state.paramsSchema ? jeditor : ""} @@ -782,12 +782,12 @@ export class SystemEventEdit extends Component { </div> <div className="p-grid p-justify-start"> - <div className="p-col-1"> + <div className="p-col-1" style={{marginLeft: '0.9em'}}> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.updateSystemEvent} disabled={!this.state.validEditor || !this.state.validForm} data-testid="save-btn" /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> </div> @@ -800,7 +800,7 @@ export class SystemEventEdit extends Component { {(this.state.dialog.submitLabel === 'Apply') && <> <Button key="submit" type="primary" onClick={this.state.dialog.onsubmit} label={this.state.dialog.submitLabel} /> - <Button key="back" onClick={() => {this.state.dialog.onCancel?this.state.dialog.onCancel(): this.setState({ dialogVisible: false }) }} label={this.state.dialog.cancelLabel} /> + <Button key="back" className="act-btn-cancel" onClick={() => {this.state.dialog.onCancel?this.state.dialog.onCancel(): this.setState({ dialogVisible: false }) }} label={this.state.dialog.cancelLabel} /> </> } {(this.state.dialog.submitLabel !== 'Apply') && @@ -834,8 +834,10 @@ export class SystemEventEdit extends Component { </Dialog> </div> <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit System Event'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Edit System Event'} message={'Do you want to discard your changes? Your changes to the System Event will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, className:'act-btn-dispose'}, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> </React.Fragment> ); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js index 0f7a37ecc3346dd7f17fee6b4be6879bfc7c907d..3cf8db2a8103f535369809bf2792cffc33e47caf 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/edit.js @@ -1,7 +1,8 @@ import React, { Component} from 'react'; import { Link, Redirect } from 'react-router-dom'; import _ from 'lodash'; - +import { Toast } from 'primereact/toast'; +import { appGrowl, setAppGrowl } from '../../layout/components/AppGrowl'; import { InputText } from 'primereact/inputtext'; import { InputTextarea } from 'primereact/inputtextarea'; import { Chips } from 'primereact/chips'; @@ -191,6 +192,10 @@ export class TaskEdit extends Component { TaskService.updateTask("draft", task) .then( (taskDraft) => { if (taskDraft) { + if (appGrowl === null) { + setAppGrowl(this.growl) + } + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Tasks updated successfully.'}); this.props.history.length>2 && this.props.history.go(-2); this.setState({redirect: '/task/view/draft/' + task.id}); } @@ -290,6 +295,7 @@ export class TaskEdit extends Component { </div> */} {this.state.userrole[this.state.taskId] && this.state.userrole[this.state.taskId].edit ? <> + <Toast ref={(el) => this.growl = el} /> <PageHeader location={this.props.location} title={'Task - Edit'} actions={[{icon: 'fa-window-close', title:'Click to Close Task Edit Page',type: 'button', actOn: 'click',props : { pathname: `/task/view/draft/${this.state.task?this.state.task.id:''}`,callback: this.checkIsDirty}}]}/> {isLoading ? <AppLoader/> : @@ -325,7 +331,7 @@ export class TaskEdit extends Component { {this.state.schedulingUnit && <> <label className="col-lg-2 col-md-2 col-sm-12">Scheduling Unit</label> - <Link className="col-lg-4 col-md-4 col-sm-12" to={ { pathname:'/schedulingunit/view', state: {id: this.state.schedulingUnit.id}}}>{this.state.schedulingUnit?this.state.schedulingUnit.name:''}</Link> + <Link className="col-lg-4 col-md-4 col-sm-12" to={ { pathname:`/schedulingunit/view/draft/${this.state.schedulingUnit.id}`, state: {id: this.state.schedulingUnit.id}}}>{this.state.schedulingUnit?this.state.schedulingUnit.name:''}</Link> </> } </div> @@ -355,7 +361,7 @@ export class TaskEdit extends Component { </div> </div> - <div className="p-grid p-justify-start"> + <div className="p-grid p-justify-start act-btn-grp"> <div className="col-lg-4 col-md-4 col-sm-12"> <label className={this.state.validDocument ? "info" : "error"}> {this.state.validation_message ? this.state.validation_message : ""} @@ -363,19 +369,22 @@ export class TaskEdit extends Component { </div> </div> - <div className="p-grid p-justify-start"> + <div className="p-grid p-justify-start act-btn-grp"> <div className="p-col-1"> <Button label="Save" className="p-button-primary" icon="pi pi-check" onClick={this.saveTask} data-testid="save-btn" disabled={!this.state.validEditor || !this.state.validForm} /> </div> <div className="p-col-1"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.checkIsDirty} /> + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" onClick={this.checkIsDirty} /> </div> </div> <div className="p-grid" data-testid="confirm_dialog"> <CustomDialog type="confirmation" visible={this.state.showDialog} width="40vw" - header={'Edit Task'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> + header={'Edit Task'} message={'Do you want to discard your changes? Your changes to the Task will not be saved.'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelEdit, style:{"background": "#e91224","border": "1px solid #e91224"} }, + {id:"no", title: 'Cancel', callback: this.close} ]} + > </CustomDialog> </div> </>: <AccessDenied/>} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js index 513a957b9c6cdca684f41796bcd8599a6be4ac84..ddbab82c8ddfda068414df95e6d7afd4f6308f95 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js @@ -18,7 +18,6 @@ import UnitConverter from '../../utils/unit.converter'; import UtilService from '../../services/util.service'; import { Link } from 'react-router-dom'; import AuthUtil from '../../utils/auth.util'; -import AuthStore from '../../authenticate/auth.store'; import AccessDenied from '../../layout/components/AccessDenied'; import Websocket from 'react-websocket'; @@ -494,8 +493,8 @@ export class TaskList extends Component { dialog.header = "Confirm to Delete Task(s)"; dialog.detail = "Do you want to delete the selected Task(s)?"; dialog.content = this.getTaskDeleteDialogContent; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.deleteTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Delete Tasks', callback: this.deleteTasks, className:'act-btn-dispose' }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.onSubmit = this.deleteTasks; dialog.width = '55vw'; dialog.showIcon = false; @@ -680,8 +679,8 @@ export class TaskList extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Task(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Cancel Tasks', callback: this.cancelTasks, className:'act-btn-dispose' }, + { id: 'no', title: 'No', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Warning: Cancelling the task means, it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled task(s) will be ignored. Do you want to proceed?"; dialog.content = this.getTaskCancelConfirmContent; dialog.submit = this.cancelTasks; @@ -715,8 +714,8 @@ export class TaskList extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to mark task(s) as obsolete"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markObsoleteTasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Mark as obsolete', className:'act-btn-dispose', callback: this.markObsoleteTasks }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Warning: Obsolete task(s) will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task(s) obsolete?"; dialog.content = this.getObsTaskConfirmDlgContent; dialog.submit = this.markObsoleteTasks; @@ -752,7 +751,7 @@ export class TaskList extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Cancel Task(s) Status"; - dialog.actions = [{ id: 'no', title: 'Ok', callback: this.closeDialog }]; + dialog.actions = [{ id: 'no', title: 'Close', callback: this.closeDialog }]; dialog.detail = "" dialog.content = this.getCancelTaskStatusContent; dialog.submit = this.closeDialog; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js index 6410a29a3bcca05b304290d243b946db5237702b..2cebc32cabaf0f81724eda93ec0c138a8c0814fe 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/view.js @@ -284,6 +284,8 @@ export class TaskView extends Component { */ showDeleteConfirmation() { let dialog = this.state.dialog; + dialog.actions = [{ id: 'yes', title: 'Delete Task', callback: this.deleteTask, className:'act-btn-dispose' }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.type = "confirmation"; dialog.header = "Confirm to Delete Task"; dialog.showIcon = false; @@ -351,8 +353,8 @@ export class TaskView extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to Cancel Task"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelTask }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Cancel Task', callback: this.cancelTask, className:'act-btn-dispose' }, + { id: 'no', title: 'No', className:'act-btn-cancel', callback: this.closeDialog }]; if (this.TASK_NOT_STARTED_STATUSES.indexOf(this.state.task.status) >= 0) { dialog.detail = "Cancelling this task means it will no longer be executed. This action cannot be undone. Do you want to proceed?"; } else if (this.TASK_ACTIVE_STATUSES.indexOf(this.state.task.status) >= 0) { @@ -371,8 +373,8 @@ export class TaskView extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to mark task as obsolete"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markTaskObsolete }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Mark as obsolete', className:'act-btn-dispose', callback: this.markTaskObsolete }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Warning: Obsolete tasks will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the task obsolete?"; dialog.content = ''; dialog.submit = this.markTaskObsolete; @@ -592,8 +594,8 @@ export class TaskView extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to cancel Subtask(s)"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.cancelSubtasks }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Cancel Subtasks', callback: this.cancelSubtasks, className:'act-btn-dispose' }, + { id: 'no', title: 'No', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Cancelling the subtask means, it will no longer be executed / will be aborted. This action cannot be undone. Already finished/cancelled subtask(s) will be ignored. Do you want to proceed?"; dialog.content = this.getSubtaskCancelConfirmContent; dialog.submit = this.cancelSubtasks; @@ -625,8 +627,8 @@ export class TaskView extends Component { let dialog = this.state.dialog; dialog.type = "confirmation"; dialog.header = "Confirm to mark Subtask(s) as obsolete"; - dialog.actions = [{ id: 'yes', title: 'Yes', callback: this.markSubtasksObsolete }, - { id: 'no', title: 'No', callback: this.closeDialog }]; + dialog.actions = [{ id: 'yes', title: 'Mark as obsolete', className:'act-btn-dispose', callback: this.markSubtasksObsolete }, + { id: 'no', title: 'Cancel', className:'act-btn-cancel', callback: this.closeDialog }]; dialog.detail = "Warning: Obsolete subtask will not be copied and dataproducts will not be used for pipelines and ingest. Are you sure you want to make the subtask obsolete?"; dialog.content = this.getSubtaskObsoleteConfirmContent; dialog.submit = this.markSubtasksObsolete; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/qa.reporting.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/qa.reporting.js index c5d4fa139c5014e6138a9511a86b953d29055b18..f283dba7ef178f3b468be7ed51d1899ff0bdf859 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/qa.reporting.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Workflow/qa.reporting.js @@ -398,7 +398,7 @@ class QAreporting extends Component{ label="Next" className="p-button-primary" icon="pi pi-check" onClick={ this.Next } /> </div> <div className="btn-bar"> - <Button label="Cancel" className="p-button-danger" icon="pi pi-times" style={{ width : '88px' }} + <Button label="Cancel" className="act-btn-cancel" icon="pi pi-times" style={{ width : '88px' }} onClick={(e) => { this.props.onCancel()}} /> </div> </div>} @@ -414,7 +414,7 @@ class QAreporting extends Component{ maximizable maximized={false} onHide={this.checkIsDirty} inputId="confirm_dialog" footer={<div> <Button label="Save" icon="pi pi-check" onClick={() => {this.saveSystemEvent()}} style={{width: '6em'}} disabled={!this.state.validForm} /> - <Button className="p-button-danger" icon="pi pi-times" label="Cancel" onClick={this.checkIsDirty} /> + <Button className="act-btn-cancel" icon="pi pi-times" label="Cancel" onClick={this.checkIsDirty} /> </div> } > @@ -430,15 +430,17 @@ class QAreporting extends Component{ </Dialog> <CustomDialog type="confirmation" visible={this.state.showCustomDialog} width="40vw" - header={'Add System Event'} message={'Do you want to leave this page? Your changes may not be saved.'} - content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelCreate}> + header={'Add System Event'} message={'Do you want to discard your changes? Your changes may not be saved..'} + content={''} onClose={this.close} + actions={ [ {id:"yes", title: 'Discard', callback: this.cancelCreate, className:'act-btn-dispose' }, + {id:"no", title: 'Cancel', className:'act-btn-cancel', callback: this.close} ]} > </CustomDialog> {this.state.showExistingSEDialog && <Dialog header={"Existing System Events"} style={{width: '70vw'}} visible={this.state.showExistingSEDialog} maximizable maximized={false} onHide={this.close} inputId="confirm_dialog" footer={<div> <Button label="Add Tasks" icon="pi pi-check" onClick={() => {this.showAddTasksDialog()}} style={{width: '9em'}} /> - <Button className="p-button-danger" icon="pi pi-times" label="Close" onClick={this.close} /> + <Button className="act-btn-cancel" icon="pi pi-times" label="Close" onClick={this.close} /> </div> } >