diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py index 4c00aa3da2312e0849ca93f5deb0d3a67bee7a9a..9290ce78a3dcba98258d8b69ac482ce65f2b0a0c 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/models/specification.py @@ -308,7 +308,7 @@ class ProjectQuota(Model): class ProjectQuotaArchiveLocation(Model): - project_quota = ForeignKey('ProjectQuota', null=False, related_name="project_quota", on_delete=PROTECT, help_text='Project to wich this quota belongs.') + project_quota = ForeignKey('ProjectQuota', null=False, related_name="project_quota_archive_location", on_delete=PROTECT, help_text='The ProjectQuota for this archive location') archive_location = ForeignKey('Filesystem', null=False, on_delete=PROTECT, help_text='Location of an archive LTA cluster.') def save(self, force_insert=False, force_update=False, using=None, update_fields=None): diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py index 650d1f6816667256d074ac1994ab3c37a8727d37..40e976e83e8bc7052da21cef74e8f8d9c363e9d6 100644 --- a/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py +++ b/SAS/TMSS/backend/src/tmss/tmssapp/serializers/specification.py @@ -174,7 +174,7 @@ class ProjectQuotaSerializer(DynamicRelationalHyperlinkedModelSerializer): class Meta: model = models.ProjectQuota fields = '__all__' - extra_fields = ['resource_type'] + extra_fields = ['resource_type', 'project_quota_archive_location'] class ProjectQuotaArchiveLocationSerializer(DynamicRelationalHyperlinkedModelSerializer): diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 12ca8a5aa7118073ce961d010b690f1d54143a1a..74a6f8c2bd0dc57fea26971a4d83c1bf3d076c4b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -8,6 +8,9 @@ import {AppFooter } from './layout/components/AppFooter'; import {RoutedContent} from './routes'; import {AppBreadcrumb } from "./layout/components/AppBreadcrumb"; import {withRouter } from 'react-router'; +import handleResponse from "./response.handler" +import { setAppGrowl } from './layout/components/AppGrowl'; +import { Growl } from 'primereact/components/growl/Growl'; import 'primeicons/primeicons.css'; import 'primereact/resources/themes/nova-light/theme.css'; @@ -139,6 +142,7 @@ class App extends Component { //console.log(this.props); return ( <React.Fragment> + <Growl ref={(el) => setAppGrowl(el)} /> <div className="App"> {/* <div className={wrapperClass} onClick={this.onWrapperClick}> */} <div className={wrapperClass}> @@ -177,4 +181,4 @@ class App extends Component { } } -export default App; +export default handleResponse(App); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/response.handler.js b/SAS/TMSS/frontend/tmss_webapp/src/response.handler.js new file mode 100644 index 0000000000000000000000000000000000000000..7c4da4c87de73f67983fb60f36e2c6aff269ab8d --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/response.handler.js @@ -0,0 +1,45 @@ +import React, {useEffect} from "react"; +import axios from "axios"; +import { appGrowl } from './layout/components/AppGrowl'; +import UIConstants from './utils/ui.constants'; +import Auth from './authenticate/auth'; +/** + * Trigger and validate the response for https status code + * @param {*} Wrapped + * @returns + */ +const handleResponse= Wrapped => { + function HandleResponse(props) { + useEffect(()=>{ + axios.interceptors.response.use(function (response) { + return response; + }, function (error) { + showMessage(error.response); + return Promise.reject(error); + }); + }) + return ( + <Wrapped {...props} /> + ); + } + + /** + * Catch relavent http status code details to show in growl + * @param {*} response + */ + function showMessage(response) { + const httpStatusMsg = UIConstants.httpStatusMessages[response.status]; + if(httpStatusMsg) { + appGrowl.show({severity: httpStatusMsg.severity, summary: httpStatusMsg.summary, sticky: httpStatusMsg.sticky, detail: '['+response.status+'] '+JSON.stringify(response.statusText)+ ' ['+httpStatusMsg.detail+']'}); + } else { + appGrowl.show({severity: 'error', summary: 'Error', sticky: 'true', detail: '['+response.status+'] '+JSON.stringify(response.statusText)+ ' '+JSON.stringify(response.data)}); + } + if (response.status === 401) { + Auth.logout(); + window.location.href = "/login"; + } + } + return HandleResponse; +} + +export default handleResponse; \ No newline at end of file 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 7ef3b05cc8bf50d554ae4f4fcd64e409b7e853c0..b64a1d66d444ac9001fe31e5ce2486d70f8c54b8 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Cycle/edit.js @@ -318,7 +318,7 @@ export class CycleEdit extends Component { this.saveCycleQuota(cycle); } else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to update Cycle'}); - this.setState({errors: cycle}); + //this.setState({errors: cycle}); } }); } @@ -373,7 +373,7 @@ export class CycleEdit extends Component { if (_.keys(quotaError).length === 0) { dialog = {header: 'Success', detail: 'Cycle updated successfully.'}; } else { - dialog = {header: 'Error', detail: 'Cycle updated successfully but resource allocation not updated properly. Try again!'}; + dialog = {header: 'Error', detail: 'Cycle updated successfully but resource allocation not updated properly.'}; } this.setState({dialogVisible: true, dialog: dialog}); } 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 c21f5afcd834388b99fe3836cd7d598be970a608..caf0c0b6e487bff2e139cdab5f857a9cc0bca1c5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/create.js @@ -325,12 +325,17 @@ export class ProjectCreate extends Component { } ProjectService.saveProject(this.state.project, this.defaultResourcesEnabled?projectQuota:[]) .then(project => { + if (project.url) { let dialog = {}; - if (this.defaultResourcesEnabled) { - dialog = {header: 'Success', detail: 'Project saved successfully. Do you want to create another project?'}; + if (project.isQuotaCreated) { + if (this.defaultResourcesEnabled) { + dialog = {header: 'Success', detail: 'Project saved successfully. Do you want to create another project?'}; + } else { + dialog = {header: 'Success', detail: 'Project saved successfully with default Resource allocations. Do you want to view and edit them?'}; + } } else { - dialog = {header: 'Success', detail: 'Project saved successfully with default Resource allocations. Do you want to view and edit them?'}; + dialog = {header: 'Warning', detail: 'Project saved successfully, but resource allocation not saved.'}; } this.setState({project:project, dialogVisible: true, dialog: dialog, isDirty: false}); } else { 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 b8bd0f3e2f9833bf6290e035240e88bad4d9695d..ac275f366da7624c2c9a2149b18690d2b9db297a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Project/edit.js @@ -335,7 +335,7 @@ export class ProjectEdit extends Component { // project['archive_subdirectory'] = (project['archive_subdirectory'].substr(-1) === '/' ? project['archive_subdirectory'] : `${project['archive_subdirectory']}/`).toLowerCase(); ProjectService.updateProject(this.props.match.params.id, project) .then(async (project) => { - if (project && this.state.project.updated_at !== project.updated_at) { + if (project && project.isUpdated && this.state.project.updated_at !== project.updated_at) { this.saveProjectQuota(project); } else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to update Project'}); @@ -381,20 +381,20 @@ export class ProjectEdit extends Component { } for (const projectQuota of updatingProjectQuota) { const updatedProjectQuota = await ProjectService.updateProjectQuota(projectQuota); - if (!updatedProjectQuota) { + if (!updatedProjectQuota || (updatedProjectQuota.status && updatedProjectQuota.status > 299)) { quotaError[projectQuota.resource_type_id] = true; } } for (const projectQuota of newProjectQuota) { const createdProjectQuota = await ProjectService.saveProjectQuota(projectQuota); - if (!createdProjectQuota) { + if (!createdProjectQuota || (createdProjectQuota.status && createdProjectQuota.status > 299)) { quotaError[projectQuota.resource_type_id] = true; } } if (_.keys(quotaError).length === 0) { dialog = {header: 'Success', detail: 'Project updated successfully.'}; } else { - dialog = {header: 'Error', detail: 'Project updated successfully but resource allocation not updated properly. Try again!'}; + dialog = {header: 'Error', detail: 'Project updated successfully but resource allocation not updated properly.'}; } this.setState({dialogVisible: true, dialog: dialog, isDirty: false}); } 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 161657340ba91b073e152f4afd04f45212a892de..6e8e565a824195df4de6e33736a6715eea03e577 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 @@ -1723,12 +1723,13 @@ export class SchedulingSetCreate extends Component { async saveSU() { let newSUCount = 0; let existingSUCount = 0; + let isUpdated = true; try{ this.setState({ // saveDialogVisible: false, confirmDialogVisible: false, showSpinner: true - }) + }); let newSU = this.state.schedulingUnit; let parameters = this.state.schedulingUnitList[0]['requirements_doc'].parameters; @@ -1911,7 +1912,10 @@ export class SchedulingSetCreate extends Component { if(taskdata){ taskDrafts = taskdata.data.results; } - await ScheduleService.updateSUDraftFromObservStrategy(observStrategy, newSU, taskDrafts, this.state.tasksToUpdate, tmpStationGroups); + let updateSu = await ScheduleService.updateSUDraftFromObservStrategy(observStrategy, newSU, taskDrafts, this.state.tasksToUpdate, tmpStationGroups); + if (updateSu && !updateSu.isSUUpdated) { + isUpdated = false; + } existingSUCount++; } else if (suRow.id === 0 && this.isNotEmpty(suRow.suname) && this.isNotEmpty(suRow.sudesc)){ @@ -1921,7 +1925,10 @@ export class SchedulingSetCreate extends Component { scheduling_constraints_template_id: newSU['scheduling_constraints_template_id'], scheduling_set_id: newSU['scheduling_set_id'] } - await ScheduleService.saveSUDraftFromObservStrategy(observStrategy, newSchedulueUnit, newConstraint, tmpStationGroups); + let updateSu = await ScheduleService.saveSUDraftFromObservStrategy(observStrategy, newSchedulueUnit, newConstraint, tmpStationGroups); + if (updateSu && !updateSu.isSUUpdated) { + isUpdated = false; + } newSUCount++; } } @@ -1932,7 +1939,13 @@ export class SchedulingSetCreate extends Component { this.dialogType = "success"; this.dialogHeader = "Success"; this.showIcon = true; - this.dialogMsg = '['+newSUCount+'] Scheduling Units are created & ['+existingSUCount+'] Scheduling Units are updated successfully.'; + if (isUpdated) { + this.dialogMsg = '['+newSUCount+'] Scheduling Units are created & ['+existingSUCount+'] Scheduling Units are updated successfully.'; + } else { + this.dialogHeader = "Warning"; + this.dialogMsg = '['+newSUCount+'] Scheduling Units are created & ['+existingSUCount+'] Scheduling Units are updated successfully, and there are some Schedule Unit/Task failed to create/update'; + } + this.dialogContent = ""; this.onCancel = this.close; this.onClose = this.close; @@ -1944,6 +1957,7 @@ export class SchedulingSetCreate extends Component { } }catch(err){ this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to create/update Scheduling Units'}); + this.setState({showSpinner: false}); } } 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 b9273de4b8be558f5f7cd8345c1dc7152c78f017..2b34d370565a6284dde6d7b40b222befe78a564c 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 @@ -5,6 +5,7 @@ import UIConstants from '../../utils/ui.constants'; import { CustomDialog } from '../../layout/components/CustomDialog'; import ScheduleService from '../../services/schedule.service'; import { Growl } from 'primereact/components/growl/Growl'; +import { appGrowl } from './../../layout/components/AppGrowl'; export class SchedulingSet extends Component { @@ -32,13 +33,13 @@ export class SchedulingSet extends Component { schedulingSet['generator_doc'] = {}; schedulingSet['scheduling_unit_drafts'] = []; const suSet = await ScheduleService.saveSchedulingSet(schedulingSet); - if (suSet.id !== null) { - this.growl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Set is created successfully.'}); + if (suSet.id && suSet.id !== null) { + appGrowl.show({severity: 'success', summary: 'Success', detail: 'Scheduling Set is created successfully.'}); this.setState({suSet: suSet, dialogVisible: true, }); this.props.onCancel(); - } else { + } /* else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: schedulingSet.message || 'Unable to save Scheduling Set'}); - } + } */ } }}, {id:"no", title: 'Cancel', callback: this.props.onCancel} ]; diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js index d80adff4e739e1a55cf1eefe06aea1f7dff7f69f..e1b884053169d5a05dd9ab001e45af3c7ae0804a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/reservation.create.js @@ -238,7 +238,7 @@ export class ReservationCreate extends Component { } } - saveReservation(){ + async saveReservation(){ let reservation = this.state.reservation; let project = this.projects.find(project => project.name === reservation.project); reservation['start_time'] = moment(reservation['start_time']).format(UIConstants.CALENDAR_DATETIME_FORMAT); @@ -246,13 +246,13 @@ export class ReservationCreate extends Component { reservation['project']= project ? project.url: null; reservation['specifications_template']= this.reservationTemplates[0].url; reservation['specifications_doc']= this.paramsOutput; - reservation = ReservationService.saveReservation(reservation); - if (reservation && reservation !== null){ + 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}) - } else { + }/* else { this.growl.show({severity: 'error', summary: 'Error Occured', detail: 'Unable to save Reservation', showDialog: false, isDirty: false}); - } + }*/ } /** diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js index 095f5b009d342fdc124972bcd8ece09ac4eab7d3..9e110bc09653909d2045fea90ae3b68107cefaa7 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/cycle.service.js @@ -100,7 +100,7 @@ const CycleService = { return response.data; } catch (error) { console.log(error.response.data); - return error.response.data; + //return error.response.data; } }, deleteCycleQuota: async function(cycleQuota) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js index 2ad00beed53a2655373cb373486180ab375ef8a1..5613c91deba01e0375f622e7e16700d872c9a494 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/project.service.js @@ -68,10 +68,14 @@ const ProjectService = { saveProject: async function(project, projectQuota) { try { const response = await axios.post(('/api/project/'), project); - project = response.data - for (let quota of projectQuota) { + project = response.data; + project['isQuotaCreated'] = true; + for (let quota of projectQuota) { quota.project = project.url; - this.saveProjectQuota(quota); + let response = await this.saveProjectQuota(quota); + if (response.status > 299) { + project['isQuotaCreated'] = false; + } } return response.data; } catch (error) { @@ -83,29 +87,33 @@ const ProjectService = { updateProject: async function(id, project) { try { const response = await axios.put((`/api/project/${id}/`), project); - return response.data; + project = response.data; + project['isUpdated'] = true; + return project; } catch (error) { - // console.log(error); console.log(error.response.data); - return error.response.data; + project = error.response.data; + project['isUpdated'] = false; + return project; } }, saveProjectQuota: async function(projectQuota) { try { const response = await axios.post(('/api/project_quota/'), projectQuota); - return response.data; + return response; } catch (error) { console.error(error); - return null; + return error.response; } }, updateProjectQuota: async function(projectQuota) { + const response = null; try { const response = await axios.put(`/api/project_quota/${projectQuota.id}/`, projectQuota); return response.data; } catch (error) { console.error(error); - return null; + return response; } }, deleteProjectQuota: async function(projectQuota) { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js index db6284425c56b108d1b5ff8dd08a341e6a78a9ef..eb1d3364f5d71f6eb31b17ba27803daafdd6f057 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/schedule.service.js @@ -493,9 +493,10 @@ const ScheduleService = { updateSUDraftFromObservStrategy: async function(observStrategy,schedulingUnit,tasks,tasksToUpdate,station_groups) { try { delete schedulingUnit['duration']; - + schedulingUnit['isSUUpdated'] = false; schedulingUnit = await this.updateSchedulingUnitDraft(schedulingUnit); if (!schedulingUnit.error) { + schedulingUnit['isSUUpdated'] = true; for (const taskToUpdate in tasksToUpdate) { let task = tasks.find(task => { return task.name === taskToUpdate}); task.specifications_doc = observStrategy.template.tasks[taskToUpdate].specifications_doc; @@ -515,6 +516,7 @@ const ScheduleService = { return schedulingUnit; } catch(error) { console.error(error); + schedulingUnit['isSUUpdated'] = false; return { error: true, message: 'Unable to Update Task Drafts' diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js index b4ca89eb6f0b31d99092b8d978ffb1e0e9c695f3..b2cdb71562603a663bddc1420395566d4a823afb 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js @@ -3,6 +3,15 @@ const UIConstants = { timeline: { types: { NORMAL: "NORMAL", WEEKVIEW:"WEEKVIEW"} }, + httpStatusMessages: { + 400: {severity: 'error', summary: 'Error', sticky: true, detail: 'Error while process request, please contact system admin'}, + 401: {severity: 'error', summary: 'Error', sticky: true, detail: 'Not authenticated, Please retry to login with valid credential'}, + 403: {severity: 'error', summary: 'Error', sticky: true, detail: 'Unknown request, please contact system admin'}, + 404: {severity: 'error', summary: 'Error', sticky: true, detail: 'URL is not recognized, please contact system admin'}, + 408: {severity: 'error', summary: 'Error', sticky: true, detail: 'Request is taking more time to response, please contact system admin'}, + 500: {severity: 'error', summary: 'Error', sticky: true, detail: 'Internal Server Error, URL may not exists, please contact system admin'}, + 503: {severity: 'error', summary: 'Error', sticky: true, detail: 'Server not available, please check system admin'}, + }, CALENDAR_DATE_FORMAT: 'yy-mm-dd', CALENDAR_DATETIME_FORMAT : 'YYYY-MM-DD HH:mm:ss', CALENDAR_TIME_FORMAT: 'HH:mm:ss',