diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js index 8ea8c891bfb85fc1baa0ce409be0069be339257c..9a016cc545728c252f21233473f88ce90897b55d 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ViewTable.js @@ -5,7 +5,7 @@ import _, { filter } from 'lodash'; import moment from 'moment'; import { useHistory } from "react-router-dom"; import { OverlayPanel } from 'primereact/overlaypanel'; -//import {InputSwitch} from 'primereact/inputswitch'; +import { InputMask } from 'primereact/inputmask'; import { InputText } from 'primereact/inputtext'; import { Calendar } from 'primereact/calendar'; import { Paginator } from 'primereact/paginator'; @@ -18,12 +18,18 @@ import { MultiSelect } from 'primereact/multiselect'; import { RadioButton } from 'primereact/radiobutton'; import { useExportData } from "react-table-plugins"; import { ProgressBar } from 'primereact/progressbar'; -//import UIConstants from '../utils/ui.constants'; + +import "flatpickr/dist/flatpickr.css"; +import Flatpickr from "react-flatpickr"; +import confirmDatePlugin from "flatpickr/dist/plugins/confirmDate/confirmDate"; +import shortcutButtonsPlugin from "shortcut-buttons-flatpickr"; + import UtilService from '../../src/services/util.service' import Papa from "papaparse"; import JsPDF from "jspdf"; import "jspdf-autotable"; import TableUtil from "../utils/table.util"; +import Validator from "../utils/validator" let doServersideFilter = false; let tbldata = [], filteredData = []; @@ -41,6 +47,7 @@ let showCSV = false; let multiSelectOption = {}; let filterCallback = null; let tableOptionsState = null; +let tableToolTipsState = {}; let setLoaderFunction = null; let showFilterOption = null; let hasFilters = false; @@ -49,7 +56,7 @@ let tmpTableData = null; let currentTableName = null; let storeFilter = false; let storage_array = []; - +//let confirmDatePlugin = new confirmDatePlugin(); // Define a default UI for filtering function GlobalFilter({ preGlobalFilteredRows, @@ -109,7 +116,8 @@ function DefaultColumnFilter({ return ( <> <div className="table-filter" onClick={e => { e.stopPropagation() }} style={{marginRight: '5px'}}> - <input title="Enter the exact value to be searched and press 'Enter' key" + <input + title={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter few characters and press ‘Enter’ key to search"} value={value} //***TO REMOVE - INCOMING CHANGE WAS value={filterValue || ''} onChange={e => { setValue(e.target.value); @@ -250,9 +258,9 @@ function SelectColumnFilter({ _.remove(tableOptionsState.filters, function(filter) { return filter.id === Header }); if (isCleared) { hasFilters = false; - if (filtered) { + //if (filtered) { filterCallback(tableOptionsState, setLoaderFunction); - } + //} } else { tableOptionsState.filters.push({id: Header, value: event.target.value}); filterCallback(tableOptionsState, setLoaderFunction); @@ -263,6 +271,7 @@ function SelectColumnFilter({ return ( <div onClick={e => { e.stopPropagation() }}> <select + title={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Select a value from list to search"} className= {columnclassname[0][Header]} style={{ height: '24.2014px', @@ -392,6 +401,7 @@ function MultiSelectColumnFilter({ maxSelectedLabels="1" selectedItemsLabel="{0} Selected" className="multi-select" + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Select one or more value from list to search"} /> </div> </div> @@ -466,6 +476,7 @@ function MultiSelectFilter({ maxSelectedLabels="1" selectedItemsLabel="{0} Selected" className="multi-select" + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Select one or more value from list to search"} /> </div> </div> @@ -509,7 +520,9 @@ function SliderColumnFilter({ if (storeFilter) { TableUtil.saveFilter(currentTableName, Header, e.value); } - }} /> + }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Select range value to search"} + /> </div> ) } @@ -557,17 +570,19 @@ function BooleanColumnFilter({ return ( <div onClick={e => { e.stopPropagation() }}> <TriStateCheckbox value={value} style={{ 'width': '15px', 'height': '24.2014px' }} on - onChange={(e) => { - setValue(e.target.value); - setFilter(e.target.value || undefined); - setFiltered(true); - if (storeFilter) { - TableUtil.saveFilter(currentTableName, Header, e.target.value); - } - if (doServersideFilter) { - callServerFilter(e.value); - } - }} /> + onChange={(e) => { + setValue(e.target.value); + setFilter(e.target.value || undefined); + setFiltered(true); + if (storeFilter) { + TableUtil.saveFilter(currentTableName, Header, e.target.value); + } + if (doServersideFilter) { + callServerFilter(e.value); + } + }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Select checkbox (True/False/None) to search"} + /> </div> ) } @@ -731,6 +746,106 @@ function DateRangeColumnFilter({ </div> ) } + +// This is a custom filter UI that uses a +// flatpickr range calendar to set the value +function FlatpickrRangeColumnFilter({ + column: { setFilter, filterValue, Header }, +}) { + const [value, setValue] = useState(''); + const [filtered, setFiltered] = useState(false); + React.useEffect(() => { + if (!filterValue && value) { + setValue(null); + } + if (storeFilter) { + const filter = TableUtil.getFilter(currentTableName, Header); + if (filter === '') { + TableUtil.saveFilter(currentTableName, Header, [] ); + setFilter(undefined); + } + const filterValue = _.map(filter, date => {return new Date(date)}); + if (filterValue[1] && !value ){ + setValue(filterValue); + setFilter(filterValue); + } + } + }, [filterValue, value]); + // Function to call the server side filtering + const callServerFilter = (value, isCleared) => { + hasFilters = true; + if (isCleared) { + hasFilters = false; + _.remove(tableOptionsState.filters, function(filter) { return filter.id === Header }); + filterCallback(tableOptionsState, setLoaderFunction); + } else { + let filterColumn = _.find(tableOptionsState.filters, {id: Header }); + if(filterColumn) { + filterColumn.value = value; + filterCallback(tableOptionsState, setLoaderFunction); + } else if (!filterColumn && value && value.length === 2) { + // Here the above condition placed because the Start/End time filters is not consistency in tableOptionsState.filters + filterColumn = {id: Header, value: value} + tableOptionsState.filters.push(filterColumn); + filterCallback(tableOptionsState, setLoaderFunction); + } else { + filterCallback(tableOptionsState, setLoaderFunction); + } + } + }; + return ( + <div className="table-filter" onClick={e => { e.stopPropagation() }}> + <Flatpickr data-enable-time data-input + options={{ "inlineHideInput": true, + "wrap": true, + "enableSeconds": true, + "time_24hr": true, + "minuteIncrement": 1, + "allowInput": true, + "mode": "range", + "defaultHour": 0, + "plugins": [new confirmDatePlugin()] + }} + title={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter the date & time range to search and press ‘Ok’ button"} + value={filterValue} + onClose={value => { + if(value.length === 2) { + setValue(value); + setFilter(value); + if(storeFilter) { + TableUtil.saveFilter(currentTableName, Header, value); + } + if ((value !== '' && value !== 'Invalid date' ) && doServersideFilter) { + setFiltered(true); + callServerFilter(value); + } + } + }} + > + <input type="text" data-input className={`p-inputtext p-component calendar-input`} /> + <button class="p-button p-component p-button-icon-only calendar-button" data-toggle + title="Click to select the date range" > + <i class="fas fa-calendar"></i> + </button> + <button class="p-button p-component p-button-icon-only calendar-reset" + onClick={(value) => { + setFilter(undefined); setValue([]); setFiltered(false);filterValue = []; + if(storeFilter){ + TableUtil.saveFilter(currentTableName, Header, [] ); + } + if (doServersideFilter) { + setFilter(undefined); + setValue(''); + callServerFilter(value, true); + } + }} title="Clear date range" > + <i class="fa fa-times" style={{color:'white', marginTop:'-2.85px'}} ></i> + </button> + </Flatpickr> + </div> + ) +} + // This is a custom filter UI that uses a // calendar to set the value function CalendarColumnFilter({ @@ -1133,6 +1248,7 @@ function NumberRangeFilter({ height: '25px' // marginRight: '0.5rem', }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Minimum Range value and press ‘Enter’ key to search"} /> <InputText @@ -1162,38 +1278,163 @@ function NumberRangeFilter({ width: '65px', height: '25px' }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Maximum Range value and press ‘Enter’ key to search"} /> </div> ) } -// This is a custom UI for our 'between' or number range -// filter. It uses two number boxes and filters rows to -// ones that have values between the two -// This is for server side filtering - -/*function NumberRangeFilter({ +// Priority Rank Range Filter +function RankRangeFilter({ column: { filterValue = [], preFilteredRows, setFilter, id, Header }, }) { let [rangeValue, setRangeValue] = useState([0,0]); - let [value, setValue] = useState(''); + const [value, setValue] = useState(''); const [filtered, setFiltered] = useState(false); - const [errorProps, setErrorProps] = useState({}); - const [maxErr, setMaxErr] = useState(false); - const [min, max] = React.useMemo(() => { - if(!preFilteredRows) { - preFilteredRows = []; + React.useEffect(() => { + if (!filterValue && value) { + setValue(''); + //setRangeValue([]) } - let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0 - let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0 - preFilteredRows.forEach(row => { - min = Math.min(row.values[id], min) - max = Math.max(row.values[id], max) - }) - //setRangeValue([min, max]); - return [min, max] - }, [id, preFilteredRows]) + if (storeFilter) { + const filterValue = TableUtil.getFilter(currentTableName, Header); + if (filterValue) { + setFiltered(true); + } + if(!value){ + setValue(filterValue); + //setFilter(filterValue); + } + } + }, [filterValue, value]); + + // Function to call the server side filtering + const callServerFilter = (event, isCleared) => { + hasFilters = true; + _.remove(tableOptionsState.filters, function(filter) { return filter.id === Header }); + if (isCleared) { + hasFilters = false; + if (filtered) { + filterCallback(tableOptionsState, setLoaderFunction); + } + } else { + tableOptionsState.filters.push({id: Header, value: rangeValue}); + filterCallback(tableOptionsState, setLoaderFunction); + } + }; + return ( + <div + style={{ + alignItems: 'center' + }} + > + <input type="decimal" + title={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Minimum Range value and press ‘Enter’ key to search"} + max="1" + min="0" + className="p-inputtext p-component" + value={value[0]} + step="0.0001" + onKeyUp={(e) => { + if (e.key === "Enter" && doServersideFilter) { + TableUtil.saveFilter(currentTableName, Header, rangeValue); + setFiltered(true); + callServerFilter(e, false); + } + }} + onChange={e => { + setFilter(undefined); setFiltered(false); + let val = e.target.value; + val = val.replace(/([^0-9.]+)/, ""); + const match = /(\d{0,1})[^.]*((?:\.\d{0,4})?)/g.exec(val); + val = match[1] + match[2]; + if (val == '' || (val >= 0 && val <= 1)) { + let max = rangeValue[1]; + setValue([val,max]); + setFilter([val,max] || undefined); + setRangeValue([val,max]); + filterValue[0] = val; + if(storeFilter) { + //TableUtil.saveFilter(currentTableName, Header, [val,max]); + setFilter([val,max]); + } + } else { + e.target.value = rangeValue[0]; + } + }} + style={{ + width: '75px', + height: '25px' + }} + /> + + <input type="decimal" + title={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Maximum Range value and press ‘Enter’ key to search"} + max="1" + min="0" + className="p-inputtext p-component" + value={value[1]} + step="0.0001" + onKeyUp={(e) => { + if (e.key === "Enter" && doServersideFilter) { + TableUtil.saveFilter(currentTableName, Header, rangeValue); + setFiltered(true); + callServerFilter(e, false); + } + }} + onChange={e => { + setFilter(undefined); setFiltered(false); + let val = e.target.value; + val = val.replace(/([^0-9.]+)/, ""); + const match = /(\d{0,1})[^.]*((?:\.\d{0,4})?)/g.exec(val); + val = match[1] + match[2]; + if (val == '' || (val >= 0 && val <= 1)) { + let min = rangeValue[0]; + setRangeValue([min,val]); + filterValue[1] = val; + setValue([min,val]); + setFilter([min,val] || undefined); + if(storeFilter) { + //TableUtil.saveFilter(currentTableName, Header, [min,val]); + setFilter([min,val]); + } + } else { + e.target.value = rangeValue[0]; + } + }} + style={{ + width: '75px', + height: '25px' + }} + /> + </div> + ) +} + +// Duration Range Filter +function DurationRangeFilter({ + column: { filterValue = [], preFilteredRows, setFilter, id, Header }, +}) { + let [rangeValue, setRangeValue] = useState([0,0]); + const [value, setValue] = useState(''); + const [filtered, setFiltered] = useState(false); + React.useEffect(() => { + if (!filterValue && value) { + setValue(''); + } + if (storeFilter) { + const filterValue = TableUtil.getFilter(currentTableName, Header); + if (filterValue) { + setFiltered(true); + } + if(!value){ + setValue(filterValue); + //setFilter(filterValue); + } + } + }, [filterValue, value]); + // Function to call the server side filtering const callServerFilter = (event, isCleared) => { hasFilters = true; @@ -1211,81 +1452,77 @@ function NumberRangeFilter({ return ( <div + onKeyPress={(e) => { + if (e.key === "Enter" && doServersideFilter) { + TableUtil.saveFilter(currentTableName, Header, rangeValue); + setFiltered(true); + callServerFilter(e, false); + } + }} style={{ - // display: 'flex', - // flexdirection:'column', alignItems: 'center' }} > - <InputText - value={filterValue[0]} - type="number" - onKeyUp={(e) => { - if (e.key === "Enter" && doServersideFilter) { - TableUtil.saveFilter(currentTableName, Header, rangeValue); - setFiltered(true); - callServerFilter(e, false); - } - }} - onChange={e => { - setFilter(undefined); setValue(''); setFiltered(false); - const val = e.target.value; - let max = rangeValue[1]; - setRangeValue([val,max]); - filterValue[0] = val; - //setFilter((old = []) => [val ? parseFloat(val, 10) : undefined, old[1]]); - if(storeFilter) { - TableUtil.saveFilter(currentTableName, Header, [val,max]); + <InputMask mask="99:99:99" + value={value[0]} + placeholder="HH:mm:ss" + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Minimum Range value in HH:mm:ss format and press ‘Enter’ key to search"} + onChange={e => { + setFilter(undefined); setFiltered(false); + let val = e.target.value; + if (val.includes(":") && !Validator.isValidHHmmss(val, true)) { + val = rangeValue[0]; + } + let max = rangeValue[1]; setValue([val,max]); - setFilter([val,max]); - } - }} - placeholder={min} - style={{ - width: '55px', - height: '25px' - // marginRight: '0.5rem', - }} + setFilter([val,max] || undefined); + setRangeValue([val,max]); + filterValue[0] = val; + if(storeFilter) { + //TableUtil.saveFilter(currentTableName, Header, [val,max]); + setFilter([val,max]); + } + }} + style={{ + width: '85px', + height: '25px' + }} /> - <InputText - value={filterValue[1]} - type="number" - {...errorProps} - className={maxErr && 'field-error'} - onKeyUp={(e) => { - if (e.key === "Enter" && doServersideFilter) { - setFiltered(true); - callServerFilter(e, false); - } - }} - onChange={e => { - const val = e.target.value; - let min = rangeValue[0]; - setRangeValue([min,val]); - filterValue[1] = val; - if(storeFilter) { - TableUtil.saveFilter(currentTableName, Header, [min,val]); + + <InputMask mask="99:99:99" + value={value[1]} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Maximum Range value in HH:mm:ss format and press ‘Enter’ key to search"} + placeholder="HH:mm:ss" + onChange={e => { + setFilter(undefined); setFiltered(false); + let val = e.target.value; + if (val.includes(":") && !Validator.isValidHHmmss(val, true)) { + val = rangeValue[1]; + } + let min = rangeValue[0]; setValue([min,val]); - setFilter([min,val]); - } - }} - placeholder={max} - style={{ - width: '55px', - height: '25px' - // marginLeft: '0.5rem', - }} - /> + setFilter([min,val] || undefined); + setRangeValue([min,val]); + filterValue[1] = val; + if(storeFilter) { + //TableUtil.saveFilter(currentTableName, Header, [min,val]); + setFilter([min,val]); + } + }} + style={{ + width: '85px', + height: '25px' + }} + /> </div> ) } -*/ // This is a custom UI for our 'between' or number range // filter. It uses two number boxes and filters rows to // ones that have values between the two function NumberRangeColumnFilter({ - column: { filterValue = [], preFilteredRows, setFilter, id }, + column: { filterValue = [], preFilteredRows, setFilter, id, Header }, }) { const [errorProps, setErrorProps] = useState({}); const [maxErr, setMaxErr] = useState(false); @@ -1320,6 +1557,7 @@ function NumberRangeColumnFilter({ height: '25px' // marginRight: '0.5rem', }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Minimum Range value and press ‘Enter’ key to search"} /> <InputText value={filterValue[1] || ''} @@ -1347,6 +1585,7 @@ function NumberRangeColumnFilter({ height: '25px' // marginLeft: '0.5rem', }} + tooltip={(tableToolTipsState[Header])?tableToolTipsState[Header]:"Enter Maximum Range value and press ‘Enter’ key to search"} /> </div> ) @@ -1386,6 +1625,11 @@ const filterTypes = { fn: DateRangeColumnFilter, type: dateRangeFilterFn }, + 'flatpickrDateRange': { + fn: FlatpickrRangeColumnFilter, + type: dateRangeFilterFn + }, + 'fromdatetime': { fn: DateTimeColumnFilter, type: fromDatetimeFilterFn @@ -1405,6 +1649,14 @@ const filterTypes = { 'numberRangeMinMax': { fn: NumberRangeFilter, type: 'between' + }, + 'rankMinMax': { + fn: RankRangeFilter, + type: 'between' + }, + 'durationMinMax': { + fn: DurationRangeFilter, + type: 'between' } }; // Let the table remove the filter if the string is empty @@ -1920,7 +2172,20 @@ function ViewTable(props) { let columns = []; let defaultdataheader = Object.keys(defaultheader[0]); let optionaldataheader = Object.keys(optionalheader[0]); - + + // Get Tooltips for each column if provided and used in filter components + tableToolTipsState = {}; + for(const headerId of defaultdataheader) { + if (defaultheader[0][headerId].tooltip) { + tableToolTipsState[defaultheader[0][headerId].name] = defaultheader[0][headerId].tooltip; + } + } + for(const headerId of optionaldataheader) { + if (optionalheader[0][headerId].tooltip) { + tableToolTipsState[optionalheader[0][headerId].name] = optionalheader[0][headerId].tooltip; + } + } + /* If allowRowSelection property is true for the component, add checkbox column as 1st column. If the record has property to select, enable the checkbox */ if (allowRowSelection) { 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 216c8b6c14a2c468b83f9a134aac3e917dc47ad9..a9309b9ff0800515c754971097430957f3309734 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Task/list.js @@ -28,6 +28,8 @@ export class TaskList extends Component { TASK_END_STATUSES = ['finished', 'error', 'cancelled']; TASK_DRAFT_EXPAND = 'task_blueprints,task_blueprints.subtasks,scheduling_unit_draft'; TASK_BLUEPRINT_EXPAND = 'subtasks,subtasks.output_dataproducts,draft,scheduling_unit_blueprint'; + COMMA_SEPARATE_TOOLTIP ='Enter Id and press ‘Enter’ key to search. For multiple values enter comma separated values. For range, provide input like 1..6'; + constructor(props) { super(props); this.getUIAttr(); @@ -76,18 +78,22 @@ export class TaskList extends Component { filter: "select" }, scheduling_unit_draft: { - name:"Scheduling Unit ID" + name:"Scheduling Unit ID", + tooltip: this.COMMA_SEPARATE_TOOLTIP }, scheduling_unit_blueprint: { - name:"Scheduling Unit ID" + name:"Scheduling Unit ID", + tooltip: this.COMMA_SEPARATE_TOOLTIP }, schedulingUnitName: { name:"Scheduling Unit Name"}, id: { - name:"ID" + name:"ID", + tooltip: this.COMMA_SEPARATE_TOOLTIP }, subTaskID: { - name:"Control ID" + name:"Control ID", + tooltip: this.COMMA_SEPARATE_TOOLTIP }, name: { name:"Name" @@ -97,25 +103,25 @@ export class TaskList extends Component { }, start_time: { name: "Start Time", - filter: "dateRange", + filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, stop_time: { name: "End Time", - filter: "dateRange", + filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, duration: { name:"Duration (HH:mm:ss)", - filter: "numberRangeMinMax", + filter: "durationMinMax", }, relative_start_time: { name:"Relative Start Time (HH:mm:ss)", - filter: "numberRangeMinMax", + filter: "durationMinMax", }, relative_stop_time: { name:"Relative End Time (HH:mm:ss)", - filter: "numberRangeMinMax", + filter: "durationMinMax", }, noOfOutputProducts: { name:"#Dataproducts" @@ -139,19 +145,20 @@ export class TaskList extends Component { name:"Tags" }, draft: { - name:"Linked Draft ID" + name:"Linked Draft ID", + tooltip: this.COMMA_SEPARATE_TOOLTIP }, url: { name:"API URL" }, created_at: { name: "Created at", - filter: "dateRange", + filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, updated_at: { name: "Updated at", - filter: "dateRange", + filter: "flatpickrDateRange", format: UIConstants.CALENDAR_DATETIME_FORMAT }, actionpath: "actionpath" @@ -166,7 +173,7 @@ export class TaskList extends Component { "Cancelled": "filter-input-50", "Duration (HH:mm:ss)": "filter-input-75", "Template ID": "filter-input-50", - "Linked Draft ID": "filter-input-50", + "Linked Draft ID": "filter-input-75", "Relative Start Time (HH:mm:ss)": "filter-input-75", "Relative End Time (HH:mm:ss)": "filter-input-75", "Start Time": "filter-input-150", @@ -176,11 +183,14 @@ export class TaskList extends Component { "Data size": "filter-input-50", "Data size on Disk": "filter-input-50", "Subtask Content": "filter-input-75", - "Linked BluePrint ID": "filter-input-50", - "API URL":"filter-input-175" + "Linked BluePrint ID": "filter-input-75", + "API URL":"filter-input-175", + "Created at":"filter-input-150", + "Updated at":"filter-input-150", }], actions: [] }; + this.statusList = []; this.access_denied_message = "Don't have permission"; this.pageUpdated = true; this.taskTypeList = [{name: 'Blueprint'}, {name: 'Draft'}]; @@ -203,6 +213,7 @@ export class TaskList extends Component { this.getTaskCancelStatusContent = this.getTaskCancelStatusContent.bind(this); this.changeTaskType = this.changeTaskType.bind(this); this.fetchTableData = this.fetchTableData.bind(this); + this.getFilterOptions = this.getFilterOptions.bind(this); } subtaskComponent = (task) => { @@ -620,12 +631,25 @@ export class TaskList extends Component { tmpDefaulColumns = _.omit(tmpDefaulColumns,columnDefinitionToRemove); tmpOptionalColumns = _.omit(tmpOptionalColumns,columnDefinitionToRemove); if(taskFilters) { + this.getStatusList(taskFilters); tmpDefaulColumns = this.getAPIFilter(taskFilters, tmpDefaulColumns); tmpOptionalColumns = this.getAPIFilter(taskFilters, tmpOptionalColumns); await this.setState({tmpDefaulcolumns: [tmpDefaulColumns], tmpOptionalcolumns:[tmpOptionalColumns], tmpColumnOrders: tmpColumnOrders, columnMap: this.columnMap}) } } + /** + * Get Status list frol filter + * @param {Array} suFilters + */ + getStatusList(taskFilters) { + if (taskFilters.data.filters['status']) { + taskFilters.data.filters['status'].choices.forEach(choice => { + this.statusList.push(choice.value); + }) + } + } + getAPIFilter(taskFilters, columnDef) { const defaultColKeys = Object.keys(columnDef); defaultColKeys.forEach(key => { @@ -775,35 +799,18 @@ export class TaskList extends Component { } else { this.filterQry += 'scheduling_unit_blueprint_name='+filter.value+'&'; } - } else if (filter.id === 'Duration (HH:mm:ss)' && filter.value != '') { - let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); - const values = _.split(filter.value, ","); - if (values[0] && !isNaN(values[0]) && values[0]>0) { - this.filterQry += columnDetails.orgField+"_min" +'=PT'+parseFloat(values[0])+'S&'; - } - if (values[1] && !isNaN(values[1]) && values[1]>0) { - this.filterQry += columnDetails.orgField+"_max" +'=PT'+parseFloat(values[1])+'S&'; - } - } else if (filter.id === 'Relative Start Time (HH:mm:ss)' && filter.value != '') { + } else if ((filter.id === 'Duration (HH:mm:ss)' || filter.id === 'Relative Start Time (HH:mm:ss)' || filter.id === 'Relative End Time (HH:mm:ss)') && filter.value != '') { let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); const values = _.split(filter.value, ","); - if (values[0] && !isNaN(values[0]) && values[0]>0) { - this.filterQry += columnDetails.orgField+"_min" +'=PT'+parseFloat(values[0])+'S&'; + if (values[0].includes(":")) { + this.filterQry += columnDetails.orgField+"_min" +'=PT'+UnitConverter.getHHmmssToSecs(values[0])+'S&'; } - if (values[1] && !isNaN(values[1]) && values[1]>0) { - this.filterQry += columnDetails.orgField+"_max" +'=PT'+parseFloat(values[1])+'S&'; - } - } else if (filter.id === 'Relative End Time (HH:mm:ss)' && filter.value != '') { - let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); - const values = _.split(filter.value, ","); - if (values[0] && !isNaN(values[0]) && values[0]>0) { - this.filterQry += columnDetails.orgField+"_min" +'=PT'+parseFloat(values[0])+'S&'; - } - if (values[1] && !isNaN(values[1]) && values[1]>0) { - this.filterQry += columnDetails.orgField+"_max" +'=PT'+parseFloat(values[1])+'S&'; + if (values[1].includes(":")) { + this.filterQry += columnDetails.orgField+"_max" +'=PT'+UnitConverter.getHHmmssToSecs(values[1])+'S&'; } } else if (filter.id === 'Linked Blueprint ID' && filter.value != '') { - _.split(filter.value, ",").forEach(value =>{ + const values = UnitConverter.getSubbandOutput(filter.value) + _.split(values, ",").forEach(value =>{ if(value && _.trim(value) !== '') { this.filterQry += 'task_blueprints='+_.trim(value)+'&'; } @@ -819,11 +826,16 @@ export class TaskList extends Component { this.filterQry += columnDetails.orgField+'_before='+moment(new Date(values[1])).format("YYYY-MM-DDT23:59:59")+".000Z&"; } } else if ((filter.id === 'Control ID') && filter.value != '') { - _.split(filter.value, ",").forEach(value =>{ + const values = UnitConverter.getSubbandOutput(filter.value); + _.split(values, ",").forEach(value =>{ if(value && _.trim(value) !== '') { this.filterQry += 'subtasks='+_.trim(value)+'&'; } }); + } else if ((filter.id === 'Scheduling Unit ID' || filter.id === 'Linked Draft ID'|| filter.id === 'ID') && filter.value != '') { + let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); + const values = UnitConverter.getSubbandOutput(filter.value); + this.filterQry += columnDetails.orgField+'='+values.toString()+"&"; } else { let columnDetails = _.find(this.state.columnMap, {displayName:filter.id}); if(columnDetails) { @@ -846,6 +858,19 @@ export class TaskList extends Component { return [this.state.tasks, this.totalPage]; } + /** + * Get Option-list values for Select Dropdown filter in 'Viewtable' + * @param {String} id : Column id + * @returns + */ + getFilterOptions(id) { + let options = null; + if(id && id === 'Status') { + options = this.statusList; + } + return options; + } + render() { if (this.state.redirect) { return <Redirect to={{ pathname: this.state.redirect }}></Redirect> @@ -884,6 +909,7 @@ export class TaskList extends Component { columnclassname={this.state.columnclassname} columnOrders={this.state.tmpColumnOrders} defaultSortColumn={this.defaultSortColumn} + showFilterOption={this.getFilterOptions} //Callback function to provide inputs for option-list in Select Dropdown filter showaction="true" keyaccessor="id" paths={this.state.paths} 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 649d3d069c973b2d3fd8edc3a172ca57e653fa7c..d89aadf99a448db85546f15d0ffec2b6ec8272b0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/ui.constants.js @@ -29,6 +29,7 @@ const UIConstants = { 'PropertyIsoDateTimeFromToRangeFilter':'', }, SU_STATUS:['cancelled', 'error', 'defining', 'defined', 'schedulable', 'scheduled','started', 'observing', 'observed', 'processing', 'processed', 'ingesting','finished', 'unschedulable'], - CURRENT_WORKFLOW_STAGE:['Waiting To Be Scheduled','Scheduled','QA Reporting (TO)', 'QA Reporting (SDCO)', 'PI Verification', 'Decide Acceptance','Ingesting','Unpin Data','Done'] + CURRENT_WORKFLOW_STAGE:['Waiting To Be Scheduled','Scheduled','QA Reporting (TO)', 'QA Reporting (SDCO)', 'PI Verification', 'Decide Acceptance','Ingesting','Unpin Data','Done'], + SU_PRIORITY_QUEUE:['A','B'], } export default UIConstants; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js index 1f135a9fd6fd152fab1765bb0909f1886c72a260..ce11a724f002d32963ecf3e1a2decd2e7b4b7582 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js @@ -236,7 +236,34 @@ const UnitConverter = { radian = this.convertToRadians((degreeHour * 15 + minute / 4 + second / 240)); } return radian; - } + }, + /** + * Convert the string input for subband list to Array + * @param {String} prpOutput + */ + getSubbandOutput(prpOutput) { + const subbandArray = prpOutput ? prpOutput.split(",") : []; + let subbandList = []; + for (const subband of subbandArray) { + const subbandRange = subband.split('..'); + if (subbandRange.length > 1) { + subbandList = subbandList.concat(_.range(subbandRange[0], (parseInt(subbandRange[1]) + 1))); + } else if (subbandRange[0] !== ''){ + subbandList = subbandList.concat(parseInt(subbandRange[0])); + } + } + prpOutput = subbandList; + return prpOutput; + }, + getStatusList(suFilters) { + let statusList = []; + if (suFilters.data.filters['status']) { + suFilters.data.filters['status'].choices.forEach(choice => { + statusList.push(choice.value); + }); + } + return statusList; + } }; export default UnitConverter; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js index 050639115a1bea70a502e340482f360f27134b48..4198a7ea71537b9719213bb0e75a5111c1043ea5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/validator.js @@ -46,6 +46,28 @@ const Validator = { } } return isModified; + }, + /** + * Validate time + * @param {string value with HH:mm:ss format} time + * @returns + */ + isValidHHmmss(time, excludeHour) { + let isValid = true; + if (time && time.includes(':')) { + time = time.replaceAll("_", "0"); + if(excludeHour) { + var timeFormat = /^([0-9]|[0-9][0-9]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$/; + isValid = timeFormat.test(time); + } else { + var timeFormat = /^([0-9]|[0-2][0-3]):([0-9]|[0-5][0-9]):([0-9]|[0-5][0-9])$/; + isValid = timeFormat.test(time); + } + + } else { + isValid = false; + } + return isValid; } };