Skip to content
Snippets Groups Projects
Commit 53d25bb0 authored by Auke Klazema's avatar Auke Klazema
Browse files

Merge branch 'TMSS-438' into 'master'

Sprint-16 Front End Demo Merge

See merge request !294
parents 9a9c217c c2b4e8f2
No related branches found
No related tags found
1 merge request!294Sprint-16 Front End Demo Merge
Showing
with 1003 additions and 140 deletions
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
\ No newline at end of file
......@@ -22,6 +22,8 @@
"font-awesome": "^4.7.0",
"history": "^5.0.0",
"interactjs": "^1.9.22",
"js-cookie": "^2.2.1",
"katex": "^0.12.0",
"lodash": "^4.17.19",
"match-sorter": "^4.1.0",
"moment": "^2.27.0",
......@@ -57,7 +59,7 @@
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": "http://127.0.0.1:8008/",
"proxy": "http://localhost:8008/",
"eslintConfig": {
"extends": "react-app"
},
......
import React, {Component} from 'react';
import { Redirect} from 'react-router-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import classNames from 'classnames';
import {AppTopbar} from './layout/components/AppTopbar';
......@@ -15,40 +16,41 @@ import './layout/layout.scss';
import 'primeflex/primeflex.css';
import './App.scss';
import './App.css';
import Auth from'./authenticate/auth';
import { Login } from './authenticate/login';
class App extends Component {
constructor() {
super();
this.state = {
layoutMode: 'static',
currentMenu: '',
currentPath: '/',
PageTitle:'',
staticMenuInactive: localStorage.getItem('staticMenuInactive') === 'true' ? true : false,
overlayMenuActive: localStorage.getItem('overlayMenuActive') === 'true' ? true : false,
mobileMenuActive: localStorage.getItem('mobileMenuActive') === 'true' ? true : false,
};
this.onWrapperClick = this.onWrapperClick.bind(this);
super();
this.state = {
layoutMode: 'static',
currentMenu: '',
currentPath: '/',
PageTitle:'',
staticMenuInactive: localStorage.getItem('staticMenuInactive') === 'true' ? true : false,
overlayMenuActive: localStorage.getItem('overlayMenuActive') === 'true' ? true : false,
mobileMenuActive: localStorage.getItem('mobileMenuActive') === 'true' ? true : false,
authenticated: Auth.isAuthenticated(),
redirect: (Auth.isAuthenticated() && window.location.pathname === "/login")?"/":window.location.pathname
};
this.onWrapperClick = this.onWrapperClick.bind(this);
this.onToggleMenu = this.onToggleMenu.bind(this);
this.onSidebarClick = this.onSidebarClick.bind(this);
this.onMenuItemClick = this.onMenuItemClick.bind(this);
this.setPageTitle = this.setPageTitle.bind(this);
this.menu = [
{label: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard',section: 'dashboard'},
{label: 'Cycle', icon:'pi pi-fw pi-spinner', to:'/cycle',section: 'cycle'},
{label: 'Project', icon: 'fab fa-fw fa-wpexplorer', to:'/project',section: 'project'},
{label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to:'/schedulingunit',section: 'schedulingunit'},
{label: 'Timeline', icon: 'pi pi-fw pi-clock', to:'/su/timelineview',section: 'su/timelineview'},
// {label: 'Tasks', icon: 'pi pi-fw pi-check-square', to:'/task'},
];
this.loggedIn = this.loggedIn.bind(this);
this.logout = this.logout.bind(this);
// this.menuComponent = {'Dashboard': Dashboard}
}
onWrapperClick(event) {
this.menu = [ {label: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard',section: 'dashboard'},
{label: 'Cycle', icon:'pi pi-fw pi-spinner', to:'/cycle',section: 'cycle'},
{label: 'Project', icon: 'fab fa-fw fa-wpexplorer', to:'/project',section: 'project'},
{label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to:'/schedulingunit',section: 'schedulingunit'},
{label: 'Timeline', icon: 'pi pi-fw pi-clock', to:'/su/timelineview',section: 'su/timelineview'},
// {label: 'Tasks', icon: 'pi pi-fw pi-check-square', to:'/task'},
];
}
onWrapperClick(event) {
if (!this.menuClick) {
this.setState({
overlayMenuActive: false,
......@@ -94,9 +96,9 @@ class App extends Component {
this.menuClick = true;
}
onMenuItemClick(event) {
this.setState({currentMenu:event.item.label, currentPath: event.item.path});
}
onMenuItemClick(event) {
this.setState({currentMenu:event.item.label, currentPath: event.item.path});
}
isDesktop() {
return window.innerWidth > 1024;
......@@ -107,36 +109,72 @@ class App extends Component {
this.setState({ PageTitle })
}
}
render() {
const wrapperClass = classNames('layout-wrapper', {
'layout-overlay': this.state.layoutMode === 'overlay',
'layout-static': this.state.layoutMode === 'static',
'layout-static-sidebar-inactive': this.state.staticMenuInactive && this.state.layoutMode === 'static',
'layout-overlay-sidebar-active': this.state.overlayMenuActive && this.state.layoutMode === 'overlay',
'layout-mobile-sidebar-active': this.state.mobileMenuActive
});
const AppBreadCrumbWithRouter = withRouter(AppBreadcrumb);
return (
<React.Fragment>
<div className="App">
{/* <div className={wrapperClass} onClick={this.onWrapperClick}> */}
<div className={wrapperClass}>
<AppTopbar onToggleMenu={this.onToggleMenu}></AppTopbar>
<Router basename={ this.state.currentPath }>
<AppMenu model={this.menu} onMenuItemClick={this.onMenuItemClick} layoutMode={this.state.la} active={this.state.menuActive}/>
<div className="layout-main">
<AppBreadCrumbWithRouter setPageTitle={this.setPageTitle} />
<RoutedContent />
</div>
</Router>
<AppFooter></AppFooter>
</div>
/**
* Callback function from login page to set the authentication state to true amd redirect to the
* original requested URL.
*/
loggedIn() {
const redirect = this.state.redirect;
this.setState({authenticated: true, redirect: redirect==="/login"?"/":redirect});
}
/**
* Logout and redirect to login page.
*/
logout() {
Auth.logout();
this.setState({authenticated: false, redirect:"/"});
}
render() {
const wrapperClass = classNames('layout-wrapper', {
'layout-overlay': this.state.layoutMode === 'overlay',
'layout-static': this.state.layoutMode === 'static',
'layout-static-sidebar-inactive': this.state.staticMenuInactive && this.state.layoutMode === 'static',
'layout-overlay-sidebar-active': this.state.overlayMenuActive && this.state.layoutMode === 'overlay',
'layout-mobile-sidebar-active': this.state.mobileMenuActive
});
const AppBreadCrumbWithRouter = withRouter(AppBreadcrumb);
console.log(this.props);
return (
<React.Fragment>
<div className="App">
{/* <div className={wrapperClass} onClick={this.onWrapperClick}> */}
<div className={wrapperClass}>
{/* Load main routes and application only if the application is authenticated */}
{this.state.authenticated &&
<>
<AppTopbar onToggleMenu={this.onToggleMenu} isLoggedIn={this.state.authenticated} onLogout={this.logout}></AppTopbar>
<Router basename={ this.state.currentPath }>
<AppMenu model={this.menu} onMenuItemClick={this.onMenuItemClick} layoutMode={this.state.la} active={this.state.menuActive}/>
<div className="layout-main">
{this.state.redirect &&
<Redirect to={{pathname: this.state.redirect}} />}
<AppBreadCrumbWithRouter setPageTitle={this.setPageTitle} />
<RoutedContent />
</div>
</Router>
<AppFooter></AppFooter>
</>
}
{/* If not authenticated, show only login page */}
{!this.state.authenticated &&
<>
<Router basename={ this.state.currentPath }>
<Redirect to={{pathname: "/login"}} />
<Login onLogin={this.loggedIn} />
</Router>
</>
}
</div>
</div>
</React.Fragment>
);
}
</React.Fragment>
);
}
}
export default App;
// import AuthService from "../services/auth.service";
/**
* Global functions to authenticate user and get user details from browser local storage.
*/
const Auth = {
/** To check if user already logged in and the token is available in the browser local storage */
isAuthenticated: () => {
let user = localStorage.getItem("user");
if (user) {
user = JSON.parse(user);
if (user.token) {
return true;
}
}
return false;
},
/** Gets user details from browser local storage */
getUser: () => {
return JSON.parse(localStorage.getItem("user"));
},
/** Authenticate user from the backend and store user details in local storage */
login: async(user, pass) => {
// const user = await AuthService.authenticate();
if (user) {
//TODO set token and username
}
localStorage.setItem("user", JSON.stringify({name:user, token: "ABCDEFGHIJ"}));
return true;
},
/** Remove user details from localstorage on logout */
logout: () => {
localStorage.removeItem("user");
}
}
export default Auth;
\ No newline at end of file
import { InputText } from 'primereact/inputtext';
import React, {Component} from 'react';
import { Redirect } from 'react-router-dom';
import Auth from '../authenticate/auth';
/**
* Component to authenticate users in the application.
*/
export class Login extends Component {
constructor(props){
super(props);
this.state = {
username: null,
password: null,
redirect: Auth.isAuthenticated()?"/":null, //If already logged in, redirect to home page
error: null,
errors: {},
validFields: {}
};
this.login = this.login.bind(this);
this.setCredentials = this.setCredentials.bind(this);
}
/**
* To set form field values.
* @param {String} field
* @param {String} value
*/
setCredentials(field, value) {
let state = this.state;
let errors = state.errors;
let validFields = state.validFields;
// If value is null or empty set error field and remove from valid field and vice versa
if (!value) {
errors[field] = `${field} required`;
delete validFields[field];
} else {
delete errors[field];
validFields[field] = field;
}
state[field] = value;
state.errors = errors;
state.validFields = validFields;
this.setState(state);
}
/**
* Login function called on click of 'Login' button.
* If authenticated, callback parent component function.
*/
async login() {
const loggedIn = await Auth.login(this.state.username, this.state.password);
if (loggedIn) {
this.setState({error: false});
this.props.onLogin();
} else {
this.setState({error: true});
}
}
render() {
if (this.state.redirect) {
return (<Redirect to={{pathname: this.state.redirect}} />);
}
return (
<>
<div className="container-fluid bg-login">
<div className="container">
<div className="row">
<div className="col-lg-9 col-md-12 login-card">
<div className="row">
{/* Left panel with image and TMSS title */}
<div className="col-md-5 detail-part">
<h3>Telescope Manager Specification System</h3>
<p>By ASTRON</p>
</div>
{/* Right panel with login form */}
<div className="col-md-7 logn-part">
<div className="row">
<div className="col-lg-10 col-md-12 mx-auto">
<div className="logo-cover">
{/* <img src="./logo.png" alt="" /> */}
</div>
<div className="login-form">
<h4>Login</h4>
<div className="form-field">
<span className="p-float-label">
<InputText id="inputtext" className={`${this.state.errors.username?"input-error ":""} form-control`}
value={this.state.username} onChange={(e) => this.setCredentials('username', e.target.value)} />
<label htmlFor="inputtext"><i className="fa fa-user"></i>Enter Username</label>
</span>
<label className={this.state.errors.username?"error":""}>
{this.state.errors.username?this.state.errors.username : ""}
</label>
</div>
<div className="form-field">
<span className="p-float-label">
<InputText id="inputtext" className={`${this.state.errors.password?"input-error ":""} form-control`}
type="password" value={this.state.password} onChange={(e) => this.setCredentials('password', e.target.value )} />
<label htmlFor="inputtext"><i className="fa fa-key"></i>Enter Password</label>
</span>
<label className={this.state.errors.password?"error":""}>
{this.state.errors.password?this.state.errors.password : ""}
</label>
</div>
<div className="row form-footer">
<div className="col-md-6 forget-paswd">
</div>
<div className="col-md-6 button-div">
<button className="btn btn-primary"
disabled={Object.keys(this.state.validFields).length<2}
onClick={this.login}>Login</button>
</div>
</div>
{this.state.error &&
<div className="row error">
Unable to login, please try with different Username and/or Password.
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}
}
\ No newline at end of file
import React, { Component } from 'react';
import {Calendar} from 'primereact/calendar';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import moment from 'moment';
import _ from 'lodash';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export default class BetweenEditor extends Component {
constructor(props) {
super(props);
this.tmpRowData = [];
this.state = {
showDialog: false,
dialogTitle: '',
};
this.copyDateValue = this.copyDateValue.bind(this);
}
isPopup() {
return true;
}
/**
* Init the date value if exists
*/
async componentDidMount(){
let parentRows = this.props.agGridReact.props.rowData[this.props.node.rowIndex];
let parentCellData = parentRows[this.props.colDef.field];
this.tmpRowData = [];
if(parentCellData){
let cellDataArray = _.split(parentCellData, '|');
await cellDataArray.forEach(dataVal =>{
let line = {};
let dataValue = _.split(dataVal, ',');
line['from'] = (dataValue[0])? moment(dataValue[0]).toDate():'';
line['until'] = ( dataValue[1])? moment(dataValue[1]).toDate():'';
this.tmpRowData.push(line);
});
}
if(this.tmpRowData.length>0){
let row = this.tmpRowData[this.tmpRowData.length-1];
if((row['from'] !== '' && row['from'] !== 'undefined') && (row['until'] !== '' && row['until'] !== 'undefined')){
let line = {'from': '', 'until': ''};
this.tmpRowData.push(line);
}
}else{
let line = {'from': '', 'until': ''};
this.tmpRowData.push(line);
}
await this.setState({
rowData: this.tmpRowData,
dialogTitle: (this.props.colDef.field === 'between') ? this.props.colDef.field : 'Not-Between',
showDialog: true,
});
}
/*isCancelAfterEnd(){console.log('after')
console.log('called')
this.copyDateValue();
}*/
/**
* Call the function on click Esc or Close the dialog
*/
async copyDateValue(){
let consolidateDates = '';
this.state.rowData.map(row =>{
if((row['from'] !== '' && row['from'] !== 'undefined') && (row['until'] !== '' && row['until'] !== 'undefined')){
consolidateDates += ((row['from'] !== '')?moment(row['from']).format(DATE_TIME_FORMAT):'' )+","+((row['until'] !== '')?moment(row['until']).format(DATE_TIME_FORMAT):'')+"|";
}
});
await this.props.context.componentParent.updateTime(
this.props.node.rowIndex,this.props.colDef.field, consolidateDates
);
this.setState({ showDialog: false});
}
/*
Set value in relevant field
*/
updateDateChanges(rowIndex, field, e){
let tmpRows = this.state.rowData;
let row = tmpRows[rowIndex];
row[field] = e.value;
tmpRows[rowIndex] = row;
if(this.state.rowData.length === rowIndex+1){
let line = {'from': '', 'until': ''};
tmpRows.push(line);
}
this.setState({
rowData: tmpRows
})
}
/*
Remove the the row from dialog
*/
removeInput(rowIndex){
let tmpRows = this.state.rowData;
delete tmpRows[rowIndex];
this.setState({
rowData: tmpRows
})
}
render() {
return (
<>
{this.state.rowData && this.state.rowData.length > 0 &&
<Dialog header={_.startCase(this.state.dialogTitle)} visible={this.state.showDialog} maximized={false}
onHide={() => {this.copyDateValue()}} inputId="confirm_dialog"
footer={<div>
<Button key="back" label="Close" onClick={() => {this.copyDateValue()}} />
</div>
} >
<div className="ag-theme-balham" style={{ height: '500px', width: '600px', paddingLeft: '20px' }}>
<div className="p-field p-grid" >
<React.Fragment>
<label key={'labelFrom'} className="col-lg-6 col-md-6 col-sm-12">From</label>
<label key={'labelUntil'} className="col-lg-4 col-md-5 col-sm-12">Until</label>
<label key={'labelRemove'} className="col-lg-2 col-md-2 col-sm-12">Remove</label>
</React.Fragment>
</div>
{this.state.rowData.map((bdate, index) => (
<React.Fragment key={index}>
<div className="p-field p-grid" >
<Calendar
d dateFormat="dd-M-yy"
value= {this.state.rowData[index].from}
onChange= {e => {this.updateDateChanges(index, 'from', e)}}
// onBlur= {e => {this.updateDateChanges(index, 'from', e)}}
showTime={true}
showSeconds={true}
hourFormat="24"
showIcon={true}
/>
<Calendar
d dateFormat="dd-M-yy"
value= {this.state.rowData[index].until}
onChange= {e => {this.updateDateChanges(index, 'until', e)}}
// onBlur= {e => {this.updateDateChanges(index, 'until', e)}}
showTime={true}
showSeconds={true}
hourFormat="24"
showIcon={true}
style={{marginLeft:'60px'}}
/>
{this.state.rowData.length !== (index+1) &&
<button className="p-link" style={{marginLeft: '6vw'}} onClick={(e) => this.removeInput(index)} >
<i className="fa fa-trash pi-error"></i></button>
}
</div>
</React.Fragment>
))}
</div>
</Dialog>
}
</>
);
}
}
\ No newline at end of file
import React, { Component } from 'react';
export default class BetweenRenderer extends Component {
constructor(props) {
super(props);
}
/**
Show cell value in grid
*/
render() {
let row = this.props.agGridReact.props.rowData[this.props.node.rowIndex];
let value = row[this.props.colDef.field];
return <> {value &&
value
}</>;
}
}
\ No newline at end of file
import React, { Component } from 'react';
import {Calendar} from 'primereact/calendar';
import moment from 'moment';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export default class CustomDateComp extends Component {
constructor(props) {
super(props);
this.state = {
date: '',
};
}
componentDidMount(){
let parentRows = this.props.agGridReact.props.rowData[this.props.node.rowIndex];
let parentCellData = parentRows[this.props.colDef.field];
this.setState({
date:parentCellData
})
}
isPopup() {
return true;
}
isCancelAfterEnd(){
let date = (this.state.date !== '' && this.state.date !== 'undefined')? moment(this.state.date).format(DATE_TIME_FORMAT) :'';
this.props.context.componentParent.updateTime(
this.props.node.rowIndex,this.props.colDef.field, date
);
}
render() {
return (
<Calendar
d dateFormat="dd-M-yy"
value= {this.state.date}
onChange= {e => {this.updateDateChanges(e)}}
onBlur= {e => {this.updateDateChanges(e)}}
//data-testid="start"
showTime= {true}
showSeconds= {true}
hourFormat= "24"
showIcon= {true}
/>
);
}
updateDateChanges(e){
if(e.value){
this.setState({date : e.value});
}
}
ondatechange(e){
this.setState({date : e.value});
}
getDate() {
return this.state.date;
}
setDate(date) {
this.setState({ date });
this.picker.setDate(date);
}
updateAndNotifyAgGrid(date) {
this.setState(
{
date,
},
this.props.onDateChanged
);
}
onDateChanged = (selectedDates) => {
this.props.context.componentParent.updateTime(
this.props.node.rowIndex,this.props.colDef.field,selectedDates[0]
);
};
}
\ No newline at end of file
import React, { Component } from 'react';
import flatpickr from 'flatpickr';
import "flatpickr/dist/flatpickr.css";
export default class CustomDateComponent extends Component {
constructor(props) {
super(props);
this.state = {
date: null,
};
}
isPopup() {
return true;
}
render() {
//Inlining styles to make simpler the component
return (
<div
className="ag-input-wrapper custom-date-filter"
role="presentation"
ref="flatpickr"
style={{ height: '50px', width: '90%' }}>
<input type="text" ref="eInput" data-input style={{ width: '100%',}} />
<a className="input-button" title="clear" data-clear>
<i className="fa fa-times"></i>
</a>
</div>
);
}
componentDidMount() {
this.picker = flatpickr(this.refs.flatpickr, {
onChange: this.onDateChanged.bind(this),
dateFormat: 'd-M-Y',
timeFormat: "h:m:d",
wrap: true,
showClearButton: false,
inlineHideInput: true,
defaultHour: 0,
defaultMinute: 1,
enableSeconds: true,
defaultSecond: 0,
hourIncrement: 1,
minuteIncrement: 1,
secondIncrement: 5,
time_24hr: true,
allowInput: true
});
this.eInput = this.refs.eInput;
this.picker.calendarContainer.classList.add('ag-custom-component-popup');
}
//*********************************************************************************
// METHODS REQUIRED BY AG-GRID
//*********************************************************************************
getDate() {
//ag-grid will call us here when in need to check what the current date value is hold by this
//component.
return this.state.date;
}
setDate(date) {
//ag-grid will call us here when it needs this component to update the date that it holds.
this.setState({ date });
this.picker.setDate(date);
}
//*********************************************************************************
// AG-GRID OPTIONAL METHODS
//*********************************************************************************
setInputPlaceholder(placeholder) {
this.eInput.setAttribute('placeholder', placeholder);
}
setInputAriaLabel(label) {
this.eInput.setAttribute('aria-label', label);
}
//*********************************************************************************
// LINKS THE INTERNAL STATE AND AG-GRID
//*********************************************************************************
updateAndNotifyAgGrid(date) {
this.setState(
{
date,
},
//Callback after the state is set. This is where we tell ag-grid that the date has changed so
//it will proceed with the filtering and we can then expect ag-Grid to call us back to getDate
this.props.onDateChanged
);
}
//*********************************************************************************
// LINKING THE UI, THE STATE AND AG-GRID
//*********************************************************************************
onDateChanged = (selectedDates) => {
//console.log('>>', selectedDates[0])
this.props.context.componentParent.updateTime(
this.props.node.rowIndex,this.props.colDef.field,selectedDates[0]
);
// this.updateAndNotifyAgGrid(selectedDates[0]);
};
}
\ No newline at end of file
......@@ -2,19 +2,25 @@ import React, { Component } from 'react';
import { InputMask } from 'primereact/inputmask';
import Validator from '../../utils/validator';
const BG_COLOR= '#f878788f';
export default class DegreeInputMask extends Component {
constructor(props) {
super(props);
this.callbackUpdateAngle = this.callbackUpdateAngle.bind(this);
}
/**
* Update Angle value
* @param {*} e
*/
callbackUpdateAngle(e) {
let isValid = false;
if(Validator.validateAngle(e.value)){
e.originalEvent.target.style.backgroundColor = '';
isValid = true;
}else{
e.originalEvent.target.style.backgroundColor = 'orange';
e.originalEvent.target.style.backgroundColor = BG_COLOR;
}
this.props.context.componentParent.updateAngle(
this.props.node.rowIndex,this.props.colDef.field,e.value,false,isValid
......
import React, { Component } from 'react';
import {MultiSelect} from 'primereact/multiselect';
import _ from 'lodash';
export default class SkySllector extends Component {
constructor(props) {
super(props);
this.dailyOptions= [
{name: 'require_day', value: 'require_day'},
{name: 'require_night', value: 'require_night'},
{name: 'avoid_twilight', value: 'avoid_twilight'},
];
this.state= {
daily: [],
}
this.callbackUpdateDailyCell = this.callbackUpdateDailyCell.bind(this);
}
async componentDidMount(){
let selectedValues = this.props.data['daily'];
if(selectedValues && selectedValues.length>0){
let tmpDailyValue = _.split(selectedValues, ",");
await this.setState({
daily: tmpDailyValue,
});
}
console.log('this.props.props',this.props.data['daily'], this.state.daily)
// this.props.props.
/* console.log('---',this.props.data['daily'])
await this.setState({
daily: this.props.data['daily']
})*/
}
async callbackUpdateDailyCell(e) {
let isValid = false;
this.setState({
daily: e.value
})
let dailyValue = '';
let selectedValues = e.value;
await selectedValues.forEach( key =>{
dailyValue += key+",";
})
dailyValue = _.trim(dailyValue)
dailyValue = dailyValue.replace(/,([^,]*)$/, '' + '$1')
this.props.context.componentParent.updateDailyCell(
this.props.node.rowIndex,this.props.colDef.field,dailyValue
);
}
afterGuiAttached(){
// this.input.input.focus();
}
isPopup() {
return true;
}
render() {
return (
<div className="col-sm-6">
<MultiSelect optionLabel="name" value={this.state.daily} options={this.dailyOptions}
optionLabel="value" optionValue="value" filter={true}
onChange={this.callbackUpdateDailyCell} />
</div>
);
}
}
\ No newline at end of file
import React, { Component } from 'react';
import { InputMask } from 'primereact/inputmask';
import Validator from '../../utils/validator';
export default class RenderTimeInputmask extends Component{
constructor(props) {
super(props);
this.callbackUpdateAngle = this.callbackUpdateAngle.bind(this);
}
callbackUpdateAngle(e) {
let isValid = false;
if(Validator.validateTime(e.value)){
e.originalEvent.target.style.backgroundColor = '';
isValid = true;
}else{
e.originalEvent.target.style.backgroundColor = 'orange';
}
this.props.context.componentParent.updateAngle(
this.props.node.rowIndex,this.props.colDef.field,e.value,false,isValid
);
}
/*
isPopup(){}
isCancelBeforeStart(){}
focusIn(){}
focusOut(){}
destroy(){}
*/
isCancelAfterEnd(){
// console.log('params', this.props);
// return false;
}
afterGuiAttached(){
//this.input.input.focus();
}
getValue(){
// console.log(this.input.value)
}
render() {
return (
<InputMask
value={this.props.value}
mask="99:99:99"
placeholder="HH:mm:ss"
className="inputmask"
onComplete={this.callbackUpdateAngle}
ref={input =>{this.input = input}}
/>
);
}
}
\ No newline at end of file
import React, { Component } from 'react';
import { Dialog } from 'primereact/dialog';
import { Button } from 'primereact/button';
import Stations from '../../routes/Scheduling/Stations';
import moment from 'moment';
import _ from 'lodash';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export default class StationEditor extends Component {
constructor(props) {
super(props);
this.tmpRowData = [];
this.state = {
schedulingUnit: {},
showDialog: false,
dialogTitle: 'Station Group',
missingStationFieldsErrors: [],
stationGroup: [],
customSelectedStations: []
};
this.formRules = {
name: {required: true, message: "Name can not be empty"},
description: {required: true, message: "Description can not be empty"},
};
}
isPopup() {
return true;
}
/**
* Init the date value if exists
*/
async componentDidMount(){
let tmpStationGroups = [];
let tmpStationGroup = {};
let rowSU = this.props.agGridReact.props.rowData[this.props.node.rowIndex];
let sgCellValue = rowSU[this.props.colDef.field];
if(sgCellValue && sgCellValue.length >0){
let stationGroups = _.split(sgCellValue, "|");
stationGroups.map(stationGroup =>{
tmpStationGroup = {};
let sgValue = _.split(stationGroup, ":");
if(sgValue && sgValue[0].length>0){
let stationArray = _.split(sgValue[0], ",");
tmpStationGroup['stations'] = stationArray;
tmpStationGroup['max_nr_missing'] = sgValue[1];
tmpStationGroups.push(tmpStationGroup);
}
})
this.setState({
stationGroup: tmpStationGroups,
showDialog: true
});
}else{
let defaultSGs = this.props.context.componentParent.state.defaultStationGroups;
if(defaultSGs){
this.setState({
stationGroup: defaultSGs,
selectedStations: defaultSGs,
showDialog: true
});
}
}
}
validateForm(fieldName) {
let validForm = false;
let errors = this.state.errors;
let validFields = this.state.validFields;
if (fieldName) {
delete errors[fieldName];
delete validFields[fieldName];
if (this.formRules[fieldName]) {
const rule = this.formRules[fieldName];
const fieldValue = this.state.schedulingUnit[fieldName];
if (rule.required) {
if (!fieldValue) {
errors[fieldName] = rule.message?rule.message:`${fieldName} is required`;
} else {
validFields[fieldName] = true;
}
}
}
} else {
errors = {};
validFields = {};
for (const fieldName in this.formRules) {
const rule = this.formRules[fieldName];
const fieldValue = this.state.schedulingUnit[fieldName];
if (rule.required) {
if (!fieldValue) {
errors[fieldName] = rule.message?rule.message:`${fieldName} is required`;
} else {
validFields[fieldName] = true;
}
}
}
}
this.setState({errors: errors, validFields: validFields});
if (Object.keys(validFields).length === Object.keys(this.formRules).length) {
validForm = true;
}
return validForm && !this.state.missingStationFieldsErrors;
}
async updateStationGroup(){
let stationValue = '';
const station_groups = [];
(this.state.selectedStations || []).forEach(key => {
let station_group = {};
const stations = this.state[key] ? this.state[key].stations : [];
const max_nr_missing = parseInt(this.state[key] ? this.state[key].missing_StationFields : 0);
station_group = {
stations,
max_nr_missing
};
station_groups.push(station_group);
});
this.state.customSelectedStations.forEach(station => {
station_groups.push({
stations: station.stations,
max_nr_missing: parseInt(station.max_nr_missing)
});
});
if(station_groups){
station_groups.map(stationGroup =>{
stationValue += stationGroup.stations+':'+stationGroup.max_nr_missing+"|";
})
}
await this.props.context.componentParent.updateDailyCell(
this.props.node.rowIndex,this.props.colDef.field, stationValue
);
this.setState({ showDialog: false});
}
onUpdateStations = (state, selectedStations, missingStationFieldsErrors, customSelectedStations) => {
this.setState({
...state,
selectedStations,
missingStationFieldsErrors,
customSelectedStations
}, () => {
this.setState({
validForm: this.validateForm()
});
});
};
render() {
return (
<>
<Dialog header={_.startCase(this.state.dialogTitle)} visible={this.state.showDialog} maximized={false}
onHide={() => {this.updateStationGroup()}} inputId="confirm_dialog"
footer={<div>
<Button key="back" label="Close" onClick={() => {this.updateStationGroup()}} />
</div>
} >
<div className="ag-theme-balham" style={{ height: '90%', width: '1000px', paddingLeft: '20px' }}>
<Stations
stationGroup={this.state.stationGroup}
onUpdateStations={this.onUpdateStations.bind(this)}
/>
</div>
</Dialog>
</>
);
}
}
\ No newline at end of file
......@@ -2,6 +2,8 @@ import React, { Component } from 'react';
import { InputMask } from 'primereact/inputmask';
import Validator from '../../utils/validator';
const BG_COLOR= '#f878788f';
export default class TimeInputMask extends Component {
constructor(props) {
super(props);
......@@ -14,7 +16,7 @@ export default class TimeInputMask extends Component {
e.originalEvent.target.style.backgroundColor = '';
isValid = true;
}else{
e.originalEvent.target.style.backgroundColor = 'orange';
e.originalEvent.target.style.backgroundColor = BG_COLOR;
}
this.props.context.componentParent.updateAngle(
......
......@@ -12,9 +12,7 @@ export default class NumericEditor extends Component {
this.cancelBeforeStart =
this.props.charPress && '1234567890'.indexOf(this.props.charPress) < 0;
this.state = this.createInitialState(props);
this.onKeyDown = this.onKeyDown.bind(this);
this.handleChange = this.handleChange.bind(this);
}
......
SAS/TMSS/frontend/tmss_webapp/src/images/login-bg-1.jpg

4.81 MiB

SAS/TMSS/frontend/tmss_webapp/src/images/login-bg-2.jpg

174 KiB

......@@ -22,6 +22,12 @@
border-top: none;
}
}
.ag-root-wrapper{
/*
calendar is overlaped by AG-grid table, so table props update to show calendar
*/
overflow: inherit;
}
.tmss-table {
overflow:auto;
// since calendar getting inserted to table, because of above overflow, not getting visible
......@@ -137,10 +143,40 @@
.plots{
padding-left: 2px;
}
.list-stations-summary {
max-height: 200px;
overflow: auto;
}
.block-list {
> a {
display: block;
}
}
.se-resizing-bar .se-navigation.sun-editor-common {
display: none;
}
.se-component.se-image-container {
img {
width: 100%;
}
}
.p-button.tooltip-wrapper {
margin: 0;
padding: 0;
border: none;
background: none;
background-color: transparent;
box-shadow: none;
font-size: 1rem;
text-align: left;
color: #000;
.p-button-text.p-c{
display: none;
}
}
.p-button.tooltip-wrapper:enabled:hover {
background-color: transparent;
color: #000;
border: none;
}
......@@ -4,24 +4,27 @@ import 'primeicons/primeicons.css';
import 'primereact/resources/themes/nova-light/theme.css';
import 'primereact/resources/primereact.css';
import 'primeflex/primeflex.css';
import { PropTypes } from 'prop-types';
import { PropTypes } from 'prop-types';
import Auth from '../../authenticate/auth';
export class AppTopbar extends Component {
export class AppTopbar extends Component {
// constructor(props) {
// super(props);
// }
constructor(props) {
super(props);
this.state = {
username: Auth.getUser().name
};
}
static defaultProps = {
onToggleMenu: null
}
static propTypes = {
onToggleMenu: PropTypes.func.isRequired
}
}
static propTypes = {
onToggleMenu: PropTypes.func.isRequired
}
render() {
return (
......@@ -31,7 +34,15 @@ import 'primeflex/primeflex.css';
<button className="p-link layout-menu-button" onClick={this.props.onToggleMenu}>
<i className="pi pi-bars"></i></button>
<span className="header-title">TMSS</span>
{this.props.isLoggedIn &&
<div className="top-right-bar">
<span><i className="fa fa-user"></i>{this.state.username}</span>
<button className="p-link layout-menu-button" onClick={this.props.onLogout}>
<i className="pi pi-power-off"></i></button>
</div>
}
</div>
</div>
</React.Fragment>
)
......
......@@ -16,4 +16,5 @@
@import "./timeline";
@import "./_aggrid";
@import "./suSummary";
@import "./login";
// @import "./splitpane";
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment