diff --git a/package.json b/package.json index d3fd31d..d1b4727 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,9 @@ "devDependencies": { "adal-angular": "^1.0.17", "babel-core": "^6.26.0", + "babel-loader": "^7.1.4", + "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.6.1", "babel-preset-es2015": "^6.24.1", "babelify": "^8.0.0", @@ -123,13 +126,21 @@ "gulp-uglify": "^3.0.0", "gulp-uglifyes": "^0.1.2", "highcharts": "^6.1.0", + "html-loader": "^0.5.5", "node-glob": "^1.2.0", "popup-es": "0.2.1", "quill": "^1.3.6", "rimraf": "^2.6.2", "run-sequence": "^2.2.0", + "sha1": "^1.1.1", + "style-loader": "^0.21.0", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.2", + "uglifyjs-webpack-plugin": "^1.2.7", + "url-loader": "^1.0.1", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", + "webpack": "^4.14.0", "workbox-build": "^2.1.1" }, "postinstall": "install-app-deps" diff --git a/src/assets/css/default.css b/src/assets/css/default.css index 8ab32be..ffdad4d 100644 --- a/src/assets/css/default.css +++ b/src/assets/css/default.css @@ -32,44 +32,8 @@ summary{ outline:none; } -.navbar-top{ - background: rgb(60,60,60) !important; - margin-bottom: 0; -} -.nav-top-menu li a{ - color: rgb(255,184,12) !important; -} -/*main menu*/ -.main-menu{ - margin-top: 50px; -} - -.main-menu li{ - padding: 10px 5px 10px 5px; - margin-bottom: 2px; - cursor:pointer; - font-size: 13px; -} - -.main-menu li.sub { - color: #bbbbbb; -} - -.main-menu li.sub .material-icons { - font-size: 20px !important; -} -.main-menu li.active{ - background: rgb(255,184,12); - color: #000; -} -.main-menu li.deactivated{ - opacity: 0.5; -} -.main-menu-list{ - color: rgb(240,240,240); -} .btn-group-xs > .btn, .btn-xs { padding : .25rem .4rem; diff --git a/src/assets/css/modules/suppliers/list.css b/src/assets/css/modules/suppliers/list.css index 20f6d9d..a1d3140 100644 --- a/src/assets/css/modules/suppliers/list.css +++ b/src/assets/css/modules/suppliers/list.css @@ -148,26 +148,7 @@ } -.dropdown-section{ - position: absolute; - width: 150px; - height: 200px; - background: #fff; - z-index: 3; - right:0px; - display: none; - visibility:hidden; - opacity: 0; - transition: all 0.3s ease-in-out; - box-shadow: 0 0 7px rgba(0,0,0,.4) -} - -.dropdown-section.open{ - transition: opacity 0.3s ease-in-out; - visibility: visible; - display: block; - opacity: 1; -} + .bidding-status-info{ float: left; diff --git a/src/assets/img/mail.png b/src/assets/img/mail.png new file mode 100644 index 0000000..c06560e Binary files /dev/null and b/src/assets/img/mail.png differ diff --git a/src/assets/js/_default.js b/src/assets/js/_default.js deleted file mode 100644 index dae7cc6..0000000 --- a/src/assets/js/_default.js +++ /dev/null @@ -1,112 +0,0 @@ - - //sideBar - let sideBar=false - - //list of scripts loaded by lazyLoader using once option - window.bms.default.loadedScript=[] - - const sideBarInit=()=>{ - let sideBar=new window.bms.exports.Sidebar('.docker-menu ','#docker-sidebar') - sideBar.toggle() - } - - - window.bms.default.activateMenu=(name)=>{ - document.querySelectorAll('.main-menu-list-item').forEach((el,index)=>{ - el.classList.remove('active') - if(el.getAttribute('data-status')==name) el.classList.add('active') - }) - } - - //parse script and link from DOM - //since XHR DOESn't execute js - window.bms.default.scriptLoader=(scope)=>{ - console.log(scope) - scope.querySelectorAll(`script`).forEach((sc,index)=>{ - - let el=document.createElement('script') - el.src=sc.getAttribute('src') - if(sc.async) el.setAttribute('async','') - sc.replaceWith(el) - }) - - scope.querySelectorAll(`link`).forEach((sc,index)=>{ - let el=document.createElement('link') - el.href=sc.getAttribute('href') - el.type="text/css" - el.rel="stylesheet" - sc.replaceWith(el) - }) - - } - - - //inject - + + diff --git a/src/cauth.html b/src/cauth.html new file mode 100644 index 0000000..de5d820 --- /dev/null +++ b/src/cauth.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/app-spinner/index.js b/src/components/app-spinner/index.js new file mode 100644 index 0000000..9a5738c --- /dev/null +++ b/src/components/app-spinner/index.js @@ -0,0 +1,57 @@ +import style from './style' + +export default class{ + constructor(opt = {}){ + this.opt = opt + this.opt.style = this.opt.style || '' + this.opt.class = this.opt.class || [] + this._spinnerWrapperClass = 'spinner-container' + this._spinnerWrapperClassSelector = '.' + 'spinner-container' + this.render() + return this + } + + __show (e) { + this.classList.add('open') + } + + __hide (e) { + this.classList.remove('open') + } + + render(){ + this.template = document.createElement('div') + this.template.classList.add(this._spinnerWrapperClass, 'spinner') + this.opt.class.forEach(val => this.template.classList.add(val)) + this.template.setAttribute('style',this.opt.style) + this.template.innerHTML = ` + +
+
+
+
+
+
` + // add listeners + this.template.show = this.__show + this.template.hide = this.__hide + return this.template + } + + show(opt = {}){ + const __target = this.opt.target || opt.target + const el = document.querySelectorAll(__target) + + return new Promise((resolve, reject) => { + for (var e of el) { + if(!e.querySelector('.spinner')) { + e.prepend(this.template) + } else { + e.querySelector('.spinner').replaceWith(this.template) + } + + resolve(this) + } + }) + } +} \ No newline at end of file diff --git a/src/components/app-spinner/style.styl b/src/components/app-spinner/style.styl new file mode 100644 index 0000000..37b0677 --- /dev/null +++ b/src/components/app-spinner/style.styl @@ -0,0 +1,52 @@ +/*spinner*/ +/*https://codepen.io/ruigewaard/pen/CtnsJ*/ +.spinner + left: calc(50% - 50px) + top: 200px + position: fixed + z-index: 4046 + display: none + + &.absolute + position: absolute + + &.open + display: block; + +.block + position: relative + box-sizing: border-box + float: left + margin: 0 10px 10px 0 + width: 12px + height: 12px + border-radius: 3px + background: rgb(200,200,200) + + +.block:nth-child(4n+1) + animation: wave 1.2s ease .0s infinite + +.block:nth-child(4n+2) + animation: wave 1.2s ease .2s infinite + +.block:nth-child(4n+3) + animation: wave 1.2s ease .4s infinite + +.block:nth-child(4n+4) + animation: wave 1.2s ease .6s infinite + margin-right: 0 + + +@keyframes wave + 0% + top: 0 + opacity: 1 + + 50% + top: 30px + opacity: .2 + + 100% + top: 0 + opacity: 1 diff --git a/src/components/approve-modal/index.js b/src/components/approve-modal/index.js new file mode 100644 index 0000000..d5bd2d7 --- /dev/null +++ b/src/components/approve-modal/index.js @@ -0,0 +1,14 @@ +import style from './style' +const template = ` +
+ +
+

Approve

+

You will not be able to make any changes once you approve this bidding request. Are you sure you want to continue?

+ + + +
+
` + +export { template } \ No newline at end of file diff --git a/src/components/approve-modal/style.styl b/src/components/approve-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/approve-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/attachments-dialog/actions.js b/src/components/attachments-dialog/actions.js new file mode 100644 index 0000000..9ad7a7b --- /dev/null +++ b/src/components/attachments-dialog/actions.js @@ -0,0 +1,227 @@ +import ApiConfig from '../../config/api' +const DropdownLoader = import('../../utils/dropdown-loader') + +export default class { + constructor (opt) { + this.opt = opt + this.__apiConfig = ApiConfig + this.filesToBeUploaded = {} + + } + + __showError () { + alert('Unable to process this request.Please try again later.') + } + + __appendFileToBeUploaded (e) { + const targ = document.querySelectorAll('.attachment-pool-section') + + // close dialog + document.querySelector('.file-attachment-main-dialog').classList.remove('open') + + // preview + targ.forEach((el, index) => { + for (var i = 0; i < e.target.files.length; i++) { + // add to DOM + el.innerHTML+=` +
+
+ ${e.target.files[i].name} + arrow_drop_down +
+
+
+
+ ` + // add to file uploader + this.save(e.target.opt.id, e.target.files[i], i) + } + }) + + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + } + + __bindSelectDeviceFile (opt) { + const el = document.querySelector(opt.selector) + const __proto = Object.assign({__proto__: this.__proto__}, this) + el.opt = opt + el.removeEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + el.addEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + } + + __bindSelectFile () { + const el = document.querySelectorAll('.attachment-recent-checkbox-bidding') + const __proto = Object.assign({__proto__: this.__proto__}, this) + + for(let x of el) { + x.removeEventListener('change', this.__enableUploadButton.bind(__proto)) + x.addEventListener('change', this.__enableUploadButton.bind(__proto)) + } + } + + __enableUploadButton(e) { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const id = e.target.getAttribute('data-resources') + const oFilename = e.target.getAttribute('data-original-filename') + + if(id&&e.target.checked) { + this.filesToBeUploaded[id] = oFilename + }else{ + delete this.filesToBeUploaded[id] + } + + // enable button + setTimeout(() => { + Object.keys(this.filesToBeUploaded ).length > 0 ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', 'disabled') + },10) + + } + + + __showRecent (json) { + this.filesToBeUploaded = {} + + const html = document.createElement('div') + html.classList.add('recently-attached') + html.innerHTML = ` +
+ ${json.original_filename} + (${json.size}KB ${json.date_created})` + document.querySelector(`#recently-attached-section-${this.opt.id}`).append(html) + + } + + __bindAttach() { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const proto = Object.assign({__proto__: this.__proto__}, this) + btn.addEventListener('click', this.attach.bind(proto)) + } + + + async recent (opt) { + const __serv = (await import('../../services/bidding-attachment-service')).default + this.opt = this.opt || opt + // get from storage + return new __serv().recent(opt).then(data => { + data.data.forEach((val, index) => { + this.__showRecent(val) + }) + }).then(() => { + setTimeout(() => { + this.__bindSelectFile() + this. __bindAttach() + }, 800); + }) + } + + async attach (e) { + // disable button first + e.target.setAttribute('disabled', 'disabled') + + //payload + const __payload = { + attachments: this.filesToBeUploaded, + action: 'create', + id: this.opt.id, + token: window.localStorage.getItem('token') + } + + + const service = (await import('../../services/bidding-attachment-service')).default + return new service().attach(__payload).then(data => { + if (!data.data) return this._showError() + //show in DOM + for (var i = 0; i < data.data.length; i++) { + const __d = { + type: data.data[i].type, + original_filename: data.data[i].original_filename, + id: data.data[i].id, + menus: ['remove'] + } + this.loadAttachments('.attachments-info-section', [__d]) + } + // close dialog + document.querySelectorAll('.file-attachment-main-dialog').forEach((val, res) => val.close()) + + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + e.target.removeAttribute('disabled') + },1000) + }).catch(err => { + this._showError() + }) + } + + upload(opt) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + this.__bindSelectDeviceFile(opt) + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // append files + data.forEach((val ,index) => { + targ.append(new res.default(val)) + }) + }) + } + + + save(id,file,index) { + //form data + const formData = new FormData() + formData.append('files', file) + formData.append('id', id) + formData.append('token', window.localStorage.getItem('token')) + + //XHR + let request = new XMLHttpRequest(); + request.open("POST", `${this.__apiConfig.url}/bidding/attachments/`); + + request.upload.addEventListener('progress', (e) => { + let targ = document.getElementById(`progress-bar-${index}`) + if (e.lengthComputable) { + let total = (e.total / e.loaded ) * 100 + targ.style.width=`${total}%` + } + }) + + request.addEventListener('load', (e) => { + + let targ = document.getElementById(`progress-bar-${index}`) + + if(request.responseText > 0) { + targ.parentNode.parentNode.remove() + // DOM + const __payload = { + id: request.responseText, + type: file.type.split('/')[1], + original_filename: file.name, + menus: ['remove'] + } + + this.loadAttachments('.attachments-info-section', [__payload]) + // more settings + setTimeout(() => { + // dropdown and popup menu + DropdownLoader.then(loader => new loader.default('device-dropdown')) + import('../popup-es').then(loader => new loader.default()) + + },1000) + + }else{ + targ.parentNode.textContent = 'Error uploading' + } + }) + + request.send(formData); + + } +} \ No newline at end of file diff --git a/src/components/attachments-dialog/index.js b/src/components/attachments-dialog/index.js new file mode 100644 index 0000000..b93ce46 --- /dev/null +++ b/src/components/attachments-dialog/index.js @@ -0,0 +1,95 @@ + +export default class { + constructor(opt = {}) { + this.opt = opt + return this.loadDialog() + + } + + __bindListeners () { + this.template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`).addEventListener('click', () => { + document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).close() + }) + } + + async loadDialog () { + const dialog = (await import('../dialog-pane')).default + return new dialog(this.opt).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#${res.id} > .body`) + sec.innerHTML = '' + sec.append(this.template) + + setTimeout(() => { + // load actions + import('./actions').then(actions => { + const attachments = new actions.default().upload({ + id: this.opt.id, + selector: '#file-upload-attachment-bidding', + }) + + // get recent files + const recent = new actions.default().recent({ + id: this.opt.id, + page: 1, + token: window.localStorage.getItem('token'), + }) + + }) + + },1000) + + }) + } + + render() { + this.template = document.createElement('section') + this.template.classList.add('col-12', 'row') + + // custom classes + if(this.opt.class) this.template.classList.add(...this.opt.class.split(' ')) + + // template + this.template.innerHTML = ` + +
+ +
+
+
+

Recently attached navigate_next Files


+
+
+
+
+   + +
+
+ ` + this.__bindListeners() + // start rendering + return this.template + } +} + + + \ No newline at end of file diff --git a/src/components/attachments-item/actions/remove.js b/src/components/attachments-item/actions/remove.js new file mode 100644 index 0000000..051c420 --- /dev/null +++ b/src/components/attachments-item/actions/remove.js @@ -0,0 +1,62 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-attachment-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemoveAttachments() + } + + success(id) { + // close popup + document.getElementById('general-modal').close() + // remove from DOM + document.querySelector(`#attachments-info-section-${id}`).remove() + } + + error (err = '') { + alert('Unable to process this request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async removeAttachments (e) { + + e.target.setAttribute('disabled', 'disabled') + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token: window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(json => { + return json.data == 1 ? this.success(__payload.id) : this.error() + }).catch(err => this.error(err)) + } + + loadRemoveAttachments (e) { + import('../../remove-conf-modal').then(res => { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.removeAttachments.bind(__proto)) + }) + } + + bindRemoveAttachments () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click', this.loadRemoveAttachments.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/attachments-item/index.js b/src/components/attachments-item/index.js new file mode 100644 index 0000000..6dcb0c3 --- /dev/null +++ b/src/components/attachments-item/index.js @@ -0,0 +1,61 @@ +import style from './style' +import ApiConfig from '../../config/api' + +const fileIconCss = import('../../assets/css/fileicon.css') + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + // bind remove action + if(opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-attachments-modal', + id: opt.id + }) + }) + } + import('../popup-es').then(loader => new loader.default()) + } + + render(opt = {}) { + opt.menus = opt.menus || [] + this.template = document.createElement('section') + this.template.classList.add('col-lg-3', 'col-md-3') + this.template.id = `attachments-info-section-${opt.id}` + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + + // template + this.template.innerHTML = ` + +
+
+
${opt.original_filename} +
+
+ arrow_drop_down + +
+
+ ` + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/attachments-item/style.styl b/src/components/attachments-item/style.styl new file mode 100644 index 0000000..82908da --- /dev/null +++ b/src/components/attachments-item/style.styl @@ -0,0 +1,7 @@ +.attachment-items + padding: 5px + background: #505050 + border: 1px solid #fefefe + position: relative + color: #fff; + \ No newline at end of file diff --git a/src/components/award-attachments-notice-modal/index.js b/src/components/award-attachments-notice-modal/index.js new file mode 100644 index 0000000..c0da583 --- /dev/null +++ b/src/components/award-attachments-notice-modal/index.js @@ -0,0 +1,17 @@ +import style from './style' + +const template = ` +
+ +

+

Upload Award Letter / Resolution

+

Please attach scanned award letter.

+
+ +
+ +
+
+ ` + +export { template } \ No newline at end of file diff --git a/src/components/award-attachments-notice-modal/style.styl b/src/components/award-attachments-notice-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/award-attachments-notice-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/bidding-info-menu/actions/approve.js b/src/components/bidding-info-menu/actions/approve.js new file mode 100644 index 0000000..9209ee5 --- /dev/null +++ b/src/components/bidding-info-menu/actions/approve.js @@ -0,0 +1,48 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindApprove() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + async send(e) { + const __serv = (await BiddingServ).default + const __payload = { + id: this.opt.id, + status: 3, + token : window.localStorage.getItem('token') + } + return new __serv().status(__payload).then(res => { + return res.data ? window.location.reload() : this.__showError() + }).catch(err => this.__showError()) + } + + loadApprove (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../approve-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.send.bind(__proto)) + }) + } + + __bindApprove () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadApprove.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/attach.js b/src/components/bidding-info-menu/actions/attach.js new file mode 100644 index 0000000..f5974b4 --- /dev/null +++ b/src/components/bidding-info-menu/actions/attach.js @@ -0,0 +1,71 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') + +export default class { + constructor(opt){ + this.opt = opt + return this.bindAttach() + } + + hideSpinner () { + const targ = document.querySelector('#bids-info-container > .spinner') + if (targ) targ.hide() + } + + success() { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = ` +
+

Successfully Changed!

+

+ You can now print this request. This will close automatically after 5 seconds. +

+
+ ` + // close popup with delay + setTimeout(() => { + document.getElementById('general-modal').close() + },5000) + + } + + error (err = '') { + alert('Unable to process your request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + + loadDialogPane () { + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#bids-info-container'}).then(t => t.template.show()) + }) + + return import('../../attachments-dialog').then(loader => { + return new loader.default({ + target: 'body', + id: this.opt.id + }) + }).then(() => { + this.hideSpinner() + }) + + } + + /** + *Bind + * + */ + bindAttach () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDialogPane.bind(proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/close.js b/src/components/bidding-info-menu/actions/close.js new file mode 100644 index 0000000..42eaabd --- /dev/null +++ b/src/components/bidding-info-menu/actions/close.js @@ -0,0 +1,49 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindClose() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + async close(e) { + const __serv = (await BiddingServ).default + const __payload = { + id: this.opt.id, + status: 5, + token : window.localStorage.getItem('token') + } + return new __serv().status(__payload).then(res => { + return res.data ? window.location.reload() : this.__showError() + }).catch(err => this.__showError()) + } + + loadClose (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../close-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.close.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bindClose () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadClose.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/fail.js b/src/components/bidding-info-menu/actions/fail.js new file mode 100644 index 0000000..645b40e --- /dev/null +++ b/src/components/bidding-info-menu/actions/fail.js @@ -0,0 +1,49 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindFail() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + async fail(e) { + const __serv = (await BiddingServ).default + const __payload = { + id: this.opt.id, + status: 6, + token : window.localStorage.getItem('token') + } + return new __serv().status(__payload).then(res => { + return res.data ? window.location.reload() : this.__showError() + }).catch(err => this.__showError()) + } + + loadFail (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../fail-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.fail.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bindFail () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadFail.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/remove.js b/src/components/bidding-info-menu/actions/remove.js new file mode 100644 index 0000000..e3c9547 --- /dev/null +++ b/src/components/bidding-info-menu/actions/remove.js @@ -0,0 +1,68 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemoveBidding() + } + + success() { + // close popup + document.getElementById('general-modal').close() + + // clear fields + return document.querySelector('#bids-info-container').innerHTML = ` +
+

Deleted successfully! check

+

You will not be able to see this item on the system any longer.
Please refresh your browser or select new bidding request

+
+ ` + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async removeBidding () { + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token : window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(json => { + return json.data == 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + loadRemoveBidding (e) { + import('../../remove-conf-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.removeBidding.bind(__proto)) + }) + } + + bindRemoveBidding () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + const sel = root.querySelector(this.opt.selector) + if(sel) sel.addEventListener('click',this.loadRemoveBidding.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/resend.js b/src/components/bidding-info-menu/actions/resend.js new file mode 100644 index 0000000..b60ed8a --- /dev/null +++ b/src/components/bidding-info-menu/actions/resend.js @@ -0,0 +1,63 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindResend() + } + + hideSpinner () { + const targ = document.querySelector('#general-modal > .spinner') + if (targ) targ.hide() + } + + __showError () { + alert('Oops! Unable to process this request. Please try again later.') + } + + async resend(e) { + e.target.disabled = 'disabled' + e.preventDefault() + + const __serv = (await BiddingServ).default + const __payload = { + id: this.opt.id, + status: 1, + token : window.localStorage.getItem('token') + } + + // spinner + import('../../app-spinner').then(loader => { + return new loader.default().show({target: '#general-modal'}).then(t => t.template.show()) + }) + + return new __serv().status(__payload).then(res => { + return res.data ? window.location.reload() : (this.__showError() | this.hideSpinner() | (e.target.disabled = false)) + }).catch(err => (this.__showError()| this.hideSpinner() | (e.target.disabled = false))) + } + + loadResend (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../send-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.resend.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bindResend () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadResend.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/return.js b/src/components/bidding-info-menu/actions/return.js new file mode 100644 index 0000000..9fcd09c --- /dev/null +++ b/src/components/bidding-info-menu/actions/return.js @@ -0,0 +1,64 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindReturn() + } + + hideSpinner () { + const targ = document.querySelector('#general-modal > .spinner') + if (targ) targ.hide() + } + + __showError () { + alert('Oops! Unable to process this request. Please try again later.') + } + + async return(e) { + e.target.disabled = 'disabled' + e.preventDefault() + + const __serv = (await BiddingServ).default + const __payload = { + id: this.opt.id, + status: 2, + token : window.localStorage.getItem('token') + } + + + // spinner + import('../../app-spinner').then(loader => { + return new loader.default().show({target: '#general-modal'}).then(t => t.template.show()) + }) + + return new __serv().status(__payload).then(res => { + return res.data ? window.location.reload() : (this.__showError() | this.hideSpinner() | (e.target.disabled = false)) + }).catch(err => (this.__showError() | this.hideSpinner() | (e.target.disabled = false))) + } + + loadReturn (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../send-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.return.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bindReturn () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadReturn.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/send.js b/src/components/bidding-info-menu/actions/send.js new file mode 100644 index 0000000..cdcbdb5 --- /dev/null +++ b/src/components/bidding-info-menu/actions/send.js @@ -0,0 +1,171 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindSend() + } + + hideSpinner () { + const targ = document.querySelector('#general-modal > .spinner') + if (targ) targ.hide() + } + + async send (e) { + e.target.disabled = 'disabled' + e.preventDefault() + const __serv = (await BiddingServ).default + const __cont = Object.keys(this.sendingList) + + let __emails = [] + + // removed unwanted spaces + __cont.forEach((val, index) => { + if(val.trim().length > 0) __emails.push(val) + }) + + // must contain atleast 1 email + if(__emails.length < 1) { + alert('Unable to send this request. Please try again later') + return document.getElementById('general-modal').close() + } + + const __payload = { + id: this.opt.id, + emails: __emails, + action: 'create', + token : this.opt.token, + } + + // spinner + import('../../app-spinner').then(loader => { + return new loader.default().show({target: '#general-modal'}).then(t => t.template.show()) + }) + + + return new __serv().send(__payload).then(res => { + // reload on success + return (res.data.length === __emails.length) ? window.location.reload() : (alert('Notice : Not all collaborators specified did not received this request.') | this.hideSpinner() | (e.target.disabled = false)) + }) + + } + + loadSend (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../send-to-cba-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + this.getCBA() + setTimeout(() => { + document.getElementById('modal-dialog-send-button').addEventListener('click', this.send.bind(__proto)) + }, 1000) + }) + } + + __removeFromSendingList (e) { + const resources = e.target.getAttribute('data-resources') + e.target.parentNode.parentNode.remove() + + // unchecked item + document.querySelector(`.cba-send-check-list-${resources}`).checked = false + delete this.sendingList[resources] + } + + __toggleBtn () { + setTimeout(() => { + // enable send button + const targetBtn = document.getElementById('modal-dialog-send-button') + return (Object.keys(this.sendingList).length > 0) ? targetBtn.removeAttribute('disabled') : targetBtn.setAttribute('disabled', 'disabled') + }, 10) + } + + __checkCBA (e) { + const targ = document.querySelector('.cba-invitation-sending-list-section') + const name = e.target.cbaName + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // add + if (e.target.checked) { + const html = document.createElement('div') + html.classList.add('col-12', 'col-md-12', `list-${e.target.value}`) + html.style.fontSize = '12px' + html.style.marginTop = '3px' + html.innerHTML = ` +
+ ${name} +
+ ` + // remove button + const removeBtn = document.createElement('span') + removeBtn.setAttribute('style', 'position:absolute;right:10px;top:0px;color:rgb(90,90,90);cursor:pointer;') + removeBtn.setAttribute('data-resources', e.target.value) + removeBtn.textContent = 'X' + removeBtn.addEventListener('click', this.__removeFromSendingList.bind(__proto)) + + html.querySelector('.cbaName').append(removeBtn) + targ.append(html) + // add to virtual list + this.sendingList[e.target.value] = name + } else { + // remove + delete this.sendingList[e.target.value] + document.querySelector(`.list-${e.target.value}`).remove() + } + + this.__toggleBtn() + } + + __showCBA (data, target) { + const targ = document.querySelector(target) + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + data.forEach((val, index) => { + // parent element + const html = document.createElement('div') + html.classList.add('row') + html.innerHTML = ` +
+
+
${val.profile_name.substr(0,2).toUpperCase()}
+
${val.profile_name}
+
+ ` + // checkbutton + const checkBtn = document.createElement('input') + checkBtn.classList.add(`cba-send-check-list-${val.id}`) + checkBtn.type = 'checkbox' + checkBtn.value = val.id + checkBtn.name = 'cba-send-check-list' + checkBtn.addEventListener('change', this.__checkCBA.bind(__proto)) + checkBtn.cbaName = val.profile_name + + html.querySelector('.checkBtn-section').append(checkBtn) + targ.append(html) + }) + + } + + async getCBA () { + const __serv = (await AccServ).default + const __cba = new __serv().cbaList({page: 1, token: window.localStorage.getItem('token'), id: this.opt.id}) + __cba.then(json => { + this.__showCBA(json, '.cba-invitation-check-list-section') + }) + } + + __bindSend () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadSend.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/actions/signatory.js b/src/components/bidding-info-menu/actions/signatory.js new file mode 100644 index 0000000..c3faf2c --- /dev/null +++ b/src/components/bidding-info-menu/actions/signatory.js @@ -0,0 +1,102 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') + +export default class { + constructor(opt){ + this.opt = opt + return this.bindChangeSignatories() + } + + success() { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = ` +
+

Successfully Changed!

+

+ You can now print this request. This will close automatically after 5 seconds. +

+
+ ` + // close popup with delay + setTimeout(() => { + document.getElementById('general-modal').close() + },5000) + + } + + error (err = '') { + alert('Unable to process your request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + + /** + * Set signatory in bidding request + */ + async set () { + const serve = (await BiddingServ).default + // values + const requested = document.querySelector('input#requested').value + const requested_position = document.querySelector('input#requested_position').value + const recommended = document.querySelector('input#recommending').value + const recommended_position = document.querySelector('input#recommending_position').value + const approved = document.querySelector('input#approved').value + const approving_position = document.querySelector('input#approving_position').value + + // payload + const __payload = { + requested, + requested_position, + recommended, + recommended_position, + approved, + approving_position, + id: this.opt.id, + token : window.localStorage.getItem('token'), + } + + // update data + return new serve().signatories(__payload).then(json => { + const res = JSON.parse(json) + return res ? this.success() : this.error() + }).catch(err => this.error(err)) + + } + + /** + *Fetch signatory form + * + */ + loadSetSignatories () { + import('../../sign-conf-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-send-button').addEventListener('click', this.set.bind(__proto)) + }) + } + + /** + *Bind + * + */ + bindChangeSignatories () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadSetSignatories.bind(proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/bidding-info-menu/index.js b/src/components/bidding-info-menu/index.js new file mode 100644 index 0000000..bf42ba9 --- /dev/null +++ b/src/components/bidding-info-menu/index.js @@ -0,0 +1,182 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + // bind remove action + if(opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-bidding-modal-btn', + id: opt.id + }) + }) + } + + // bind remove action + if(opt.menus.indexOf('sign')!=-1) { + import('./actions/signatory').then(loader => { + return new loader.default({ + root: this.template, + selector: '.signatories-bidding-modal-btn', + id: opt.id + }) + }) + } + + // bind remove action + if(opt.menus.indexOf('attach')!=-1) { + import('./actions/attach').then(loader => { + return new loader.default({ + root: this.template, + selector: '.file-attachment-dialog-btn', + id: opt.id + }) + }) + } + + + // bind remove action + if(opt.menus.indexOf('send')!=-1) { + import('./actions/send').then(loader => { + return new loader.default({ + root: this.template, + selector: '.send-bidding-modal-btn', + id: opt.id, + }) + }) + } + + + // return + if(opt.menus.indexOf('return')!=-1) { + import('./actions/return').then(loader => { + return new loader.default({ + root: this.template, + selector: '.return-bidding-modal-btn', + id: opt.id, + }) + }) + } + + // resend + if(opt.menus.indexOf('resend')!=-1) { + import('./actions/resend').then(loader => { + return new loader.default({ + root: this.template, + selector: '.resend-bidding-modal-btn', + id: opt.id, + }) + }) + } + + } + + async render(opt) { + const ApiConfig = await import('../../config/api') + this.template = document.createElement('menu') + this.template.id = "detail-info-menu" + this.template.classList.add('row') + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + opt.menus = opt.menus || [] + + // template + this.template.innerHTML = ` + + + ` + // event listeners + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/bidding-info-menu/style.styl b/src/components/bidding-info-menu/style.styl new file mode 100644 index 0000000..b996a15 --- /dev/null +++ b/src/components/bidding-info-menu/style.styl @@ -0,0 +1,11 @@ +#detail-info-menu + background:#fff + position: relative + box-shadow: 0 0 10px rgba(200,200,200,.4) + font-size: 14px; + color: #444444 + margin-top: 0 + padding: 5px + + & a + color: #444 diff --git a/src/components/bidding-status/index.js b/src/components/bidding-status/index.js new file mode 100644 index 0000000..44b11a8 --- /dev/null +++ b/src/components/bidding-status/index.js @@ -0,0 +1,27 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + async render(opt = {}) { + this.template = document.createElement('section') + this.template.id = 'bidding-status' + this.template.classList.add('row') + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + +
${opt.icon}
+
${opt.message}
+
${opt.actions}
+ ` + // start rendering + return this.template + } +} diff --git a/src/components/bidding-status/style.styl b/src/components/bidding-status/style.styl new file mode 100644 index 0000000..6d56a34 --- /dev/null +++ b/src/components/bidding-status/style.styl @@ -0,0 +1,5 @@ +#bidding-status + font-size: 14px + padding: 15px + background: #495057 + color: #fff \ No newline at end of file diff --git a/src/pages/bidding/modal/close.html b/src/components/close-modal/index.js similarity index 77% rename from src/pages/bidding/modal/close.html rename to src/components/close-modal/index.js index 2eec0aa..91447a4 100644 --- a/src/pages/bidding/modal/close.html +++ b/src/components/close-modal/index.js @@ -1,9 +1,7 @@ - +import style from './style' +const template = `
+
lock

Close

@@ -12,4 +10,6 @@

Close

-
\ No newline at end of file +` + +export { template } \ No newline at end of file diff --git a/src/components/close-modal/style.styl b/src/components/close-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/close-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/customer-reviews/index.js b/src/components/customer-reviews/index.js new file mode 100644 index 0000000..6033710 --- /dev/null +++ b/src/components/customer-reviews/index.js @@ -0,0 +1,47 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + this.__timestamp = Date.now() + return this.render(opt) + } + + render(opt = {}) { + this.template = document.createElement('article') + this.template.style.fontSize = '0.9em' + this.template.classList.add('row', 'col-12') + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + +
+
+ +
+
JO
+
+

John Kenneth G. Abella
+ Information Technology Services Unit
+ + print PRINT + +

+
+
+ +

Nice!!!!!!
Compucare HP Eliteone800

+
+ +
price
starstarstarstar_border
quality
starstarstarstar
time
starstarstar_borderstar_border
+ +


+ ` + + + // start rendering + return this.template + } +} diff --git a/src/components/customer-reviews/style.styl b/src/components/customer-reviews/style.styl new file mode 100644 index 0000000..98f5453 --- /dev/null +++ b/src/components/customer-reviews/style.styl @@ -0,0 +1,5 @@ +.star-ratings + cursor: pointer + + &.active + color: #ffb80c; \ No newline at end of file diff --git a/src/components/deadline-modal/index.js b/src/components/deadline-modal/index.js new file mode 100644 index 0000000..840fdf9 --- /dev/null +++ b/src/components/deadline-modal/index.js @@ -0,0 +1,16 @@ +import style from './style' +const template = ` +
+ +
+

Set Deadline

+

This will only allow suppliers to submit their proposals within specified date.

+ +

+ + + +
+
` + +export { template } \ No newline at end of file diff --git a/src/components/deadline-modal/style.styl b/src/components/deadline-modal/style.styl new file mode 100644 index 0000000..023974e --- /dev/null +++ b/src/components/deadline-modal/style.styl @@ -0,0 +1,7 @@ +.deadline-modal-section + padding: 20px + + & input + + &#deadline + border: 1px solid #ccc !important diff --git a/src/components/dialog-pane/index.js b/src/components/dialog-pane/index.js new file mode 100644 index 0000000..521343a --- /dev/null +++ b/src/components/dialog-pane/index.js @@ -0,0 +1,56 @@ +import style from './style' +export default class { + constructor (opt = {}) { + this.opt = opt + return this.render().__show() + } + + __close () { + this.classList.remove('open') + } + + close (e) { + return e.target.classList.contains('file-attachment-main-dialog') ? document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).classList.remove('open') : false + } + + open (e) { + return setTimeout(() => { document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).classList.add('open') }, 10) + } + + cancel () { + return document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).classList.remove('open') + } + + render () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const template = document.createElement('section') + template .classList.add('col', 'col-lg-12', 'file-attachment-main-dialog') + + //unique dialog box + template.id = `file-attachment-main-dialog-${this.opt.id}` + template.close = this.__close + template.addEventListener('click', this.close.bind(proto)) + template.innerHTML = ` + +
+ ` + let closebtn = template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`) + closebtn = closebtn ? closeBtn.addEventListener('click', this.cancel.bind(proto)) : false + template.open = this.open.bind(proto) + this.template = template + return this + } + + /** + * Add to DOM only ONCE + * + * @returns promise + */ + __show () { + return new Promise((resolve, reject) => { + let res = (!document.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`)) ? document.querySelector(this.opt.target).append(this.template) : false + resolve(this.template) + }) + + } +} \ No newline at end of file diff --git a/src/components/dialog-pane/style.styl b/src/components/dialog-pane/style.styl new file mode 100644 index 0000000..e6a4f64 --- /dev/null +++ b/src/components/dialog-pane/style.styl @@ -0,0 +1,24 @@ +.file-attachment-main-dialog + background: rgba(255,255,255,0.6) + height: 100vh + position: fixed + z-index: 2 + opacity: 0 + visibility: hidden + top: 0 + +.file-attachment-main-dialog + & .body + transition: all 0.2s ease-in-out + right: -120vh + opacity: 0 + +.file-attachment-main-dialog + &.open + visibility: visible + opacity: 1 + +.file-attachment-main-dialog.open + & .body + right: 0 + opacity: 1 \ No newline at end of file diff --git a/src/pages/bidding/modal/failed.html b/src/components/fail-modal/index.js similarity index 76% rename from src/pages/bidding/modal/failed.html rename to src/components/fail-modal/index.js index 75dc475..21b5dc8 100644 --- a/src/pages/bidding/modal/failed.html +++ b/src/components/fail-modal/index.js @@ -1,9 +1,7 @@ - +import style from './style' +const template = `
+

Failure of bidding

You will not be able to make any changes once you change the status of this bidding request. Are you sure you want to continue?

@@ -11,4 +9,6 @@

Failure of bidding

-
\ No newline at end of file +` + +export { template } \ No newline at end of file diff --git a/src/components/fail-modal/style.styl b/src/components/fail-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/fail-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/general-style/circle-marker.styl b/src/components/general-style/circle-marker.styl new file mode 100644 index 0000000..a10b68d --- /dev/null +++ b/src/components/general-style/circle-marker.styl @@ -0,0 +1,20 @@ +.circle-marker + float: left + width: 33px + height: 33px + border-radius: 50% + text-align: center + background: #009688 + overflow: hidden + margin-left: 10px + margin-right: 10px + color: #fff + cursor: pointer + padding: 1px + + &.small + width: 25px + height: 25px + + &.gray + background: #676767 \ No newline at end of file diff --git a/src/components/general-style/scrollbar-custom.styl b/src/components/general-style/scrollbar-custom.styl new file mode 100644 index 0000000..6035b33 --- /dev/null +++ b/src/components/general-style/scrollbar-custom.styl @@ -0,0 +1,9 @@ +::-webkit-scrollbar + width: 0.5em; + +::-webkit-scrollbar-track + -webkit-box-shadow: none; + +::-webkit-scrollbar-thumb + background-color: rgba(10,10,10,0.5); + outline: 1px solid slategrey; \ No newline at end of file diff --git a/src/components/general-style/star-ratings.styl b/src/components/general-style/star-ratings.styl new file mode 100644 index 0000000..c3e3a05 --- /dev/null +++ b/src/components/general-style/star-ratings.styl @@ -0,0 +1,5 @@ +.star-ratings + cursor: pointer + + &.active + color: #ffb80c \ No newline at end of file diff --git a/src/components/inv-item/index.js b/src/components/inv-item/index.js new file mode 100644 index 0000000..875ee40 --- /dev/null +++ b/src/components/inv-item/index.js @@ -0,0 +1,38 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + render(opt) { + this.template = document.createElement('div') + this.template.setAttribute('data-list', opt.id) + + // class + this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + + ${opt.name}
+

+ Quantity : ${opt.quantity} - ${opt.unit} +

+ +

+ Deadline :${opt.deadline}
+ Bidding Item: # ${opt.bidding_requirements_id}
+

+
+ REF#${opt.id} +
+ + + ` + // start rendering + return this.template + } +} + diff --git a/src/components/inv-item/style.styl b/src/components/inv-item/style.styl new file mode 100644 index 0000000..a28f7c5 --- /dev/null +++ b/src/components/inv-item/style.styl @@ -0,0 +1,28 @@ +.list-container + display: block + +.list-bidding-section + border-top: 1px solid rgba(240,240,240,0.8) + +.list + padding-top: 10px + background: #fff + padding-bottom: 5px + cursor: pointer + height: auto + overflow: auto + border-bottom: 1px solid rgba(220,220,220,.6) + + & a + text-decoration-line: none + + &:hover + text-decoration-line: none + + + & .list-item-header + color: #000 + + &.active + border-left: 3px solid #17a2b8 + background: rgba(240,240,240,.3) diff --git a/src/components/left-sidebar/index.js b/src/components/left-sidebar/index.js new file mode 100644 index 0000000..e7ac09c --- /dev/null +++ b/src/components/left-sidebar/index.js @@ -0,0 +1,90 @@ +import style from './style' +import scrollbar from '../general-style/scrollbar-custom' + +export default ` + +
+ + + + + + + +
+` \ No newline at end of file diff --git a/src/components/left-sidebar/style.styl b/src/components/left-sidebar/style.styl new file mode 100644 index 0000000..4ad6cbb --- /dev/null +++ b/src/components/left-sidebar/style.styl @@ -0,0 +1,24 @@ +.main-menu + margin-top: 50px + + li + padding: 10px 5px 10px 5px + margin-bottom: 2px + cursor:pointer + font-size: 13px + + &.sub + color: #bbbbbb; + + & .material-icons + font-size: 20px !important + + &.active + background: rgb(255,184,12) + color: #000 + + &.deactivated + opacity: 0.5 + +.main-menu-list + color: rgb(240,240,240) diff --git a/src/components/list-item/index.js b/src/components/list-item/index.js new file mode 100644 index 0000000..dfaf8c6 --- /dev/null +++ b/src/components/list-item/index.js @@ -0,0 +1,32 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + render(opt) { + this.template = document.createElement('div') + this.template.setAttribute('data-list', opt.id) + + // class + this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + +
#${opt.id}
+ +

+ ${opt.profile_name}
+ ${opt.date_created} +

+
+
+ ` + // start rendering + return this.template + } +} + diff --git a/src/components/list-item/style.styl b/src/components/list-item/style.styl new file mode 100644 index 0000000..66e72e2 --- /dev/null +++ b/src/components/list-item/style.styl @@ -0,0 +1,29 @@ +list-container + display: block + +.list-bidding-section + border-top: 1px solid rgba(240,240,240,0.8) + +.list + padding-top: 10px + background: #fff + padding-bottom: 5px + cursor: pointer + height: auto + overflow: auto + border-bottom: 1px solid rgba(220,220,220,.6) + + & a + text-decoration-line: none + + &:hover + text-decoration-line: none + + + & .list-item-header + font-weight: bold + color: #000 + + &.active + border-left: 3px solid #17a2b8 + background: rgba(240,240,240,.3) diff --git a/src/components/list-section/index.js b/src/components/list-section/index.js new file mode 100644 index 0000000..453bf41 --- /dev/null +++ b/src/components/list-section/index.js @@ -0,0 +1,50 @@ +import style from './style' + +export default ` + +
+
+
+
+ + + + +
+
+
+ + Filter expand_more + + + + + New add_circle_outline +
+
+ + +
+ + +
+
+
+ +` \ No newline at end of file diff --git a/src/components/list-section/style.styl b/src/components/list-section/style.styl new file mode 100644 index 0000000..c4c6a1c --- /dev/null +++ b/src/components/list-section/style.styl @@ -0,0 +1,16 @@ +.list-sidebar + padding-bottom: 100px + +.search-button-group + & .btn + background:#fff; + color:#3c3c3c; + +.search-button-group + & #search + border:none !important; + +.search-button-group + & .btn:nth-child(2) + border-left:1px solid #efefef; + diff --git a/src/components/main-header/index.js b/src/components/main-header/index.js new file mode 100644 index 0000000..6eb85c4 --- /dev/null +++ b/src/components/main-header/index.js @@ -0,0 +1,53 @@ +import style from './style' + +export default ` + +
+ +
` \ No newline at end of file diff --git a/src/components/main-header/style.styl b/src/components/main-header/style.styl new file mode 100644 index 0000000..721666c --- /dev/null +++ b/src/components/main-header/style.styl @@ -0,0 +1,40 @@ +navbar + width: 100% + min-height: 50px + +.navbar-fixed-top + position: fixed + right: 0 + left: 0 + z-index: 1030 + +.navbar-dark + background: #3c3c3c!important + color: rgba(255,255,255,.84) + +.hamburger + float: left + +.hamburger .burger + display: inherit + width: 18px + height: 2px + background: rgb(255,184,12) !important + margin: 2px 98% 1px 15px + +.hamburger.hidden + display: none + +.nav-top-menu + display: none + +.back-to-list-button + color: rgb(255,184,12) !important + +.navbar-top + background: rgb(60,60,60) !important + margin-bottom: 0 + +.nav-top-menu li a + color: rgb(255,184,12) !important + diff --git a/src/components/particulars-item/actions/remove.js b/src/components/particulars-item/actions/remove.js new file mode 100644 index 0000000..7468c18 --- /dev/null +++ b/src/components/particulars-item/actions/remove.js @@ -0,0 +1,71 @@ +const BiddingServ = import('../../../services/bidding-part-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemove() + } + + hideSpinner () { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() + } + + success() { + this.hideSpinner() + // close popup + document.getElementById('general-modal').close() + this.opt.root.querySelector(this.opt.selector).parentNode.parentNode.innerHTML = ` +

Content is unavailable. This particular has been removed

+ ` + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + this.hideSpinner() + } + + async remove () { + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token : window.localStorage.getItem('token'), + } + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res) + return res.data == 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + load (e) { + import('../../remove-conf-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.remove.bind(__proto)) + }) + } + + bindRemove() { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click',this.load.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/particulars-item/index.js b/src/components/particulars-item/index.js new file mode 100644 index 0000000..2a37a47 --- /dev/null +++ b/src/components/particulars-item/index.js @@ -0,0 +1,103 @@ +import style from '../general-style/circle-marker' +const requirementsItem = import('../requirements-item') + + +export default class { + constructor(opt = {}) { + this.__timestamp = Date.now() + this.opt = opt + this.opt.menus = opt.menus || [] + return this.render(opt) + } + __bindListeners () { + if(this.opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-particular-modal-btn', + id: this.opt.id, + }) + }) + } + } + + __loadPopup () { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + + } + + render(opt = {}) { + this.template = document.createElement('section') + this.template.style.fontSize = '0.9em' + this.template.style.marginBottom = '40px' + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + // template + this.template.innerHTML = ` + +
+
+

+ shopping_basket ${opt.name}
+ Deadline : ${opt.deadline} +

+
+
+ ${this.opt.menus.indexOf('update')!=-1 ? `` : '' } + ${this.opt.menus.indexOf('remove')!=-1 ? `` : ''} +
+ +
+
+

+ ${opt.requirements.length} Requirements   + ${this.opt.menus.indexOf('add')!=-1 ? `Add New` : '' } +

+
+
+
+
+ + ` + // get items + requirementsItem.then(res => { + const id = `#item-${this.__timestamp}` + this.opt.requirements.forEach((el, index) => { + // render + this.template.querySelector(id).append( + new res.default({ + id: el.id, + name: el.name, + currency: el.budget_currency, + amount: new Intl.NumberFormat('en-us').format(el.budget_amount), + deadline: el.deadline, + funds: el.funds, + awarded: el.awardees.length, + quantity: el.quantity, + unit: el.unit, + menus: (this.opt.menus.indexOf('remove')!=-1) ? ['update', 'remove'] : [] + })) + }) + + this.__loadPopup() + }) + + this.__bindListeners() + + // start rendering + return this.template + } +} diff --git a/src/assets/js/components/PopupES/PopupES.js b/src/components/popup-es/index.js similarity index 79% rename from src/assets/js/components/PopupES/PopupES.js rename to src/components/popup-es/index.js index da1f1b4..3664f4f 100644 --- a/src/assets/js/components/PopupES/PopupES.js +++ b/src/components/popup-es/index.js @@ -34,6 +34,8 @@ export default class{ //funtions this._findPopups(this.selector) this._findPopupHandler(this.openSelector) + setTimeout(() => { this.__observe() },800) + } _setDisplayAttr(el,display=true){ return display?el.setAttribute('open',''):el.removeAttribute('open') @@ -44,19 +46,19 @@ export default class{ let date=new Date() return el.style.zIndex=(typeof i=='number')?1:2046+(date.getMinutes()+date.getSeconds()) } - _findPopups(selector){ + _findPopups(selector){ return new Promise((resolve,reject)=>{ - document.querySelectorAll(selector).forEach((el,index)=>{ + document.querySelectorAll(selector).forEach((el,index)=>{ //Ignore already defined popups if(el.popup) return resolve(this) - el.show=(e)=>{ - //prevent undefined event when calling .show() in console - if(e) e.preventDefault() - this._setDisplayAttr(el,1) - //set z-index - this._setIndex(el) + el.show = (e) => { + //prevent undefined event when calling .show() in console + if(e) e.preventDefault() + this._setDisplayAttr(el,1) + //set z-index + this._setIndex(el) } el.close=()=>{ @@ -64,8 +66,9 @@ export default class{ this._setIndex(el,1) } + //prevent attaching event listeners to the same target - el.popup=true + el.popup = true //handle close event this._findPopupCloseHandler(`${selector} ${this.closeSelector}`) @@ -74,8 +77,8 @@ export default class{ }) } _findPopupHandler(selector){ - document.querySelectorAll(selector).forEach((el,index)=>{ - let target=el.getAttribute(this.targetSelector) + document.querySelectorAll(selector).forEach((el,index)=>{ + let target = el.getAttribute(this.targetSelector) this._findTargetPopup(target).then((e)=>{ el.removeEventListener('click',e.show) el.addEventListener('click',e.show) @@ -106,6 +109,27 @@ export default class{ return this.offsetParent.offsetParent.close() } + __observe() { + /*let mutationObserver = new MutationObserver(function(mutations) { + console.log(document.contains(document.querySelector('[data-toggle]'))) + mutations.forEach(function(mutation) { + if(mutation.addedNodes[0]) { + console.log(typeof mutation.addedNodes) + } + }) + }) + + + mutationObserver.observe(document.body, { + attributes: true, + characterData: true, + childList: true, + subtree: true, + attributeOldValue: true, + characterDataOldValue: true + })*/ + } + closeAll(){ return new Promise((resolve,reject)=>{ document.querySelectorAll(this.selector).forEach((el,index)=>{ @@ -125,7 +149,7 @@ export default class{ }) }) }) - } + } \ No newline at end of file diff --git a/src/components/popup-es/style.styl b/src/components/popup-es/style.styl new file mode 100644 index 0000000..4bafe3c --- /dev/null +++ b/src/components/popup-es/style.styl @@ -0,0 +1,46 @@ +/** + * Popup.css + */ +*[data-popup] + position: fixed + display: block + width: 100% + height: 100% + top:0 + left: 0 + overflow-y: auto + background: rgba(40,40,40,0.9) + visibility:hidden + margin:auto + padding: 1em + opacity: 0 + z-index: 1 + border-width: initial + border-style: solid + border-color: transparent + border-image: initial + + +*[data-popup][open] + opacity: 1 + visibility: visible !important + +*[data-popup="fade"] + transition: visibility 0.2s, opacity 0.2s ease-in-out + + +*[data-popup] > .content:not([data-role="none"]) + position: absolute + float: left + width: 250px + height:auto + min-height: 300px + background: #fff + left:calc(50% - 125px) + top:10vh + margin-bottom: 5vh + + +*[data-popup] .popup-close-button + float: right + margin:5px diff --git a/src/components/proposal-attachments-item/actions/remove.js b/src/components/proposal-attachments-item/actions/remove.js new file mode 100644 index 0000000..588174c --- /dev/null +++ b/src/components/proposal-attachments-item/actions/remove.js @@ -0,0 +1,62 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-proposal-attachment-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemoveAttachments() + } + + success(id) { + // close popup + document.getElementById('general-modal').close() + // remove from DOM + document.querySelector(`#attachments-info-section-${id}`).remove() + } + + error (err = '') { + alert('Unable to process this request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async removeAttachments (e) { + + e.target.setAttribute('disabled', 'disabled') + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token: window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res.data) + return res.data == 1 ? this.success(__payload.id) : this.error() + }).catch(err => this.error(err) | console.log(error)) + } + + loadRemoveAttachments (e) { + import('../../remove-conf-modal').then(res => { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.removeAttachments.bind(__proto)) + }) + } + + bindRemoveAttachments () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click', this.loadRemoveAttachments.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/proposal-attachments-item/index.js b/src/components/proposal-attachments-item/index.js new file mode 100644 index 0000000..1415abf --- /dev/null +++ b/src/components/proposal-attachments-item/index.js @@ -0,0 +1,61 @@ +import style from './style' +import ApiConfig from '../../config/api' + +const fileIconCss = import('../../assets/css/fileicon.css') + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + // bind remove action + if(opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-attachments-modal', + id: opt.id + }) + }) + } + import('../popup-es').then(loader => new loader.default()) + } + + render(opt = {}) { + opt.menus = opt.menus || [] + this.template = document.createElement('section') + this.template.classList.add('col-lg-3', 'col-md-3') + this.template.id = `attachments-info-section-${opt.id}` + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + + // template + this.template.innerHTML = ` + +
+
+
${opt.original_filename} +
+
+ arrow_drop_down + +
+
+ ` + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/proposal-attachments-item/style.styl b/src/components/proposal-attachments-item/style.styl new file mode 100644 index 0000000..82908da --- /dev/null +++ b/src/components/proposal-attachments-item/style.styl @@ -0,0 +1,7 @@ +.attachment-items + padding: 5px + background: #505050 + border: 1px solid #fefefe + position: relative + color: #fff; + \ No newline at end of file diff --git a/src/components/proposal-attachments-notice-modal/index.js b/src/components/proposal-attachments-notice-modal/index.js new file mode 100644 index 0000000..4a991e4 --- /dev/null +++ b/src/components/proposal-attachments-notice-modal/index.js @@ -0,0 +1,19 @@ +import style from './style' + +const template = ` +
+ +

+

Please attach scanned formal quotation

+

Follow the instructions below to upload file

+
+ +
+
+
+ +
+
+` + +export { template } \ No newline at end of file diff --git a/src/components/proposal-attachments-notice-modal/style.styl b/src/components/proposal-attachments-notice-modal/style.styl new file mode 100644 index 0000000..91a1553 --- /dev/null +++ b/src/components/proposal-attachments-notice-modal/style.styl @@ -0,0 +1,8 @@ +#general-modal + & .content + min-width: 350px + width: 60% + left: 20% + +.send-req-modal-section + padding: 20px \ No newline at end of file diff --git a/src/components/proposal-dialog/actions.js b/src/components/proposal-dialog/actions.js new file mode 100644 index 0000000..534ecb2 --- /dev/null +++ b/src/components/proposal-dialog/actions.js @@ -0,0 +1,227 @@ +import ApiConfig from '../../config/api' +const DropdownLoader = import('../../utils/dropdown-loader') + +export default class { + constructor (opt) { + this.opt = opt + this.__apiConfig = ApiConfig + this.filesToBeUploaded = {} + + } + + __showError () { + alert('Unable to process this request.Please try again later.') + } + + __appendFileToBeUploaded (e) { + const targ = document.querySelectorAll('.attachment-pool-section') + + // close dialog + document.querySelector('.file-attachment-main-dialog').classList.remove('open') + + // preview + targ.forEach((el, index) => { + for (var i = 0; i < e.target.files.length; i++) { + // add to DOM + el.innerHTML+=` +
+
+ ${e.target.files[i].name} + arrow_drop_down +
+
+
+
+ ` + // add to file uploader + this.save(e.target.opt.id, e.target.files[i], i) + } + }) + + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + } + + __bindSelectDeviceFile (opt) { + const el = document.querySelector(opt.selector) + const __proto = Object.assign({__proto__: this.__proto__}, this) + el.opt = opt + el.removeEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + el.addEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + } + + __bindSelectFile () { + const el = document.querySelectorAll('.attachment-recent-checkbox-bidding') + const __proto = Object.assign({__proto__: this.__proto__}, this) + + for(let x of el) { + x.removeEventListener('change', this.__enableUploadButton.bind(__proto)) + x.addEventListener('change', this.__enableUploadButton.bind(__proto)) + } + } + + __enableUploadButton(e) { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const id = e.target.getAttribute('data-resources') + const oFilename = e.target.getAttribute('data-original-filename') + + if(id&&e.target.checked) { + this.filesToBeUploaded[id] = oFilename + }else{ + delete this.filesToBeUploaded[id] + } + + // enable button + setTimeout(() => { + Object.keys(this.filesToBeUploaded ).length > 0 ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', 'disabled') + },10) + + } + + + __showRecent (json) { + this.filesToBeUploaded = {} + + const html = document.createElement('div') + html.classList.add('recently-attached') + html.innerHTML = ` +
+ ${json.original_filename} + (${json.size}KB ${json.date_created})` + document.querySelector(`#recently-attached-section-${this.opt.id}`).append(html) + + } + + __bindAttach() { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const proto = Object.assign({__proto__: this.__proto__}, this) + btn.addEventListener('click', this.attach.bind(proto)) + } + + + async recent (opt) { + const __serv = (await import('../../services/bidding-attachment-service')).default + this.opt = this.opt || opt + // get from storage + return new __serv().recent(opt).then(data => { + data.data.forEach((val, index) => { + this.__showRecent(val) + }) + }).then(() => { + setTimeout(() => { + this.__bindSelectFile() + this. __bindAttach() + }, 800); + }) + } + + async attach (e) { + // disable button first + e.target.setAttribute('disabled', 'disabled') + + //payload + const __payload = { + attachments: this.filesToBeUploaded, + action: 'create', + id: this.opt.id, + token: window.localStorage.getItem('token') + } + + + const service = (await import('../../services/bidding-attachment-service')).default + return new service().attach(__payload).then(data => { + if (!data.data) return this._showError() + //show in DOM + for (var i = 0; i < data.data.length; i++) { + const __d = { + type: data.data[i].type, + original_filename: data.data[i].original_filename, + id: data.data[i].id, + menus: ['remove'] + } + this.loadAttachments('.attachments-info-section', [__d]) + } + // close dialog + document.querySelectorAll('.file-attachment-main-dialog').forEach((val, res) => val.close()) + + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + e.target.removeAttribute('disabled') + },1000) + }).catch(err => { + this._showError() + }) + } + + upload(opt) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + this.__bindSelectDeviceFile(opt) + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // append files + data.forEach((val ,index) => { + targ.append(new res.default(val)) + }) + }) + } + + + save(id,file,index) { + //form data + const formData = new FormData() + formData.append('files', file) + formData.append('id', id) + formData.append('token', window.localStorage.getItem('token')) + + //XHR + let request = new XMLHttpRequest(); + request.open("POST", `${this.__apiConfig.url}/bidding/attachments/`); + + request.upload.addEventListener('progress', (e) => { + let targ = document.getElementById(`progress-bar-${index}`) + if (e.lengthComputable) { + let total = (e.total / e.loaded ) * 100 + targ.style.width=`${total}%` + } + }) + + request.addEventListener('load', (e) => { + + let targ = document.getElementById(`progress-bar-${index}`) + + if(request.responseText > 0) { + targ.parentNode.parentNode.remove() + // DOM + const __payload = { + id: request.responseText, + type: file.type.split('/')[1], + original_filename: file.name, + menus: ['remove'] + } + + this.loadAttachments('.attachments-info-section', [__payload]) + // more settings + setTimeout(() => { + // dropdown and popup menu + DropdownLoader.then(loader => new loader.default('device-dropdown')) + import('.././popup-es').then(loader => new loader.default()) + + },1000) + + }else{ + targ.parentNode.textContent = 'Error uploading' + } + }) + + request.send(formData); + + } +} \ No newline at end of file diff --git a/src/components/proposal-dialog/actions/award.js b/src/components/proposal-dialog/actions/award.js new file mode 100644 index 0000000..747381a --- /dev/null +++ b/src/components/proposal-dialog/actions/award.js @@ -0,0 +1,58 @@ +const BiddingReqServ = import('../../../services/bidding-req-proposal-service') + + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = {} + this.sendingItems = {} + this.__timeout = {} + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + + async process(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + action: 'award', + token: window.localStorage.getItem('token'), + } + + e.target.disabled = 'disabled' + return new __serv().award(__payload).then(res => { + if(!parseInt(res)) return + if(parseInt(res) > 0) return window.location.reload() + this.__showError() + + }).catch(err => console.log(err)) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../requirement-award-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/proposal-dialog/actions/winner.js b/src/components/proposal-dialog/actions/winner.js new file mode 100644 index 0000000..f00c46c --- /dev/null +++ b/src/components/proposal-dialog/actions/winner.js @@ -0,0 +1,90 @@ +const BiddingReqServ = import('../../../services/bidding-req-proposal-service') +const statusMessage = import('../../requirement-status') +const infoStatus = import('../../bidding-status') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.__timeout = {} + + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + /** + * Display notification to attach bidding resolution after they awarded a supplier. + * @param {Object} e - MouseEvent + * @returns XMLHttpRequest + */ + loadAwardRequirementsAttachmentsNotice () { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../award-attachments-notice-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', () => { + document.getElementById('general-modal').close() + // click attachment button + document.querySelector('.file-prop-attachment-dialog-btn').click() + }) + + statusMessage.then(loader => { + const __targ = document.querySelector('#prop-info-menu-status') + __targ.innerHTML = '' + infoStatus.then(stat => { + return new stat.default(loader.showModifiedStatus()).then(html => __targ.append(html)) + }) + + }) + }) + } + + + + + async process(e) { + const __serv = (await BiddingReqServ).default + + const __payload = { + id: this.opt.id, + remarks: document.getElementById('award-remarks').value, + action: 'winner', + token: window.localStorage.getItem('token') + } + + e.target.disabled = 'disabled' + return new __serv().send(__payload).then(res => { + if(!res) return this.__showError () + if(parseInt(res) > 0) { + return this.loadAwardRequirementsAttachmentsNotice() + } + this.__showError() + }).catch(err => console.log(err)) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../winner-proposal-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/proposal-dialog/index.js b/src/components/proposal-dialog/index.js new file mode 100644 index 0000000..0b9a4a3 --- /dev/null +++ b/src/components/proposal-dialog/index.js @@ -0,0 +1,338 @@ +import { isCBAAsst , isAdmin, isGSU, isSupplier, isStandard } from '../../utils/privilege-loader' + +export default class { + constructor(opt = {}) { + this.__info = {} + this.opt = opt + return this.loadDialog() + + } + + + loadPopup () { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + }) + + popupes.then(loader => { + const a = new loader.default() + }) + + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../proposal-attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + + // append files + data.forEach((val ,index) => { + val.menus = ['remove'] + // class + val.class = "col-lg-12 col-12" + targ.append(new res.default(val)) + }) + import('../../utils/dropdown-loader').then(loader => loader.default('device-dropdown')) + }) + } + + + async loadDialog () { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const dialog = (await import('../../components/dialog-pane')).default + this.__info = await this.__getInfo(this.opt.id) + + //this.__info = await this.__getInfo(this.opt.id) + return new dialog(this.opt).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#${res.id} > .body`) + + sec.innerHTML = '' + sec.append(this.template) + }) + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + import('../../services/bidding-proposal-service').then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + getMenu() { + let menus = [] + + if(this.__info.status == 0) menus = ['send', 'attach', 'remove', 'update'] + if(this.__info.status == 1 && isCBAAsst()) menus = ['request_new', 'attach', 'remove', 'update'] + if(this.__info.status == 5 && isCBAAsst()) menus = ['attach'] + + return import('../proposal-menu').then(loader => { + return new loader.default({ + id: this.opt.id, + menus + }).then(res => { + const __targ = this.template.querySelector('#prop-info-menu') + __targ.innerHTML = '' + __targ.append(res) + + // send status + if(this.__info.status == 1 && !isCBAAsst()) { + this.template.querySelector('#prop-info-menu-status').innerHTML = ` +
+

+ share This proposal has been sent. You cannot change your proposal any longer. +

+
+ ` + } + + // send status + if(this.__info.status == 1 && isCBAAsst()) { + this.template.querySelector('#prop-info-menu-status').innerHTML = ` +
+

+ share You have received this proposal. Please review all the details before doing any further actions. + + star Set as Winner + +
+ Note: Supplier will not received any notification. General Services should 'award' the supplier manualy. +

+
` + + import('./actions/winner').then(loader => { + return new loader.default({ + root: this.template, + selector: '.award-prop-modal-btn', + id: this.__info.id, + }) + }) + } + + + // awarded status + if(this.__info.status == 3) { + this.template.querySelector('#prop-info-menu-status').innerHTML = ` +
+

+
Congratulations!
This proposal stands out among others +

+
+ ` + } + + if(this.__info.status == 2) { + this.template.querySelector('#prop-info-menu-status').innerHTML = ` +
+

+

+ warning Your proposal needs changes Find out why ?
+

${this.__info.bidders_remarks}

+
+

+
` + } + + + if(this.__info.status == 5 && (isCBAAsst() || isGSU())) { + this.template.querySelector('#prop-info-menu-status').innerHTML = ` +
+
+ +
+
Winner
+ This bidding proposal won the competition. You can now congratuate the supplier and officialy award them. 
+ + card_membership Award + +

+
+
+
` + // award + import('./actions/award').then(loader => { + return new loader.default({ + root: this.template, + selector: '.award-prop-modal-btn', + id: this.__info.id, + }) + }) + } + + }) + }) + } + + __bindListeners () { + this.template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`).addEventListener('click', () => { + document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).close() + }) + + this.getMenu().then(() => this.loadPopup ()) + + this.loadAttachments('.recently-attached-prop-section', this.__info.attachments) + + } + + + + render() { + this.template = document.createElement('section') + this.template.classList.add('row', 'col-12') + this.template.style.overflowY = 'auto' + this.__specs = '' + + let __classMarker = '' + + this.__info.specs.map(el => { + // show old value + if (((el.name != el.orig_name) || (el.value != el.orig_value)) && (el.orig_value)) __classMarker = 'text-danger' + + this.__specs+= `
+ ${el.name} +
+
+

${el.value}

+
` + + }) + // custom classes + if(this.opt.class) this.template.classList.add(...this.opt.class.split(' ')) + + // template + this.template.innerHTML = ` +
+ +
+
+
+

Your Proposal navigate_next Preview + close (x) +

+ + + + +
+ +
+ +
+
+
+
+

${this.__info.currency}: ${new Intl.NumberFormat('en-us').format(this.__info.amount)}

+

DISCOUNT : ${new Intl.NumberFormat('en-us').format(this.__info.discount)}

+ + +
+
+
+ add_shopping_cart + Specification +

+ + +
+

+
+

Remarks


+

${this.__info.remarks}

+
+ +
+
+
+ + ` + this.__bindListeners() + // start rendering + return this.template + } +} + + + \ No newline at end of file diff --git a/src/components/proposal-form/index.js b/src/components/proposal-form/index.js new file mode 100644 index 0000000..c648bcd --- /dev/null +++ b/src/components/proposal-form/index.js @@ -0,0 +1,339 @@ + +export default class { + constructor(opt = {}) { + this.__info = {} + this.opt = opt + return this.loadDialog() + + } + + __bindListeners () { + this.template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`).addEventListener('click', () => { + document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).close() + }) + } + + + async loadDialog () { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const dialog = (await import('../dialog-pane')).default + + return new dialog(this.opt).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#file-attachment-main-dialog-${this.opt.id} > .body`) + if(!sec) return + sec.parentNode.style.zIndex = 5 + sec.innerHTML = '' + sec.append(this.template) + console.log(sec) + }) + } + + + __autoComputeTotalAmount (quantity) { + const targ = document.querySelector('#proposal-form-amount-per-item:not(.event-binded)') + const totalTarg = document.querySelector('#proposal-form-amount') + + if (targ) { + targ.classList.add('event-binded') + targ.addEventListener('keyup', () => { + const total = (targ.value*quantity) + totalTarg.value = total + }) + } + } + + __cancelSpecsInput (e) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const val = e.target.value + const id = e.target.id + + let targ = document.getElementById(`orig-req-val-${id}`) + targ.innerHTML = val + + // change link + let btn = document.createElement('a') + btn.href = '#' + btn.setAttribute('onclick', 'event.preventDefault()') + btn.setAttribute('data-resources', id) + btn.setAttribute('data-resources-val', val) + btn.textContent = 'change' + btn.addEventListener('click', this.__changeEventSpecsInput.bind(__proto)) + + e.target.replaceWith(btn) + } + + __changeEventSpecsInput (e) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const id = e.target.getAttribute('data-resources') + const origValue = e.target.getAttribute('data-resources-val') + const origEl = e.target + let targ = document.getElementById(`orig-req-val-${id}`) + targ.innerHTML = `` + + let link = document.createElement('a') + link.href = '#' + link.setAttribute('onclick','event.preventDefault();') + link.textContent = 'cancel' + link.id = id + link.value = origValue + link.setAttribute('data-resources', id) + link.setAttribute('data-resources-val', origValue) + link.addEventListener('click', this.__cancelSpecsInput.bind(__proto)) + + e.target.replaceWith(link) + } + + __addOtherSpecsField () { + let targ = document.getElementById('specs-other-section') + + let sec = document.createElement('section') + + sec.innerHTML = ` + +
+ +
+
+ + + remove + +
+ +
+ ` + targ.append(sec) + } + + __loadAdditionalSettings() { + const __proto = Object.assign({__proto__: this.__proto__}, this) + // attach to DOM + setTimeout(() => { + const section = document.querySelector('.specs-section-proposal') + const sectionOthers = document.querySelector('#specs-other-section') + this.__autoComputeTotalAmount(this.opt.info.quantity) + + // original specs + this.opt.info.orig_specs.forEach((val, index) => { + let html = document.createElement('span') + html.classList.add('row', 'specs-input-section', 'specs-input-section-orig') + html.setAttribute('data-resources', this.opt.info.id) + html.setAttribute('style', 'margin-top: 15px;') + html.innerHTML = ` +
+ ${val.name} +
+
+ ${val.value} + +
+ + ` + + // change link + let btn = document.createElement('a') + btn.href = '#' + btn.setAttribute('onclick', 'event.preventDefault()') + btn.setAttribute('data-resources', val.id) + btn.setAttribute('data-resources-val', val.value) + btn.textContent = 'change' + btn.addEventListener('click', this.__changeEventSpecsInput.bind(__proto)) + + html.querySelector('.orig-req-menu').append(btn) + section.append(html) + }) + + // other specs + // specs + this.opt.info.other_specs.forEach((val, index) => { + let html = document.createElement('span') + html.classList.add('row', 'specs-input-section', 'specs-input-section-others') + html.setAttribute('data-resources', this.opt.info.id) + html.setAttribute('style', 'margin-top: 15px;') + html.innerHTML = ` +
+ +
+
+ + +
+ ` + let removeParentSpecs = document.createElement('a') + removeParentSpecs.id = val.id + removeParentSpecs.href = '#' + removeParentSpecs.textContent = 'remove' + removeParentSpecs.setAttribute('onclick', 'event.preventDefault();') + // tag specs to be removed + removeParentSpecs.addEventListener('click', (e) => { + e.target.parentNode.parentNode.parentNode.remove(); + let targ = document.querySelector('#specsToBeRemoved') + let specsToBeRemoved = [] + if (!targ) { + targ = document.createElement('input') + targ.id = 'specsToBeRemoved' + targ.type = 'hidden' + const sec = document.querySelector(`#file-attachment-main-dialog-${this.opt.id} > .body`) + sec.append(targ) + } + + if(targ.value.length > 1) { + specsToBeRemoved = JSON.parse(targ.value) + } + specsToBeRemoved.push({id:e.target.id, value: 'yes'}) + targ.value = JSON.stringify(specsToBeRemoved) + }) + + html.querySelector('.orig-req-menu').append(removeParentSpecs) + + sectionOthers.append(html) + }) + + // bind other specs + const otherSpecsBtn = document.querySelector('.add-other-specs-btn:not(.event-binded)') + if (otherSpecsBtn) { + otherSpecsBtn.classList.add('event-binded') + otherSpecsBtn.addEventListener('click', this.__addOtherSpecsField.bind(__proto)) + } + + //this.loadPopup() + }) + } + + render() { + this.template = document.createElement('section') + this.template.classList.add('row') + this.template.style.overflowY = 'auto' + + // custom classes + if(this.opt.class) this.template.classList.add(...this.opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + + + +
+
+
+ +
+
+
+

+

${this.opt.info.name || ''}

+ + +

+ Unit Price : + ${this.opt.info.currency || 'PHP'} + + + + +

+ +

+ Total Amount (Unit Price X Quantity) :
+ + + + This will be automatically filled out + + +

+ +

+ Discount (for all items) : + + + + +

+ +

Quantity : ${this.opt.info.quantity || ''} ${this.opt.info.unit || ''}

+ +

+
+ +
+

+

+ Specifications
+ Please fill up all the fields below + +

+
+ +
+ + +
+


+

+ + Others + (optional) + + + add_circle + +
+ + These are additional specifications not included in the original bidding request. + + +

+ +
+ +
+ + +
+


+

+ + Remarks + (optional) + +
+ + Add additional content in your proposal. Please make it short and simple + + + + + +

+ +
+ +
+ +
+ + +
+
` + //this.__bindListeners () + this.__loadAdditionalSettings() + // start rendering + return this.template + } +} + + + \ No newline at end of file diff --git a/src/components/proposal-menu/actions/attach.js b/src/components/proposal-menu/actions/attach.js new file mode 100644 index 0000000..2e93724 --- /dev/null +++ b/src/components/proposal-menu/actions/attach.js @@ -0,0 +1,194 @@ +import ApiConfig from '../../../config/api' + +const BiddingServ = import('../../../services/bidding-proposal-service') +const DropdownLoader = import('../../../utils/dropdown-loader') + +export default class { + constructor(opt) { + this.__apiConfig = ApiConfig + this.opt = opt + // hackish way to prevent the same ID + // spread operator failed so use Object + this.opt2 = Object.assign({}, opt) + this.opt2.id = '1-1' + this.bind() + } + + success() { + // close popup + document.getElementById('general-modal').close() + window.location.reload() + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + __appendFileToBeUploaded (e) { + const targ = document.querySelectorAll('.attachment-prop-pool-section') + + // close dialog + document.querySelector(`#file-attachment-main-dialog-${this.opt2.id}`).close() + + // preview + targ.forEach((el, index) => { + for (var i = 0; i < e.target.files.length; i++) { + // add to DOM + el.innerHTML+=` +
+
+ ${e.target.files[i].name} + arrow_drop_down +
+
+
+
+ ` + // add to file uploader + this.save(this.opt.id, e.target.files[i], i) + } + }) + + // dropdown + import('../../../utils/dropdown-loader').then(loader => loader.default('device-dropdown')) + } + + __bindSelectDeviceFile (opt) { + const el = document.querySelector(opt.selector) + const __proto = Object.assign({__proto__: this.__proto__}, this) + el.opt = opt + el.removeEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + el.addEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../../attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // append files + data.forEach((val ,index) => { + // class + val.class = "col-lg-12 col-12" + targ.append(new res.default(val)) + }) + }) + } + + save(id, file, index) { + //form data + const formData = new FormData() + formData.append('files', file) + formData.append('id', id) + formData.append('token', window.localStorage.getItem('token')) + + //XHR + let request = new XMLHttpRequest(); + request.open("POST", `${this.__apiConfig.url}/bidding/invitations/attachments/`); + + request.upload.addEventListener('progress', (e) => { + let targ = document.getElementById(`progress-bar-${index}`) + if (e.lengthComputable) { + let total = (e.total / e.loaded ) * 100 + targ.style.width=`${total}%` + } + }) + + request.addEventListener('load', (e) => { + + let targ = document.getElementById(`progress-bar-${index}`) + + if(request.responseText > 0) { + targ.parentNode.parentNode.remove() + // DOM + const __payload = { + id: request.responseText, + type: file.type.split('/')[1], + original_filename: file.name, + menus: ['remove'] + } + + this.loadAttachments('.recently-attached-prop-section', [__payload]) + // more settings + setTimeout(() => { + // dropdown and popup menu + DropdownLoader.then(loader => new loader.default('device-dropdown')) + import('../../../components/popup-es').then(loader => new loader.default()) + + },1000) + + }else{ + targ.parentNode.textContent = 'Error uploading' + } + }) + + request.send(formData); + + } + + async load () { + const dialog = (await import('../../dialog-pane')).default + + return new dialog(this.opt2).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#${res.id} > .body`) + sec.classList.add('col-lg-2') + sec.classList.remove('col-lg-6') + sec.innerHTML = '' + sec.parentNode.style.zIndex = 21 + sec.append(this.template) + + setTimeout(() => { + this.__bindSelectDeviceFile({ + selector: '#file-upload-attachment-prop-modal', + }) + + },1000) + + }) + } + + render () { + this.template = document.createElement('section') + this.template.id = "file-attachment-main-dialog-prop-modal" + this.template.classList.add('row') + + + // template + this.template.innerHTML = ` +
+
+

File Attachment navigate_next Select

+
+
+
+ cloud +
Upload files from your device
+ + +
+
+
+
` + } + + bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + const sel = root.querySelectorAll(`${this.opt.selector}:not(.event-binded)`) + sel.forEach((el, index) => { + el.classList.add('event-binded') + el.addEventListener('click', this.load.bind(proto)) + }) + } + +} \ No newline at end of file diff --git a/src/components/proposal-menu/actions/remove.js b/src/components/proposal-menu/actions/remove.js new file mode 100644 index 0000000..a6b3eca --- /dev/null +++ b/src/components/proposal-menu/actions/remove.js @@ -0,0 +1,60 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-proposal-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemoveBidding() + } + + success() { + // close popup + document.getElementById('general-modal').close() + window.location.reload() + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async removeBidding () { + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token : window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res) + return res == 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + loadRemoveBidding (e) { + import('../../remove-conf-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.removeBidding.bind(__proto)) + }) + } + + bindRemoveBidding () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click',this.loadRemoveBidding.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/proposal-menu/actions/request.js b/src/components/proposal-menu/actions/request.js new file mode 100644 index 0000000..e7cf482 --- /dev/null +++ b/src/components/proposal-menu/actions/request.js @@ -0,0 +1,66 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-proposal-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bind() + } + + success() { + // close popup + document.getElementById('general-modal').close() + window.location.reload() + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async send () { + const serve = (await BiddingServ).default + const reason = document.querySelector('textarea#reason').value + const __payload = { + id: this.opt.id, + action: 'change', + reason, + token : window.localStorage.getItem('token'), + } + + if(reason.length < 3) return + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res) + return res == 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + + + loadSend (e) { + import('../../request-new-quotation-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-send-button').addEventListener('click', this.send.bind(__proto)) + }) + } + + bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click',this.loadSend.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/proposal-menu/actions/send.js b/src/components/proposal-menu/actions/send.js new file mode 100644 index 0000000..273ef99 --- /dev/null +++ b/src/components/proposal-menu/actions/send.js @@ -0,0 +1,62 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-proposal-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bind() + } + + success() { + // close popup + document.getElementById('general-modal').close() + window.location.reload() + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async send () { + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'send', + token : window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res) + return res == 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + + + loadSend (e) { + import('../../send-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-send-button').addEventListener('click', this.send.bind(__proto)) + }) + } + + bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click',this.loadSend.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/proposal-menu/actions/update.js b/src/components/proposal-menu/actions/update.js new file mode 100644 index 0000000..e6600ba --- /dev/null +++ b/src/components/proposal-menu/actions/update.js @@ -0,0 +1,122 @@ +const BiddingServ = import('../../../services/bidding-proposal-service') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bind() + } + + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + /** + * Display a notice to attach their formal quotation before proceeding + * @param {Object} e - MouseEvent + * @returns XMLHttpRequest + */ + __loadProposalAttachmentsNotice () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const stat = document.querySelector('#bidding-status') + + import('../../../components/proposal-attachments-notice-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }).catch(err=>{ + console.log(err) + }) + + //const URL='pages/bidding/modal/proposal-attachments-notice.html' + + // show reload status + stat.innerHTML = '
This bidding requirement has been modified. Please reload this page to see changes reload
' + + // close form + document.querySelector('.file-attachment-main-dialog').close() + // allow attachment + /*let btn = document.getElementById('modal-dialog-send-button') + if (!document.querySelector('.file-prop-attachment-dialog-btn')) { + btn.classList.add('file-prop-attachment-dialog-btn') + } + // autoclose modal + btn.addEventListener('click', () => { + document.getElementById('bidding-modal').close() + }) + + window.bms.default.lazyLoad(['./assets/js_native/assets/js/modules/Invitation/Util/AttachmentsModal.js'])*/ + } + + /** + * @param {Object} e - MouseEvent + */ + async create(e) { + const __serv = (await BiddingServ).default + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const amount = document.getElementById('proposal-form-amount').value + const discount = document.getElementById('proposal-form-discount').value + const remarks = document.getElementById('proposal-form-remarks').value + let otherSpecsToBeRemoved = document.getElementById('specsToBeRemoved') + otherSpecsToBeRemoved = otherSpecsToBeRemoved ? otherSpecsToBeRemoved.value : JSON.stringify([]) + + let original = [] + let others = [] + + document.querySelectorAll('.specs-input-section-orig').forEach((el,index) => { + const val = el.querySelector('.specs-input-section-value') + if(val) { + const id = val.getAttribute('data-resources') + original.push({id, value: val.value }) + } + }) + + document.querySelectorAll('.specs-input-section-others').forEach((el, index) => { + const name = el.querySelector('.specs-input-section-name') + const val = el.querySelector('.specs-input-section-value') + if (val && name) { + const id = val.getAttribute('data-resources') + others.push({id, name: name.value, value: val.value}) + } + }) + + let __payload = { + id: this.opt.id, + amount, + discount, + remarks, + original, + others, + otherSpecsToBeRemoved : JSON.parse(otherSpecsToBeRemoved) , + token : window.localStorage.getItem('token'), + action: 'update', + } + + new __serv().create(__payload).then(res => { + return res ? this.__loadProposalAttachmentsNotice() : this.__showError() + }).catch(err => this.__showError() | console.log(err)) + } + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../../components/save-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.create.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bind() { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(`${this.opt.selector}:not(.event-binded)`) + el.forEach((els, index) => { + els.classList.add('event-binded') + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/proposal-menu/actions/view.js b/src/components/proposal-menu/actions/view.js new file mode 100644 index 0000000..bc15a27 --- /dev/null +++ b/src/components/proposal-menu/actions/view.js @@ -0,0 +1,86 @@ + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + loadPopup () { + + const popupes = import('../../popup-es') + const popupesStyle = import('../../popup-es/style') + + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + }) + + popupes.then(loader => { new loader.default() }) + + } + + loadDialogPane (e) { + e.preventDefault() + // dialog + let opt = Object.assign({}, this.opt) + opt.id = this.opt.dialogId + this.__getInfo(this.opt.id).then(res => { + import('../../proposal-form').then(loader => { + opt.info = res + return new loader.default(opt) + }).then(() => { + this.loadPopup() + import('./update').then(actions => { + return new actions.default({ + selector: '.save-bidding-modal-btn', + id: this.opt.id, + }) + }) + + }) + + + }) + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + import('../../../services/bidding-proposal-service').then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + + /** + *Bind + * + */ + __bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDialogPane.bind(proto)) + }) + } + +} \ No newline at end of file diff --git a/src/components/proposal-menu/index.js b/src/components/proposal-menu/index.js new file mode 100644 index 0000000..961a844 --- /dev/null +++ b/src/components/proposal-menu/index.js @@ -0,0 +1,151 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + + // bind remove action + /*if(opt.menus.indexOf('send')!=-1) { + import('./actions/send').then(loader => { + return new loader.default({ + root: this.template, + selector: '.send-bidding-modal-btn', + id: opt.id, + }) + }) + }*/ + + if(opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-prop-modal-btn', + id: opt.id, + }) + }) + } + + if(opt.menus.indexOf('attach')!=-1) { + import('./actions/attach').then(loader => { + return new loader.default({ + root: this.template, + selector: '.file-prop-attachment-dialog-btn', + id: opt.id, + target: 'body', + }) + }) + } + + import('./actions/view').then(loader => { + return new loader.default({ + root: this.template, + selector: '.proposal-reg-dialog-btn-update', + id: opt.id, + dialogId: '1-2', + target: '.bidding-section', + }) + }) + + if(opt.menus.indexOf('send')!=-1) { + import('./actions/send').then(loader => { + return new loader.default({ + root: this.template, + selector: '.send-bidding-modal-btn', + id: opt.id, + }) + }) + } + + if(opt.menus.indexOf('request_new')!=-1) { + import('./actions/request').then(loader => { + return new loader.default({ + root: this.template, + selector: '.resend-prop-modal-btn', + id: opt.id, + }) + }) + } + + } + + async render(opt) { + const ApiConfig = await import('../../config/api') + this.template = document.createElement('menu') + this.template.id = "proposal-menu" + this.template.classList.add('row') + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + opt.menus = opt.menus || [] + + // template + this.template.innerHTML = ` + + + ` + // event listeners + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/proposal-menu/style.styl b/src/components/proposal-menu/style.styl new file mode 100644 index 0000000..b366dcf --- /dev/null +++ b/src/components/proposal-menu/style.styl @@ -0,0 +1,11 @@ +#proposal-menu + background:#fff + position: relative + border: 1px solid rgba(200,200,200,.4) + font-size: 14px; + color: #444444 + margin-top: 0 + padding: 5px + + & a + color: #444 diff --git a/src/components/remove-conf-modal/index.js b/src/components/remove-conf-modal/index.js new file mode 100644 index 0000000..d2849d0 --- /dev/null +++ b/src/components/remove-conf-modal/index.js @@ -0,0 +1,15 @@ +import style from './style' + +const template = ` + +
+
+

Remove

+

This will be removed permanently in the system and could no be longer recover. Are you sure you want to continue?

+ + +
+
+` + +export { template } \ No newline at end of file diff --git a/src/components/remove-conf-modal/style.styl b/src/components/remove-conf-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/remove-conf-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/report-change-date-modal/index.js b/src/components/report-change-date-modal/index.js new file mode 100644 index 0000000..ef04572 --- /dev/null +++ b/src/components/report-change-date-modal/index.js @@ -0,0 +1,28 @@ +import style from './style' + +const template = ` +
+ +
+ date_range +

Select Date

+ Plese avoid date not more than 5 years as it will yield too much information and might take long to display +
+
+
+ From :
+ +
+ +
+ To :
+ +
+
+
+ + +
+` + +export { template } \ No newline at end of file diff --git a/src/components/report-change-date-modal/style.styl b/src/components/report-change-date-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/report-change-date-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/request-new-quotation-modal/index.js b/src/components/request-new-quotation-modal/index.js new file mode 100644 index 0000000..f2888e8 --- /dev/null +++ b/src/components/request-new-quotation-modal/index.js @@ -0,0 +1,26 @@ +import style from './style' + +const template = ` +
+ +

Request new proposal

+

This will notify the supplier that you are requesting for changes with their proposal

+ +

+ + * Required + +

+ + + +

+
+ + +
+
+ +` + +export { template } \ No newline at end of file diff --git a/src/components/request-new-quotation-modal/style.styl b/src/components/request-new-quotation-modal/style.styl new file mode 100644 index 0000000..ba1e432 --- /dev/null +++ b/src/components/request-new-quotation-modal/style.styl @@ -0,0 +1,17 @@ +#general-modal + & .content + min-width: 400px + width: 30% + left: 35% + +.send-req-modal-section + padding: 20px + + & textarea + &#reason + border: 1px solid #ccc !important + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px diff --git a/src/components/requirement-attachments-dialog/actions.js b/src/components/requirement-attachments-dialog/actions.js new file mode 100644 index 0000000..e9092a5 --- /dev/null +++ b/src/components/requirement-attachments-dialog/actions.js @@ -0,0 +1,229 @@ +import ApiConfig from '../../config/api' +const DropdownLoader = import('../../utils/dropdown-loader') + +export default class { + constructor (opt) { + this.opt = opt + this.__apiConfig = ApiConfig + this.filesToBeUploaded = {} + + } + + __showError () { + alert('Unable to process this request.Please try again later.') + } + + __appendFileToBeUploaded (e) { + const targ = document.querySelectorAll('.attachment-requirements-pool-section') + + // close dialog + document.querySelector('.file-attachment-main-dialog').classList.remove('open') + + // preview + targ.forEach((el, index) => { + for (var i = 0; i < e.target.files.length; i++) { + // add to DOM + el.innerHTML+=` +
+
+ ${e.target.files[i].name} + arrow_drop_down +
+
+
+
+ ` + // add to file uploader + this.save(e.target.opt.id, e.target.files[i], i) + console.log(e.target) + } + }) + + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + } + + __bindSelectDeviceFile (opt) { + const el = document.querySelector(opt.selector) + const __proto = Object.assign({__proto__: this.__proto__}, this) + el.opt = opt + el.removeEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + el.addEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + } + + __bindSelectFile () { + const el = document.querySelectorAll('.attachment-recent-checkbox-bidding') + const __proto = Object.assign({__proto__: this.__proto__}, this) + + for(let x of el) { + x.removeEventListener('change', this.__enableUploadButton.bind(__proto)) + x.addEventListener('change', this.__enableUploadButton.bind(__proto)) + } + } + + __enableUploadButton(e) { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const id = e.target.getAttribute('data-resources') + const oFilename = e.target.getAttribute('data-original-filename') + + if(id&&e.target.checked) { + this.filesToBeUploaded[id] = oFilename + }else{ + delete this.filesToBeUploaded[id] + } + + // enable button + setTimeout(() => { + Object.keys(this.filesToBeUploaded ).length > 0 ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', 'disabled') + },10) + + } + + + __showRecent (json) { + this.filesToBeUploaded = {} + + const html = document.createElement('div') + html.classList.add('recently-attached') + html.innerHTML = ` +
+ ${json.original_filename} + (${json.size}KB ${json.date_created})` + document.querySelector(`#recently-attached-section-${this.opt.id}`).append(html) + + } + + __bindAttach() { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const proto = Object.assign({__proto__: this.__proto__}, this) + btn.addEventListener('click', this.attach.bind(proto)) + } + + + async recent (opt) { + const __serv = (await import('../../services/bidding-req-attachment-service')).default + this.opt = this.opt || opt + // get from storage + return new __serv().recent(opt).then(data => { + data.data.forEach((val, index) => { + this.__showRecent(val) + }) + }).then(() => { + setTimeout(() => { + this.__bindSelectFile() + this. __bindAttach() + }, 800); + }) + } + + async attach (e) { + // disable button first + e.target.setAttribute('disabled', 'disabled') + + //payload + const __payload = { + attachments: this.filesToBeUploaded, + action: 'create', + id: this.opt.id, + token: window.localStorage.getItem('token') + } + + + const service = (await import('../../services/bidding-req-attachment-service')).default + return new service().attach(__payload).then(data => { + if (!data.data) return this._showError() + //show in DOM + for (var i = 0; i < data.data.length; i++) { + const __d = { + type: data.data[i].type, + original_filename: data.data[i].original_filename, + id: data.data[i].id, + menus: ['remove'] + } + this.loadAttachments('#attachments-requirements-info-section', [__d]) + } + // close dialog + document.querySelectorAll('.file-attachment-main-dialog').forEach((val, res) => val.close()) + + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + e.target.removeAttribute('disabled') + },1000) + }).catch(err => { + this._showError() + }) + } + + upload(opt) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + this.__bindSelectDeviceFile(opt) + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../attachments-item').then(res => { + const targ = document.querySelector(target) + console.log(targ) + if (!targ) return 0 + // append files + data.forEach((val ,index) => { + targ.append(new res.default(val)) + }) + }) + } + + + save(id,file,index) { + //form data + const formData = new FormData() + formData.append('files', file) + formData.append('id', id) + formData.append('token', window.localStorage.getItem('token')) + + //XHR + let request = new XMLHttpRequest(); + request.open("POST", `${this.__apiConfig.url}/bidding/requirements/attachments/`); + + request.upload.addEventListener('progress', (e) => { + let targ = document.getElementById(`progress-bar-${index}`) + if (e.lengthComputable) { + let total = (e.total / e.loaded ) * 100 + targ.style.width=`${total}%` + } + }) + + request.addEventListener('load', (e) => { + + let targ = document.getElementById(`progress-bar-${index}`) + + if(request.responseText > 0) { + targ.parentNode.parentNode.remove() + // DOM + const __payload = { + id: request.responseText, + type: file.type.split('/')[1], + original_filename: file.name, + menus: ['remove'] + } + + this.loadAttachments('#attachments-requirements-info-section', [__payload]) + // more settings + setTimeout(() => { + // dropdown and popup menu + DropdownLoader.then(loader => new loader.default('device-dropdown')) + import('../popup-es').then(loader => new loader.default()) + + },1000) + + }else{ + targ.parentNode.textContent = 'Error uploading' + } + }) + + request.send(formData); + + } +} \ No newline at end of file diff --git a/src/components/requirement-attachments-dialog/index.js b/src/components/requirement-attachments-dialog/index.js new file mode 100644 index 0000000..b93ce46 --- /dev/null +++ b/src/components/requirement-attachments-dialog/index.js @@ -0,0 +1,95 @@ + +export default class { + constructor(opt = {}) { + this.opt = opt + return this.loadDialog() + + } + + __bindListeners () { + this.template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`).addEventListener('click', () => { + document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).close() + }) + } + + async loadDialog () { + const dialog = (await import('../dialog-pane')).default + return new dialog(this.opt).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#${res.id} > .body`) + sec.innerHTML = '' + sec.append(this.template) + + setTimeout(() => { + // load actions + import('./actions').then(actions => { + const attachments = new actions.default().upload({ + id: this.opt.id, + selector: '#file-upload-attachment-bidding', + }) + + // get recent files + const recent = new actions.default().recent({ + id: this.opt.id, + page: 1, + token: window.localStorage.getItem('token'), + }) + + }) + + },1000) + + }) + } + + render() { + this.template = document.createElement('section') + this.template.classList.add('col-12', 'row') + + // custom classes + if(this.opt.class) this.template.classList.add(...this.opt.class.split(' ')) + + // template + this.template.innerHTML = ` + +
+
    +
  • +
    + computer Device +
    +
    +
  • +
  • Recent Attachments
  • +
  • + + + + +
  • +
+
+
+
+

Recently attached navigate_next Files


+
+
+
+
+   + +
+
+ ` + this.__bindListeners() + // start rendering + return this.template + } +} + + + \ No newline at end of file diff --git a/src/components/requirement-attachments-item/actions/remove.js b/src/components/requirement-attachments-item/actions/remove.js new file mode 100644 index 0000000..fea51d2 --- /dev/null +++ b/src/components/requirement-attachments-item/actions/remove.js @@ -0,0 +1,62 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-req-attachment-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bindRemoveAttachments() + } + + success(id) { + // close popup + document.getElementById('general-modal').close() + // remove from DOM + document.querySelector(`#attachments-info-section-${id}`).remove() + } + + error (err = '') { + alert('Unable to process this request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + async removeAttachments (e) { + + e.target.setAttribute('disabled', 'disabled') + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token: window.localStorage.getItem('token'), + } + + // remove from DB + return new serve().remove(__payload).then(json => { + return json.data == 1 ? this.success(__payload.id) : this.error() + }).catch(err => this.error(err)) + } + + loadRemoveAttachments (e) { + import('../../remove-conf-modal').then(res => { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.removeAttachments.bind(__proto)) + }) + } + + bindRemoveAttachments () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + root.querySelector(this.opt.selector).addEventListener('click', this.loadRemoveAttachments.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/requirement-attachments-item/index.js b/src/components/requirement-attachments-item/index.js new file mode 100644 index 0000000..b189da3 --- /dev/null +++ b/src/components/requirement-attachments-item/index.js @@ -0,0 +1,61 @@ +import style from './style' +import ApiConfig from '../../config/api' + +const fileIconCss = import('../../assets/css/fileicon.css') + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + // bind remove action + if(opt.menus.indexOf('remove')!=-1) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-attachments-modal', + id: opt.id + }) + }) + } + import('../popup-es').then(loader => new loader.default()) + } + + render(opt = {}) { + opt.menus = opt.menus || [] + this.template = document.createElement('section') + this.template.classList.add('col-lg-3', 'col-md-3') + this.template.id = `attachments-info-section-${opt.id}` + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + + // template + this.template.innerHTML = ` + +
+
+
${opt.original_filename} +
+
+ arrow_drop_down + +
+
+ ` + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/requirement-attachments-item/style.styl b/src/components/requirement-attachments-item/style.styl new file mode 100644 index 0000000..82908da --- /dev/null +++ b/src/components/requirement-attachments-item/style.styl @@ -0,0 +1,7 @@ +.attachment-items + padding: 5px + background: #505050 + border: 1px solid #fefefe + position: relative + color: #fff; + \ No newline at end of file diff --git a/src/components/requirement-award-modal/index.js b/src/components/requirement-award-modal/index.js new file mode 100644 index 0000000..76471a0 --- /dev/null +++ b/src/components/requirement-award-modal/index.js @@ -0,0 +1,18 @@ +import style from './style' + +const template = ` +
+ + +
+ +

Award

+

Officiay award this bidding to the selected proposal/supplier

+
+ + +
+
+
` + +export { template } \ No newline at end of file diff --git a/src/components/requirement-award-modal/style.styl b/src/components/requirement-award-modal/style.styl new file mode 100644 index 0000000..2de0929 --- /dev/null +++ b/src/components/requirement-award-modal/style.styl @@ -0,0 +1,37 @@ +#general-modal + & .content + min-width: 400px + width: 30% + left: 35% + + +.send-req-modal-section + & input#search-supplier + #remarks + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px + + +#award-remarks + height: 250px + border: 1px solid #ccc !important + + +.send-req-modal-section + position: relative + z-index: 0 + + &:after + content: '' + position: absolute + top: 10px + right: 0 + bottom: 0 + left: 0 + z-index: -1 + opacity: 0.15 diff --git a/src/components/requirement-awardees-item/actions/award.js b/src/components/requirement-awardees-item/actions/award.js new file mode 100644 index 0000000..b844d1f --- /dev/null +++ b/src/components/requirement-awardees-item/actions/award.js @@ -0,0 +1,58 @@ +const BiddingReqServ = import('../../../services/bidding-req-service') + + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = {} + this.sendingItems = {} + this.__timeout = {} + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + + async process(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + action: 'award', + token: window.localStorage.getItem('token'), + } + + e.target.disabled = 'disabled' + return new __serv().award(__payload).then(res => { + if(!parseInt(res)) return + if(parseInt(res) > 0) return window.location.reload() + this.__showError() + + }).catch(err => console.log(err)) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../requirement-award-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-awardees-item/index.js b/src/components/requirement-awardees-item/index.js new file mode 100644 index 0000000..94df450 --- /dev/null +++ b/src/components/requirement-awardees-item/index.js @@ -0,0 +1,236 @@ +import style from './style' +import styleRatings from '../general-style/star-ratings' + +export default class { + constructor(opt = {}) { + this.opt = opt + this.criteriaToBeSaved = {} + this.criteria = [{ + name: 'price', + alias: 'Price' + },{ + name: 'quality', + alias: 'Goods/ Service Quality' + },{ + name: 'time', + alias: 'Delivery Time' + }] + return this.render(opt) + } + + __listeners (opt) { + import('./actions/award').then(loader => { + return new loader.default({ + root: this.template, + selector: '.award-list-modal-btn', + id: opt.id, + }) + }) + } + + __saveRating (e) { console.log(this.opt) + // ctrl + enter + if (e.ctrlKey && e.target.value.length > 3) { + // all criteria must be rated + if(Object.keys(this.criteriaToBeSaved).length != this.criteria.length) return 0 + // disable form + e.target.disabled = 'disabled' + + // payload + const __payload = { + id: this.opt.bidding_requirements_id, + supplier_id: this.opt.id, + feedback: e.target.value, + ratings: this.criteriaToBeSaved, + token: window.localStorage.getItem('token'), + action: 'create' + } + // save feedback + import('../../services/bidding-req-service').then(res => { + + new res.default().sendFeedbackToAwardee(__payload).then(json => { + if(json > 1) window.location.reload() + }) + }) + + } + } + + __giveFeedback (opt) { + const uniqueId = new Date().getTime() + const __proto = Object.assign({__proto__ : this.__proto__}, this) + // rating's criteria + this.criteria.forEach((val, index) => { + const span = document.createElement('span') + span.innerHTML = `${val.alias}` + + for (let x = 0; x < 4; x++) { + const star = document.createElement('i') + star.classList.add('material-icons', 'md-18', 'star-ratings', `star-${val.name}`, `star-group-${uniqueId}`) + star.textContent = 'star_border' + star.position = x + star.criteria = val.name + star.addEventListener('click', this.rate.bind(__proto)) + span.append(star) + } + + span.append(document.createElement('br')) + this.template.querySelector('.rating-section').append(span) + }) + + + // feedback form + const textArea = document.createElement('textArea') + textArea.classList.add('form-control') + textArea.groupId = uniqueId + textArea.supplierId = opt.id + textArea.placeholder = 'Say something about your experience with this supplier' + textArea.setAttribute('style', 'border:1px solid #ccc !important;') + textArea.addEventListener('keyup', this.__saveRating.bind(__proto)) + + const saveProcedure = document.createElement('small') + saveProcedure.innerHTML = 'Press CTRL key + ENTER to save' + + // Insert to DOM + this.template.querySelector('.rating-feedback-section').append(textArea) + this.template.querySelector('.rating-feedback-section').append(saveProcedure) + } + + __getFeedbacks (opt) { + const reqServ = import('../../services/bidding-req-service') + + this.template.querySelectorAll('.feedback-list-section').forEach((el, index) => { + // id + const __payload = { + id: this.opt.id, + token: window.localStorage.getItem('token') + } + + // get from DB + reqServ.then(res => { + new res.default().getFeedbackAwardee(__payload).then(json => { + if (json.length > 0) { + el.innerHTML = '

What other say about this supplier expand_more

' + } + + + json.forEach((val, ind) => { + const nameAlias = val.author[0].profile_name.substr(0,2).toUpperCase() + const art = document.createElement('article') + + art.classList.add('row', 'col-12') + art.innerHTML+=` +
+
+
+
${nameAlias}
+
+

${val.author[0].profile_name}
+ ${val.author[0].department} +

+
+
+
+ +


+

${val.feedback}


+


+ ` + const rateSec = art.querySelector('.rate-section') + val.ratings.forEach((rateVal, rateIndex) => { + const span = document.createElement('span') + span.innerHTML = this.criteria[rateVal.name] || `${rateVal.name}` + span.innerHTML += ' ' + + for (let x = 0; x < 4; x++) { + const star = document.createElement('i') + star.classList.add('material-icons', 'md-18', 'star-ratings', `star-${rateVal.name}`) + + if (x < rateVal.value) { + star.textContent = 'star' + star.classList.add('active') + } else { + star.textContent = 'star_border' + } + + star.position = x + star.criteria = rateVal.name + span.append(star) + } + + span.append(document.createElement('br')) + + rateSec.append(span) + + }) + + el.append(art) + }) + }) + }) + }) + } + + rate (e) { + // copy element + const newEl = e.target.cloneNode() + newEl.textContent = 'star' + newEl.classList.add('active') + + e.target.parentNode.querySelectorAll(`.star-${e.target.criteria}`).forEach((el, index) => { + if (el.position > e.target.position) { + el.textContent = 'star_border' + el.classList.remove('active') + } else { + el.textContent = 'star' + el.classList.add('active') + this.criteriaToBeSaved[e.target.criteria] = e.target.position+1 + } + }) + } + + + render(opt = {}) { + this.template = document.createElement('div') + this.template.classList.add(`requirement-awardees-item-${opt.id}`, 'row') + this.__stat = '' + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + + +
+ ${opt.name} ${this.__stat} ${ + opt.status == 2 ? `star Awarded`: + ` + card_membership Click to Award + ` + }
+

+ +

+ +

${opt.remarks}

+ + +

+ insert_comment How will you rate this supplier ? + expand_more +

+
+
+


+
+ + + ` + this.__listeners(opt) + this.__giveFeedback(opt) + this.__getFeedbacks(opt) + // start rendering + return this.template + } +} diff --git a/src/components/requirement-awardees-item/style.styl b/src/components/requirement-awardees-item/style.styl new file mode 100644 index 0000000..1bd1c0a --- /dev/null +++ b/src/components/requirement-awardees-item/style.styl @@ -0,0 +1,3 @@ +.requirement-item + border-left: 2px solid green + padding-left:10px \ No newline at end of file diff --git a/src/components/requirement-invitation-advanced-modal/index.js b/src/components/requirement-invitation-advanced-modal/index.js new file mode 100644 index 0000000..6943503 --- /dev/null +++ b/src/components/requirement-invitation-advanced-modal/index.js @@ -0,0 +1,38 @@ +import style from './style' + +const template = ` + +
+

mail Send

+

Please select item(s) and supplier(s) below

+
+ +
+ Items / Services +
+
+
+
+ + +
+ Suppliers + + +
+ + +
+
+
+
+
+ +

+ +

Are you sure you want to continue?

+ + +
+` +export { template } \ No newline at end of file diff --git a/src/components/requirement-invitation-advanced-modal/style.styl b/src/components/requirement-invitation-advanced-modal/style.styl new file mode 100644 index 0000000..5deb47f --- /dev/null +++ b/src/components/requirement-invitation-advanced-modal/style.styl @@ -0,0 +1,21 @@ +#general-modal + & .content + min-width: 400px + width : 30% + left: 35% + + +.send-req-modal-section + padding: 20px + +.send-req-modal-section + & input + + &#search-supplier + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px \ No newline at end of file diff --git a/src/components/requirement-invitation-quick-modal/index.js b/src/components/requirement-invitation-quick-modal/index.js new file mode 100644 index 0000000..df9993b --- /dev/null +++ b/src/components/requirement-invitation-quick-modal/index.js @@ -0,0 +1,24 @@ +import style from './style' + +const template = ` + +
+

mail Send

+

Please select a supplier

+ + +
+ +
+
+
+ + +

+

Are you sure you want to continue?

+ + +
+ +` +export { template } \ No newline at end of file diff --git a/src/components/requirement-invitation-quick-modal/style.styl b/src/components/requirement-invitation-quick-modal/style.styl new file mode 100644 index 0000000..5deb47f --- /dev/null +++ b/src/components/requirement-invitation-quick-modal/style.styl @@ -0,0 +1,21 @@ +#general-modal + & .content + min-width: 400px + width : 30% + left: 35% + + +.send-req-modal-section + padding: 20px + +.send-req-modal-section + & input + + &#search-supplier + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px \ No newline at end of file diff --git a/src/components/requirement-menu/actions/attach.js b/src/components/requirement-menu/actions/attach.js new file mode 100644 index 0000000..61aaadc --- /dev/null +++ b/src/components/requirement-menu/actions/attach.js @@ -0,0 +1,71 @@ +import { showError } from '../../../pages/bidding-reg-form/actions'; + +const BiddingServ = import('../../../services/bidding-list-service') + +export default class { + constructor(opt){ + this.opt = opt + return this.bindAttach() + } + + hideSpinner () { + const targ = document.querySelector('#bids-info-container > .spinner') + if (targ) targ.hide() + } + + success() { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = ` +
+

Successfully Changed!

+

+ You can now print this request. This will close automatically after 5 seconds. +

+
+ ` + // close popup with delay + setTimeout(() => { + document.getElementById('general-modal').close() + },5000) + + } + + error (err = '') { + alert('Unable to process your request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + } + + + loadDialogPane () { + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#bids-info-container'}).then(t => t.template.show()) + }) + + return import('../../requirement-attachments-dialog').then(loader => { + return new loader.default({ + target: 'body', + id: this.opt.id + }) + }).then(() => { + this.hideSpinner() + }) + + } + + /** + *Bind + * + */ + bindAttach () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDialogPane.bind(proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-menu/actions/deadline.js b/src/components/requirement-menu/actions/deadline.js new file mode 100644 index 0000000..592e424 --- /dev/null +++ b/src/components/requirement-menu/actions/deadline.js @@ -0,0 +1,49 @@ +const BiddingReqServ = import('../../../services/bidding-req-service') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bindDeadline() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + async deadline(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + deadline: document.getElementById('deadline').value, + action: 'create', + token : window.localStorage.getItem('token') + } + + e.target.disabled = 'disabled' + + return new __serv().deadline(__payload).then(res => { + return parseInt(res) > 0 ? window.location.reload() : this.__showError() + }).catch(err => this.__showError()) + } + + loadDeadline (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../deadline-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.deadline.bind(__proto)) + }) + } + + __bindDeadline () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDeadline.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-menu/actions/invite_advanced.js b/src/components/requirement-menu/actions/invite_advanced.js new file mode 100644 index 0000000..8ff4bb3 --- /dev/null +++ b/src/components/requirement-menu/actions/invite_advanced.js @@ -0,0 +1,235 @@ +const BiddingReqServ = import('../../../services/bidding-req-service') +const SupplierServ = import('../../../services/supplier-service') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = {} + this.sendingItems = {} + this.__timeout = {} + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later. Make sure you have set the deadline before inviting a supplier') + } + + __removeFromSendingList (e) { + const resources = e.target.getAttribute('data-resources') + e.target.parentNode.parentNode.remove() + // unchecked item + document.querySelector(`.suppliers-send-check-list-${resources}`).checked = false + delete this.sendingList[resources] + + this.__toggleButton() + } + + __toggleButton () { + setTimeout(() => { + // enable send button + const __btn = document.getElementById('modal-dialog-send-button') + if ((Object.keys(this.sendingList).length) > 0) return __btn.removeAttribute('disabled') + __btn.setAttribute('disabled', 'disabled') + + }, 10) + } + + __checkSupplier (e) { + const __targ = document.querySelector('.suppliers-invitation-sending-list-section') + const __name = e.target.name + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // add + if (e.target.checked) { + const html = document.createElement('div') + html.classList.add('col-12', 'col-md-12', `list-${e.target.value}`) + html.style.fontSize = '12px' + html.style.marginTop = '3px' + html.innerHTML = ` +
+ ${__name} +
+ ` + + const removeBtn = document.createElement('span') + removeBtn.setAttribute('style', 'position:absolute;right:10px;top:0px;color:rgb(90,90,90);cursor:pointer;') + removeBtn.setAttribute('data-resources', e.target.value) + removeBtn.textContent = 'X' + removeBtn.addEventListener('click', this.__removeFromSendingList.bind(__proto)) + + html.querySelector('.suppliersName').append(removeBtn) + + __targ.append(html) + // add to virtual list + this.sendingList[e.target.value] = __name + + } else { + // remove + delete this.sendingList[e.target.value] + document.querySelector(`.list-${e.target.value}`).remove() + + } + + this.__toggleButton() + + } + + __appendSuppliersList (target, data, isEmpty = false) { + const __target = document.querySelector(target) + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + if(isEmpty) __target.innerHTML = '' + + data.forEach((val, index) => { + const __el = document.createElement('section') + __el.classList.add('row') + __el.innerHTML = `
+ +
+
${val.name}
` + __el.querySelector('input').addEventListener('change', this.__checkSupplier.bind(__proto)) + __target.append(__el) + + }) + } + + async __getSuppliersList (opt = {}) { + const __serv = (await SupplierServ).default + + return new __serv().list({page: opt.page || 1, filter: 'all'}).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data) + } + }).catch(err => console.log(err)) + } + + __checkItem (e) { + const name = e.target.itemName + // add + if (e.target.checked) { + // ad dto virtual list + this.sendingItems[e.target.value] = name + } else { + // remove + delete this.sendingItems[e.target.value] + } + } + + __appendItems (target, data, isEmpty = false) { + const __targ = document.querySelector(target) + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + data.forEach((val, index) => { + let html = document.createElement('details') + html.classList.add('col-12','row') + html.innerHTML = `${val.name}` + + val.requirements.forEach((req, index) => { + let htmls = document.createElement('div') + htmls.classList.add('row') + + // checkbutton + const checkBtn = document.createElement('input') + checkBtn.classList.add(`items-send-check-list-${req.id}`) + checkBtn.type = 'checkbox' + checkBtn.value = req.id + checkBtn.name = 'items-send-check-list' + checkBtn.addEventListener('click', this.__checkItem.bind(__proto)) + checkBtn.itemName = req.id + + htmls.innerHTML = ` +
+
+ ${req.name} +
` + htmls.querySelector('.checkBtn-section').append(checkBtn) + html.append(htmls) + }) + __targ.append(html) + }) + } + + async __getItems () { + const __serv = (await import('../../../services/bidding-list-service')).default + return new __serv().view({id: this.opt.bidding_id, token: this.opt.token}).then(res => { console.log(res) + if(!res.data) return + if(!res.data[0]) return + this.__appendItems('.item-sending-list-section', res.data[0].particulars, true) + }).catch(err => console.log(err)) + } + + async __searchSupplier (e) { + const __serv = (await SupplierServ).default + clearTimeout(this.__timeout) + + this.__timeout = setTimeout(() => { + if (!e.target.value.length) { + this.__getSuppliersList() + } + + return new __serv().search({ param: e.target.value }).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data, true) + } + }) + }, 1000) + + + } + + __bindSearchSupplier() { + // bind search + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + document.getElementById('search-supplier').addEventListener('keyup', this.__searchSupplier.bind(__proto)) + } + + + async process(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + suppliers: this.sendingList, + items: this.sendingItems, + action: Object.keys(this.sendingItems).length > 0 ? 'send_items' : 'send', + token: window.localStorage.getItem('token'), + } + const br = Object.values(this.sendingList) + let x = 0; + + e.target.disabled = 'disabled' + return new __serv().invite(__payload).then(res => { + if(res.data) { + for(let val in res.data) { + return window.location.reload() + } + } + return this.__showError () + }).catch(err => console.log(err) | this.__showError ()) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../requirement-invitation-advanced-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + this.__getSuppliersList() + this.__bindSearchSupplier() + this.__getItems() + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-menu/actions/invite_quick.js b/src/components/requirement-menu/actions/invite_quick.js new file mode 100644 index 0000000..03790ff --- /dev/null +++ b/src/components/requirement-menu/actions/invite_quick.js @@ -0,0 +1,179 @@ +const BiddingReqServ = import('../../../services/bidding-req-service') +const SupplierServ = import('../../../services/supplier-service') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = {} + this.sendingItems = {} + this.__timeout = {} + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later. Make sure you have set the deadline before inviting a supplier') + } + + __removeFromSendingList (e) { + const resources = e.target.getAttribute('data-resources') + e.target.parentNode.parentNode.remove() + // unchecked item + document.querySelector(`.suppliers-send-check-list-${resources}`).checked = false + delete this.sendingList[resources] + + this.__toggleButton() + } + + __toggleButton () { + setTimeout(() => { + // enable send button + const __btn = document.getElementById('modal-dialog-send-button') + if ((Object.keys(this.sendingList).length) > 0) return __btn.removeAttribute('disabled') + __btn.setAttribute('disabled', 'disabled') + + }, 10) + } + + __checkSupplier (e) { + const __targ = document.querySelector('.suppliers-invitation-sending-list-section') + const __name = e.target.name + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // add + if (e.target.checked) { + const html = document.createElement('div') + html.classList.add('col-12', 'col-md-12', `list-${e.target.value}`) + html.style.fontSize = '12px' + html.style.marginTop = '3px' + html.innerHTML = ` +
+ ${__name} +
+ ` + + const removeBtn = document.createElement('span') + removeBtn.setAttribute('style', 'position:absolute;right:10px;top:0px;color:rgb(90,90,90);cursor:pointer;') + removeBtn.setAttribute('data-resources', e.target.value) + removeBtn.textContent = 'X' + removeBtn.addEventListener('click', this.__removeFromSendingList.bind(__proto)) + + html.querySelector('.suppliersName').append(removeBtn) + + __targ.append(html) + // add to virtual list + this.sendingList[e.target.value] = __name + + } else { + // remove + delete this.sendingList[e.target.value] + document.querySelector(`.list-${e.target.value}`).remove() + + } + + this.__toggleButton() + + } + + __appendSuppliersList (target, data, isEmpty = false) { + const __target = document.querySelector(target) + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + if(isEmpty) __target.innerHTML = '' + + data.forEach((val, index) => { + const __el = document.createElement('section') + __el.classList.add('row') + __el.innerHTML = `
+ +
+
${val.name}
` + __el.querySelector('input').addEventListener('change', this.__checkSupplier.bind(__proto)) + __target.append(__el) + + }) + } + + async __getSuppliersList (opt = {}) { + const __serv = (await SupplierServ).default + + return new __serv().list({page: opt.page || 1, filter: 'all'}).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data) + } + }).catch(err => console.log(err)) + } + + async __searchSupplier (e) { + const __serv = (await SupplierServ).default + clearTimeout(this.__timeout) + + this.__timeout = setTimeout(() => { + if (!e.target.value.length) { + this.__getSuppliersList() + } + + return new __serv().search({ param: e.target.value }).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data, true) + } + }) + }, 1000) + + + } + + __bindSearchSupplier() { + // bind search + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + document.getElementById('search-supplier').addEventListener('keyup', this.__searchSupplier.bind(__proto)) + } + + + async process(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + suppliers: this.sendingList, + items: this.sendingItems, + action: Object.keys(this.sendingItems).length > 0 ? 'send_items' : 'send', + token: window.localStorage.getItem('token'), + } + const br = Object.values(this.sendingList) + let x = 0; + + e.target.disabled = 'disabled' + return new __serv().invite(__payload).then(res => { + if(res.data) { + for(let val in res.data) { + return window.location.reload() + } + } + return this.__showError() + }).catch(err => console.log(err) | this.__showError()) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../requirement-invitation-quick-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + this.__getSuppliersList() + this.__bindSearchSupplier() + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-menu/actions/winner.js b/src/components/requirement-menu/actions/winner.js new file mode 100644 index 0000000..e17aaf8 --- /dev/null +++ b/src/components/requirement-menu/actions/winner.js @@ -0,0 +1,216 @@ +const BiddingReqServ = import('../../../services/bidding-req-service') +const SupplierServ = import('../../../services/supplier-service') +const statusMessage = import('../../requirement-status') +const infoStatus = import('../../bidding-status') + +let globalSuppliers = {} +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = {} + this.sendingItems = {} + this.suppliers = {} + this.__timeout = {} + + return this.__bind() + } + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + __removeFromSendingList (e) { + const resources = e.target.getAttribute('data-resources') + e.target.parentNode.parentNode.remove() + // unchecked item + document.querySelector(`.suppliers-send-check-list-${resources}`).checked = false + delete this.sendingList[resources] + + this.__toggleButton() + } + + __toggleButton () { + setTimeout(() => { + // enable send button + const __btn = document.getElementById('modal-dialog-send-button') + if ((Object.keys(this.sendingList).length) > 0) return __btn.removeAttribute('disabled') + __btn.setAttribute('disabled', 'disabled') + + }, 10) + } + + __checkSupplier (e) { + const __targ = document.querySelector('.suppliers-invitation-sending-list-section') + const __name = e.target.getAttribute('data-suppliers-name') + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // add + if (e.target.checked) { + const html = document.createElement('div') + html.classList.add('col-12', 'col-md-12', `list-${e.target.value}`) + html.style.fontSize = '12px' + html.style.marginTop = '3px' + html.innerHTML = ` +
+ ${__name} +
+ ` + + const removeBtn = document.createElement('span') + removeBtn.setAttribute('style', 'position:absolute;right:10px;top:0px;color:rgb(90,90,90);cursor:pointer;') + removeBtn.setAttribute('data-resources', e.target.value) + removeBtn.textContent = 'X' + removeBtn.addEventListener('click', this.__removeFromSendingList.bind(__proto)) + + html.querySelector('.suppliersName').append(removeBtn) + + __targ.innerHTML = '' + __targ.append(html) + // add to virtual list + this.sendingList = {} + this.sendingList[e.target.value] = __name + globalSuppliers = this.sendingList + + + } else { + // remove + // delete this.sendingList[e.target.value] + //document.querySelector(`.list-${e.target.value}`).remove() + + } + + this.__toggleButton() + + } + + __appendSuppliersList (target, data, isEmpty = false) { + const __target = document.querySelector(target) + const __proto = Object.assign({__proto__: this.__proto__ }, this) + + if(isEmpty) __target.innerHTML = '' + + data.forEach((val, index) => { + const __el = document.createElement('section') + __el.classList.add('row') + __el.innerHTML = `
+ +
+
${val.name}
` + __el.querySelector('input').addEventListener('change', this.__checkSupplier.bind(__proto)) + __target.append(__el) + + }) + } + + async __getSuppliersList (opt = {}) { + const __serv = (await SupplierServ).default + + return new __serv().list({page: opt.page || 1, filter: 'all'}).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data) + } + }).catch(err => console.log(err)) + } + + async __searchSupplier (e) { + const __serv = (await SupplierServ).default + clearTimeout(this.__timeout) + + this.__timeout = setTimeout(() => { + if (!e.target.value.length) { + this.__getSuppliersList() + } + + return new __serv().search({ param: e.target.value }).then(res => { + if(res.data) { + this.__appendSuppliersList('.suppliers-invitation-check-list-section', res.data, true) + } + }) + }, 1000) + + + } + + __bindSearchSupplier() { + // bind search + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + document.getElementById('search-supplier').addEventListener('keyup', this.__searchSupplier.bind(__proto)) + } + + /** + * Display notification to attach bidding resolution after they awarded a supplier. + * @param {Object} e - MouseEvent + * @returns XMLHttpRequest + */ + loadAwardRequirementsAttachmentsNotice () { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../award-attachments-notice-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', () => { + document.getElementById('general-modal').close() + // click attachment button + document.querySelector('.file-attachment-requirement-dialog-btn').click() + }) + + statusMessage.then(loader => { + const __targ = document.querySelector('#requirement-menu-status') + __targ.innerHTML = '' + infoStatus.then(stat => { + return new stat.default(loader.showModifiedStatus()).then(html => __targ.append(html)) + }) + + }) + }) + } + + + + async process(e) { + const __serv = (await BiddingReqServ).default + const __payload = { + id: this.opt.id, + suppliers: globalSuppliers, + remarks: document.getElementById('remarks').value, + action: 'winner', + token: window.localStorage.getItem('token') + } + + const br = Object.values(globalSuppliers) + let x = 0; + + e.target.disabled = 'disabled' + return new __serv().winner(__payload).then(res => { + if(!res) return this.__showError () + if(parseInt(res) > 0) { + return this.loadAwardRequirementsAttachmentsNotice() + } + }).catch(err => console.log(err)) + + } + + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../winner-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + this.__getSuppliersList() + this.__bindSearchSupplier() + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.process.bind(__proto)) + }) + } + + + __bind () { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/components/requirement-menu/index.js b/src/components/requirement-menu/index.js new file mode 100644 index 0000000..449328b --- /dev/null +++ b/src/components/requirement-menu/index.js @@ -0,0 +1,142 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + // deadline + if(opt.menus.indexOf('deadline')!=-1) { + import('./actions/deadline').then(loader => { + return new loader.default({ + root: this.template, + selector: '.set-deadline-modal-btn', + id: opt.id, + }) + }) + } + + // invitation (QUICK) + if(opt.menus.indexOf('invite')!=-1) { + import('./actions/invite_quick').then(loader => { + return new loader.default({ + root: this.template, + selector: '.send-requirements-modal-btn', + id: opt.id, + }) + }) + + import('./actions/invite_advanced').then(loader => { + return new loader.default({ + root: this.template, + selector: '.send-requirements-selected-modal-btn', + id: opt.id, + bidding_id: opt.bidding_id, + }) + }) + } + + // deadline + if(opt.menus.indexOf('winner')!=-1) { + import('./actions/winner').then(loader => { + return new loader.default({ + root: this.template, + selector: '.award-requirements-modal-btn', + id: opt.id, + }) + }) + } + + // deadline + if(opt.menus.indexOf('attach')!=-1) { + import('./actions/attach').then(loader => { + return new loader.default({ + root: this.template, + selector: '.file-attachment-requirement-dialog-btn', + id: opt.id, + }) + }) + } + } + + async render(opt) { + const ApiConfig = await import('../../config/api') + this.template = document.createElement('menu') + this.template.id = "requirement-info-menu" + this.template.classList.add('row') + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + opt.menus = opt.menus || [] + + // template + this.template.innerHTML = ` + + + ` + // event listeners + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/requirement-menu/style.styl b/src/components/requirement-menu/style.styl new file mode 100644 index 0000000..9f52e00 --- /dev/null +++ b/src/components/requirement-menu/style.styl @@ -0,0 +1,11 @@ +#requirement-info-menu + background:#fff + position: relative + box-shadow: 0 0 10px rgba(200,200,200,.4) + font-size: 14px; + color: #444444 + margin-top: 0 + padding: 5px + + & a + color: #444 diff --git a/src/components/requirement-recepient-item/index.js b/src/components/requirement-recepient-item/index.js new file mode 100644 index 0000000..3fab8d2 --- /dev/null +++ b/src/components/requirement-recepient-item/index.js @@ -0,0 +1,49 @@ +import ApiConfig from '../../config/api' + +const fileIconCss = import('../../assets/css/fileicon.css') + +export default class { + constructor(opt = {}) { + return this.render(opt) + } + + bindListeners (opt) { + import('../popup-es').then(loader => new loader.default()) + } + + render(opt = {}) { + opt.menus = opt.menus || [] + this.template = document.createElement('section') + this.template.classList.add('col-lg-4', 'col-md-4') + this.template.id = `requirement-recepient-item-${opt.id}` + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + + // template + this.template.innerHTML = ` + +
+
+
${opt.name}
+
+ +
+ arrow_drop_down + +
+
+ + ` + this.bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/requirement-status/awarded_style.styl b/src/components/requirement-status/awarded_style.styl new file mode 100644 index 0000000..1dcb688 --- /dev/null +++ b/src/components/requirement-status/awarded_style.styl @@ -0,0 +1,16 @@ +.congrats-banner + position: relative + z-index: 0 + +.congrats-banner:after + content: '' + position: absolute + top: 0 + right: 0 + bottom: 0 + left: 0 + z-index: -1 + opacity: 0.15 + +#bidding-status + background: #464a4e \ No newline at end of file diff --git a/src/components/requirement-status/index.js b/src/components/requirement-status/index.js new file mode 100644 index 0000000..4c205b3 --- /dev/null +++ b/src/components/requirement-status/index.js @@ -0,0 +1,39 @@ +/** + * Bidding Status Code + * Please see https://github.com/SEARCAPhil/bms_api/blob/develop/docs/bidding/status.md + * + * 0 Draft - Newly created bidding request + * 1 Sent - Bidding request was received by CBA Assts + * 2 Returned - Bidding request was returned to the creator for modifications + * 3 Approved - Bidding is on progress + * 4 Deleted - Removed from the system (soft delete) + * 5 Closed - Bidding request is successful + * 6 Failed - Bidding failed for whatever reason + */ +import awardedStyle from './awarded_style' + +/** + * Awarded + * For all accounts + * + * @function showAwardedStatus + */ +const showAwardedStatus = (id) => { + return { + id, + icon: ``, + message: '

This has been awarded. Please review before making any changes.

', + actions: ` ` + } +} + +const showModifiedStatus = (id) => { + return { + id, + icon: ` refresh`, + message: '

This bidding requirement has been modified. Please reload this page to see changes

', + actions: ` reload ` + } +} + +export { showAwardedStatus, showModifiedStatus } \ No newline at end of file diff --git a/src/components/requirements-item/actions/remove.js b/src/components/requirements-item/actions/remove.js new file mode 100644 index 0000000..0a667f8 --- /dev/null +++ b/src/components/requirements-item/actions/remove.js @@ -0,0 +1,72 @@ +const BiddingServ = import('../../../services/bidding-req-service') + +export default class { + constructor(opt) { + this.opt = opt + this.bind() + } + + hideSpinner () { + const targ = document.querySelector('#bids-info-container > .spinner') + if (targ) targ.hide() + } + + success() { + const root = this.opt.root || document + // close popup + document.getElementById('general-modal').close() + root.querySelector(this.opt.selector).parentNode.parentNode.remove() + this.hideSpinner() + } + + error (err = '') { + alert('Unable to remove this bidding request! Please try again later') + // close popup + document.getElementById('general-modal').close() + console.log(err) + this.hideSpinner() + } + + async remove () { + const serve = (await BiddingServ).default + const __payload = { + id: this.opt.id, + action: 'remove', + token : window.localStorage.getItem('token'), + } + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#bids-info-container'}).then(t => t.template.show()) + }) + + // remove from DB + return new serve().remove(__payload).then(res => { console.log(res) + return res.data === 1 ? this.success() : this.error() + }).catch(err => this.error(err)) + } + + load (e) { + import('../../remove-conf-modal').then(res => { + + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + // DOM + const __target = document.querySelector('#general-modal > .content > .body') + + __target.innerHTML = res.template + // close button + __target.querySelector('#modal-dialog-close-button').addEventListener('click', document.querySelector('#general-modal').close) + + // remove button + document.getElementById('modal-dialog-remove-button').addEventListener('click', this.remove.bind(__proto)) + }) + } + + bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const root = this.opt.root || document + const sel = root.querySelector(this.opt.selector) + if(sel) sel.addEventListener('click',this.load.bind(proto)) + } + +} \ No newline at end of file diff --git a/src/components/requirements-item/index.js b/src/components/requirements-item/index.js new file mode 100644 index 0000000..36759a1 --- /dev/null +++ b/src/components/requirements-item/index.js @@ -0,0 +1,52 @@ +import style from './style' + +export default class { + constructor(opt = {}) { + this.opt = opt + return this.render(opt) + } + + __bindListeners (opt) { + import('./actions/remove').then(loader => { + return new loader.default({ + root: this.template, + selector: '.remove-requirements-modal-btn', + id: opt.id, + }) + }) + } + + render(opt = {}) { + this.template = document.createElement('div') + this.template.classList.add('requirement-item') + this.__funds = opt.funds.map(val => `${val.fund_type} - ${val.cost_center} - ${val.line_item}`) + + // custom classes + if(opt.class) this.template.classList.add(...opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + ${opt.name} + (${opt.quantity} ${opt.unit}) + ${this.__funds} + + ${opt.awarded ? ` + star + Awarded + ` : ''} + + + ${opt.menus['update'] ? `edit` : '' } + ${opt.menus['remove'] ? ` + remove_circle_outline +  ` : '' } + + + ${opt.currency} ${opt.amount} + ` + this.__bindListeners(opt) + // start rendering + return this.template + } +} diff --git a/src/components/requirements-item/style.styl b/src/components/requirements-item/style.styl new file mode 100644 index 0000000..6e20102 --- /dev/null +++ b/src/components/requirements-item/style.styl @@ -0,0 +1,4 @@ +.requirement-item + border-left: 2px solid green + padding-left:10px + margin-bottom: 10px \ No newline at end of file diff --git a/src/components/save-modal/index.js b/src/components/save-modal/index.js new file mode 100644 index 0000000..1e73888 --- /dev/null +++ b/src/components/save-modal/index.js @@ -0,0 +1,16 @@ +import style from './style' + +const template = ` +
+ +
+

save Save

+

Please review the details before proceeding. Are you sure you want to continue?

+ + + +
+
+` + +export { template } \ No newline at end of file diff --git a/src/components/save-modal/style.styl b/src/components/save-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/save-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/send-modal/index.js b/src/components/send-modal/index.js new file mode 100644 index 0000000..c6fcc85 --- /dev/null +++ b/src/components/send-modal/index.js @@ -0,0 +1,15 @@ +import style from './style' + +const template = ` +
+ +
+

Send

+

You will not be able to update the content of this item after you send it to the recepient. Are you sure you want to continue?

+ + +
+
+` + +export { template } \ No newline at end of file diff --git a/src/components/send-modal/style.styl b/src/components/send-modal/style.styl new file mode 100644 index 0000000..16b1c70 --- /dev/null +++ b/src/components/send-modal/style.styl @@ -0,0 +1,2 @@ +.remove-modal-section + padding: 20px diff --git a/src/components/send-to-cba-modal/index.js b/src/components/send-to-cba-modal/index.js new file mode 100644 index 0000000..03b9a91 --- /dev/null +++ b/src/components/send-to-cba-modal/index.js @@ -0,0 +1,27 @@ +import style from './style' + +const template = ` +
+ +

Send

+ +

Please review before sending. You will not be able to make any changes after you send it to the recepient. + Only proceed if you know what you are doing. +

+
+
+ +
+ +
+
+
+ + +

+

Are you sure you want to continue?

+ + +
` + + export { template } \ No newline at end of file diff --git a/src/components/send-to-cba-modal/style.styl b/src/components/send-to-cba-modal/style.styl new file mode 100644 index 0000000..ba8ca0b --- /dev/null +++ b/src/components/send-to-cba-modal/style.styl @@ -0,0 +1,22 @@ +#general-modal + & .content + min-width: 400px + width: 30% + left: 35% + +.send-req-modal-section + padding: 20px + + +.send-req-modal-section + + & input + + & #search-supplier + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px \ No newline at end of file diff --git a/src/pages/bidding/modal/signatories.html b/src/components/sign-conf-modal/index.js similarity index 90% rename from src/pages/bidding/modal/signatories.html rename to src/components/sign-conf-modal/index.js index a3cf8ee..abc7ba2 100644 --- a/src/pages/bidding/modal/signatories.html +++ b/src/components/sign-conf-modal/index.js @@ -1,21 +1,9 @@ -
+const template = ` + +

Change Signatories

Please review the original bidding request bidding report before doing any changes.

@@ -24,7 +12,6 @@

Change Signatories

Requested By

Leave blank to use the default value

-


@@ -33,7 +20,6 @@

Change Signatories

Recommended By

Leave blank to use the default value

-


@@ -42,16 +28,11 @@

Change Signatories

Approved By

Leave blank to use the default value

-


- - - - -
-
\ No newline at end of file +
+` + +export { template } \ No newline at end of file diff --git a/src/components/sign-conf-modal/style.styl b/src/components/sign-conf-modal/style.styl new file mode 100644 index 0000000..f4d7eb0 --- /dev/null +++ b/src/components/sign-conf-modal/style.styl @@ -0,0 +1,18 @@ +#general-modal + & .content + width: 30% + left: 35% + +#bidding-modal + & .content + min-width: 400px + width : 30% + left: 35% + +.deadline-modal-section + padding: 20px + +.deadline-modal-section + & input + border: 1px solid #ccc !important + background: #f9f9f9 !important \ No newline at end of file diff --git a/src/components/status-inv-message/index.js b/src/components/status-inv-message/index.js new file mode 100644 index 0000000..3e2c35a --- /dev/null +++ b/src/components/status-inv-message/index.js @@ -0,0 +1,28 @@ +/** + * Bidding Status Code + * Please see https://github.com/SEARCAPhil/bms_api/blob/develop/docs/bidding/status.md + * + * 0 Draft - Newly created bidding request + * 1 Sent - Bidding request was received by CBA Assts + * 2 Returned - Bidding request was returned to the creator for modifications + * 3 Approved - Bidding is on progress + * 4 Deleted - Removed from the system (soft delete) + * 5 Closed - Bidding request is successful + * 6 Failed - Bidding failed for whatever reason + */ + +/** + * @function showInitial + */ +const showInitial = (id) => { + return { + id, + icon: 'mail', + message: ' Hooray! You are invited to bid on this product. Please review the details before sending a proposal', + actions: `` + } +} + + + +export { showInitial } \ No newline at end of file diff --git a/src/components/status-message/index.js b/src/components/status-message/index.js new file mode 100644 index 0000000..591d0a7 --- /dev/null +++ b/src/components/status-message/index.js @@ -0,0 +1,142 @@ +/** + * Bidding Status Code + * Please see https://github.com/SEARCAPhil/bms_api/blob/develop/docs/bidding/status.md + * + * 0 Draft - Newly created bidding request + * 1 Sent - Bidding request was received by CBA Assts + * 2 Returned - Bidding request was returned to the creator for modifications + * 3 Approved - Bidding is on progress + * 4 Deleted - Removed from the system (soft delete) + * 5 Closed - Bidding request is successful + * 6 Failed - Bidding failed for whatever reason + */ + +/** + * @function showBiddingReqSent + */ +const showBiddingReqSent = (id) => { + return { + id, + icon: 'check_circle', + message: 'Bidding Request Sent. You Are not able to modify the content of this bidding request.
email Waiting for verification', + actions: `` + } +} + + +/** + * Shows when the request IS FOR APPROVAL + * + * @function showBiddingReqApprove + */ +const showBiddingReqApprove = (id) => { + return { + id, + icon: 'bookmark', + message: 'This Bidding request is for approval. Make sure you review this request before making any further actions.', + actions: `` + } +} + + +/** + * @function showBiddingReqReturned + */ +const showBiddingReqReturned = (id) => { + return { + id, + icon: 'keyboard_return', + message: 'This Bidding request was returned. Make sure that all details are complete and follows the bidding request standard procedures', + actions: `` + } +} + + +/** + * @function showBiddingClosed + */ +const showBiddingClosed = (id) => { + return { + id, + icon: 'block', + message: 'Sorry ! Bidding for this item is already closed
You are not able to add or modify any content under this item', + actions: `` + } +} + + +/** + * @function showBiddingFailed + */ +const showBiddingFailed = (id) => { + return { + id, + icon: 'lock', + message: 'This bidding request has been closed due to failure of bidding. For more info, please contact the Administrator', + actions: `` + } +} + + +/** + * Shows when the request was APPROVED + * + * @function showBiddingApprove + */ +const showBiddingApproved = (id) => { + return { + id, + icon: 'check_circle', + message: 'This Bidding request was approved. You may now close this bidding request', + actions: ` + + + + + ` + } +} + +/** + * @function showBiddingDisapproved + */ +const showBiddingDisapproved = (id) => { + return { + id, + icon: 'trash', + message: ' This Bidding has been disapproved! This bidding request is now closed', + actions: `lock` + } +} + +/** + * Shows when the request was APPROVED + * This is when the current role is GSU + * + * @function showBiddingApproveReadOnly + */ +const showBiddingApproveReadOnly = (id) => { + return { + id, + icon: 'check_circle', + message: 'This Bidding request was approved. You may now invite suppliers to bid on this request', + actions: ` ` + } +} + +/** + * Shows when the request was APPROVED + * For non-admin access + * + * @function showBiddingApproveReadOnlyStandard + */ +const showBiddingApproveReadOnlyStandard = (id) => { + return { + id, + icon: 'check_circle', + message: 'This Bidding request was approved and reviewed.', + actions: ` ` + } +} + +export { showBiddingReqSent, showBiddingReqApprove, showBiddingReqReturned, showBiddingClosed, showBiddingFailed, showBiddingDisapproved, showBiddingApproveReadOnly, showBiddingApproveReadOnlyStandard, showBiddingApproved } \ No newline at end of file diff --git a/src/pages/bidding/modal/winner.html b/src/components/winner-modal/index.js similarity index 65% rename from src/pages/bidding/modal/winner.html rename to src/components/winner-modal/index.js index 9ce248c..dfae851 100644 --- a/src/pages/bidding/modal/winner.html +++ b/src/components/winner-modal/index.js @@ -1,29 +1,9 @@ - -
-
+const template = ` +
+ +

Winner

Tag the selected supplier(s) as winning bidder(s)

@@ -66,5 +46,6 @@

Winner



+` - \ No newline at end of file +export { template } \ No newline at end of file diff --git a/src/components/winner-modal/style.styl b/src/components/winner-modal/style.styl new file mode 100644 index 0000000..ef82186 --- /dev/null +++ b/src/components/winner-modal/style.styl @@ -0,0 +1,20 @@ +#general-modal + & .content + min-width: 300px + width: 25% + left: 32.5% + +.send-req-modal-section + & input#search-supplier + #remarks + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px + + +#remarks + height: 250px \ No newline at end of file diff --git a/src/components/winner-proposal-modal/index.js b/src/components/winner-proposal-modal/index.js new file mode 100644 index 0000000..57ce80f --- /dev/null +++ b/src/components/winner-proposal-modal/index.js @@ -0,0 +1,26 @@ +import style from './style' + +const template = ` +
+ +
+

Winner

+ +

This proposal will be tagged as the official winner for this bidding.

+
+ +
+
+ Remarks
+ +
+

+
+
+

Are you sure you want to continue?

+ + +
+
` + +export { template } \ No newline at end of file diff --git a/src/components/winner-proposal-modal/style.styl b/src/components/winner-proposal-modal/style.styl new file mode 100644 index 0000000..88deb12 --- /dev/null +++ b/src/components/winner-proposal-modal/style.styl @@ -0,0 +1,21 @@ +#general-modal + & .content + min-width: 400px + width: 30% + left: 35% + +.send-req-modal-section + & input#search-supplier + #remarks + border: 1px solid #ccc !important + + +.suppliers-invitation-sending-list-section + background: #f0f0f0 + margin-top: 10px + padding: 5px + + +#award-remarks + height: 250px + border: 1px solid #ccc !important \ No newline at end of file diff --git a/src/config/adal.js b/src/config/adal.js new file mode 100644 index 0000000..ef12b55 --- /dev/null +++ b/src/config/adal.js @@ -0,0 +1,10 @@ +export default { + instance: 'https://login.microsoftonline.com/', + tenant: 'common', //COMMON OR YOUR TENANT ID + clientId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', //This is your client ID + redirectUri: `http://localhost/bms_rethink/www/auth.html`, //This is your redirect URI + cacheLocation: 'localStorage', + // callback: userSignedIn, + popUp: true, + endpoints : {"https://graph.microsoft.com": "https://graph.microsoft.com"}, +} \ No newline at end of file diff --git a/src/config/api.js b/src/config/api.js new file mode 100644 index 0000000..d3def28 --- /dev/null +++ b/src/config/api.js @@ -0,0 +1,9 @@ +const settings = { + protocol: 'http', + host: 'localhost', + port: null, + domain:'bms_api/src/api', +} +settings.url = `${settings.protocol}://${settings.host}${settings.port ? ':this.port' : ''}/${settings.domain}` + +export default settings \ No newline at end of file diff --git a/src/electron/main.js b/src/electron/main.js deleted file mode 100644 index cabffdb..0000000 --- a/src/electron/main.js +++ /dev/null @@ -1,60 +0,0 @@ -const {app, BrowserWindow} = require('electron') -const path = require('path') -const url = require('url') - -// Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. -let win -let tray = null - -function createWindow () { - // Create the browser window. - win = new BrowserWindow({width: 800, height: 600}) - - - - // and load the index.html of the app. - win.loadURL(url.format({ - pathname: path.join(__dirname, '../../www/index.html'), - protocol: 'file:', - slashes: true, - title: 'Bidding Management System', - icon: path.join(__dirname,'../../www/assets/img/icon.ico') - })) - - - - - // Emitted when the window is closed. - win.on('closed', () => { - // Dereference the window object, usually you would store windows - // in an array if your app supports multi windows, this is the time - // when you should delete the corresponding element. - win = null - }) -} - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on('ready', createWindow) - -// Quit when all windows are closed. -app.on('window-all-closed', () => { - // On macOS it is common for applications and their menu bar - // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== 'darwin') { - app.quit() - } -}) - -app.on('activate', () => { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (win === null) { - createWindow() - } -}) - -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and require them here. \ No newline at end of file diff --git a/src/gulpfile.js b/src/gulpfile.js deleted file mode 100644 index 201b529..0000000 --- a/src/gulpfile.js +++ /dev/null @@ -1,158 +0,0 @@ -const gulp=require('gulp') -const uglify=require('gulp-uglify') -const uglify_css=require('gulp-clean-css') -const uglifyes=require('gulp-uglifyes') -const htmlmin=require('gulp-htmlmin') -const babelify = require('babelify') -const browserify = require("browserify") -const source=require('vinyl-source-stream') -const buffer=require('vinyl-buffer') -const glob=require('glob') -const runSequence = require('run-sequence') -const wbBuild = require('workbox-build'); - - - - -gulp.task('minifying html',()=>{ - gulp.src(['./**/*.html']) - .pipe(htmlmin({collapseWhitespace: true,minifyCSS: true,removeComments: true})) - .pipe(gulp.dest('../www')) -}) - - -gulp.task('minifying css',()=>{ - gulp.src(['./assets/css/**/*.css']) - .pipe(uglify_css()) - .pipe(gulp.dest('../www/assets/css')) -}) - - -gulp.task('copying fonts',()=>{ - gulp.src(['./assets/fonts/*']) - .pipe(gulp.dest('../www/assets/fonts')) -}) -gulp.task('copying images',()=>{ - gulp.src(['./assets/img/*']) - .pipe(gulp.dest('../www/assets/img')) -}) - -gulp.task('copying manifest',()=>{ - gulp.src(['./manifest.json']) - .pipe(gulp.dest('../www')) -}) - -gulp.task('copying js to js_native folder',()=>{ - glob('./assets/js/**/*.js',function(err,file){ - if(err) done(err) - - var file=file.map((entry)=>{ - return browserify({ - entries: [entry], - }) - .transform(babelify.configure({ - presets : ["es2015"] - })) - .bundle() - .pipe(source(entry)) - .pipe(buffer()) - .pipe(uglify()) - .pipe(gulp.dest('../www/assets/js_native')) - }) - - }) -}) - -gulp.task('minifying modules and saving to js_es',()=>{ - gulp.src(['./assets/js/**/*.js']) - .pipe(uglifyes({ - warnings:true, - mangle:false, - ecma:8 - })) - .pipe(gulp.dest('../www/assets/js_es')) -}) - - -gulp.task('copying other js files',()=>{ - gulp.src(['./assets/js/default.js']) - .pipe(gulp.dest('../www/assets/js')) -}) - - -gulp.task('creating export classes',()=>{ - glob('./assets/js/exports.js',function(err,file){ - if(err) done(err) - - var file=file.map((entry)=>{ - return browserify({ - entries: [entry], - }) - .transform(babelify.configure({ - presets : ["es2015"] - })) - .bundle() - .pipe(source(entry)) - .pipe(buffer()) - .pipe(uglify()) - .pipe(gulp.dest('../www')) - }) - - }) - -}) - - - - -gulp.task('generating app shell',()=>{ - gulp.src(['./assets/js/shell.js']) - .pipe(uglifyes({ - warnings:true, - mangle:false, - ecma:8 - })) - .pipe(gulp.dest('../www/assets/js')) -}) - -//electron app -gulp.task('copying twbs to www/node_modules',()=>{ - gulp.src(['../node_modules/bootstrap/**/*.*']) - .pipe(gulp.dest('../www/node_modules/bootstrap')) -}) - - - -gulp.task('copying msal to www/node_modules',()=>{ - gulp.src(['../node_modules/msal/**/*.*']) - .pipe(gulp.dest('../www/node_modules/msal')) -}) - - -gulp.task('copying msal to www/node_modules',()=>{ - gulp.src(['../node_modules/msal/**/*.*']) - .pipe(gulp.dest('../www/node_modules/msal')) -}) - - -//https://developers.google.com/web/tools/workbox/get-started/gulp -/*gulp.task('bundle-sw', () => { - return wbBuild.generateSW({ - globDirectory: './', - swDest: '../www/sw.js', - globPatterns: ['**\/*.{html,js,css}'], - globIgnores: ['spec\/*'] - }) - .then(() => { - console.log('Service worker generated.'); - }) - .catch((err) => { - console.log('[ERROR] This happened: ' + err); - }); -})*/ - - -gulp.task('default',(cb)=>{ - runSequence('minifying html','minifying css','copying fonts','copying images','creating export classes','generating app shell','copying manifest','copying js to js_native folder','minifying modules and saving to js_es','copying other js files','copying twbs to www/node_modules', 'copying msal to www/node_modules') -}); - diff --git a/src/index.html b/src/index.html index 5f9f4d1..436ddea 100644 --- a/src/index.html +++ b/src/index.html @@ -19,15 +19,15 @@ specific language governing permissions and limitations under the License. --> - + - + - + - - - - - BMS - - + - - - - - - - - + + -

Loading Application . . .

- Hang on! Please wait while we are working on the app. . . +

Loading Application . . .

+ Hang on! Please wait while we are working on the app. . .
-
- -
- -
- - - - - -
-
-

Initializing Application

-
-
-
-
-
-
-
-
-
-
-
- - - - - - -
- - - -
-
- - - - - -
- -
- -
- -
-
-
- - - - -
- -
- -
-
-
-
-
-
- - -
-
-
-
- -
-
-
-
-
- -

Close (x)

-
-
-
-

- Proposals - 0 -


-

-
- -
-
- - -
- -
- -
- -
-
- - - -
- -
- -
- Include signatories? -
-
-
- -
- -
- -
-
-
- -
- - - - - - -
- -
-
-
-
- -
-

- Proposals - 0 -


-

-
- -
- -
-

- insert_drive_file
- Submit a good propasal and stand above all other companies now!
-
- Submit add_circle -

-
- -

- -
- -
- -
- -
-
-
- - - - - - - - -
-
-
-
-
- - - -
- - x -
- -
+ + + +
+
+ +
+
+ + + + + + + + +
+
+ + + - - - - - - - - - - - - + + + + - diff --git a/src/manifest.json b/src/manifest.json deleted file mode 100644 index d104a12..0000000 --- a/src/manifest.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "SEARCA's Bidding Management System", - "short_name": "BMS", - "start_url": ".", - "display": "standalone", - "background_color": "#000", - "theme_color": "#000", - "description": "A simply readable Hacker News app.", - "manifest_version": 2 -} \ No newline at end of file diff --git a/src/pages/auth-company-section/auth.js b/src/pages/auth-company-section/auth.js new file mode 100644 index 0000000..3d0206a --- /dev/null +++ b/src/pages/auth-company-section/auth.js @@ -0,0 +1,68 @@ +const Checker = import('../../utils/tenant-checker') +const Auth = import('../../services/auth-service') + + +/* function auth call */ +//const auth = new Auths() +const usernameField = document.getElementById('username') +const passwordField = document.getElementById('password') +const loginBtn = document.getElementById('loginBtn') +const loginForm = document.querySelector('form[name="login"]') +const loginStatus = document.getElementById('auth-status') + +const showLoginError = () => { + loginStatus.innerHTML ='
Invalid username or password
Please try again later
' + loginBtn.removeAttribute('disabled') +} + +const login = (e) => { + e.preventDefault() + // non empty value + if (usernameField.value.length < 1 || passwordField.value.length < 1) return showLoginError() + + // disable + loginBtn.setAttribute('disabled', 'disabled') + + Auth.then(loader => { + return new loader.default().login(usernameField.value,passwordField.value).then(json => { + + if (!json.token) return showLoginError() + // account not yet verified + if (!json.role) return window.location = 'confirmation.html' + + localStorage.setItem('token', json.token) + localStorage.setItem('role', json.role) + window.localStorage.setItem('id', json.id) + window.localStorage.setItem('givenName', usernameField.value) + localStorage.setItem('username', usernameField.value) + + window.location = window.location.href.substr(0,window.location.href.lastIndexOf('/'))+'/#/home' + + + }).catch((err) => { + console.log(err) + showLoginError() + }) + }) + + + +} + +const listenOnUsernameChange = (e) => { + e.preventDefault() + const __emailField = document.getElementById('organixzation') + if(__emailField.nodeValue.length < 1) return + + //redirect to auth page + const isFiltered = Checker.checkCorporateEmailAddress({ domain: 'searca', email: __emailField.value }) + if (isFiltered) { + this.setAttribute('disabled','disabled') + setTimeout(()=>{window.location = window.location.href.substr(0,window.location.href.lastIndexOf('/'))+'/auth.html'},600) + } else { + document.querySelector('.auth-org').style.display = 'none' + document.querySelector('.auth-default').style.display = 'block' + } +} + +loginForm.addEventListener('submit', login) \ No newline at end of file diff --git a/src/pages/auth-company-section/index.js b/src/pages/auth-company-section/index.js new file mode 100644 index 0000000..e9d08fd --- /dev/null +++ b/src/pages/auth-company-section/index.js @@ -0,0 +1,45 @@ +import style from './style' + + +export default ` + +
+
+
+
+

+

Sign-in

+

SEARCA's Bidding Management System

+
+
+
+ +
+
+
+


+ +

+

+ +

+ + + +

By signing to BMS you agree to Users License agreement and
Data policy settings of SEARCA

+

+ +


+

+ + ©www.searca.org + v2.0 + +

+

+
+
+
+
+
+` \ No newline at end of file diff --git a/src/pages/auth-company-section/o365.js b/src/pages/auth-company-section/o365.js new file mode 100644 index 0000000..9800a0d --- /dev/null +++ b/src/pages/auth-company-section/o365.js @@ -0,0 +1,125 @@ +import AuthenticationContext from 'adal-angular' +const XHR = import('../../utils/xhr') + +// initializ settings +const init = () => { + // phonegap settings + if (document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1) { + delete window.open; // This restores the default browser + } +} + +(async () => { + const AdalConfig = await import('../../config/adal') + const ApiConfig = await import('../../config/api') + + // adal error + const authError = () => { + alert('Unable to authenticate. Please try again later.') + } + + // adal callback + const userSignedIn = (err, token) => { + //window.bms.default.spinner.show() + if (!err) { + window.ADAL.acquireToken("https://graph.microsoft.com", function (error, token) { + if(token.length) { + getGraph(token) + } else { + authError() + } + }) + } + } + + + +const loginOnPremiseServer = (data) => { + return new Promise((resolve, reject) => { + XHR.then(res => { + const XHReq = new res.default() + const payload = { + url:`${ApiConfig.default.url}/auth/`, + method:'POST', + body: JSON.stringify({ + data, + }), + } + + XHReq.request(payload).then((json) => { + const data = JSON.parse(json) + try{ + resolve(data) + }catch(err) { + reject(err) + } + }) + }) + + }) +} + +// authenticate to remote server +const loginOnPremise = (data) => { + loginOnPremiseServer (data).then((json) => { + // account not yet verified + if (!json.role) { + window.location = 'authentication/confirmation.html' + return 0 + } + + // window.bms.default.spinner.hide() + localStorage.setItem('token', json.token) + localStorage.setItem('role', json.role) + localStorage.setItem('username', data.mail) + window.localStorage.setItem('id', data.id) + window.localStorage.setItem('givenName', data.displayName) + window.localStorage.setItem('department', data.department) + window.localStorage.setItem('position', data.jobTitle) + window.location = './#/home/' + + }).catch((err) => { + // window.bms.default.spinner.hide() + authError() + document.querySelector('.btn-office365').removeAttribute('disabled') + console.log(err) + }) + +} + +// get msgraph +const getGraph = (token) => { + fetch('https://graph.microsoft.com/beta/me/',{ + headers:{'Authorization':'Bearer '+token}, + method: 'GET'} + ).then(response => response.json()).catch((err) => { + authError() + }).then(data => { + // auth to onpremise + if(data.id) { + loginOnPremise(data) + } + }) +} + + + // add callback to config + AdalConfig.default.callback = userSignedIn + // adal global instance + window.AuthenticationContext = AuthenticationContext + window.ADAL = new window.AuthenticationContext(AdalConfig.default) + // adal cllback + window.ADAL.handleWindowCallback() + + + + console.log("%cUnauthorized Access","color: red; font-size: x-large") + console.log("%cBidding Management System : You are trying to view the code in console","color: grey") + + // button + document.querySelector('.btn-office365').addEventListener('click', (e) => { + e.target.disabled = 'disabled' + window.ADAL.login() + }) + +})() diff --git a/src/pages/auth-company-section/style.styl b/src/pages/auth-company-section/style.styl new file mode 100644 index 0000000..a4c425f --- /dev/null +++ b/src/pages/auth-company-section/style.styl @@ -0,0 +1,7 @@ +body + font-family: 'Roboto', sans-serif + overflow: hidden + +.auth-main + padding-top: 100px + diff --git a/src/pages/auth-section/index.js b/src/pages/auth-section/index.js new file mode 100644 index 0000000..01feec5 --- /dev/null +++ b/src/pages/auth-section/index.js @@ -0,0 +1,49 @@ +import style from './style' + + +export default ` + +
+
+
+

SEARCA

+


Bidding Management System

+
+
+

+ Compare supplier's price easier , faster and better than before! Be the first to use the new and advanced bidding management system +

+

+
+

Sign-in with

+ +

+ +

+ +

OR

+ +

+
+
+ +
+
+ +
+
+ +
+
+ +` \ No newline at end of file diff --git a/src/pages/auth-section/o365.js b/src/pages/auth-section/o365.js new file mode 100644 index 0000000..9800a0d --- /dev/null +++ b/src/pages/auth-section/o365.js @@ -0,0 +1,125 @@ +import AuthenticationContext from 'adal-angular' +const XHR = import('../../utils/xhr') + +// initializ settings +const init = () => { + // phonegap settings + if (document.URL.indexOf( 'http://' ) === -1 && document.URL.indexOf( 'https://' ) === -1) { + delete window.open; // This restores the default browser + } +} + +(async () => { + const AdalConfig = await import('../../config/adal') + const ApiConfig = await import('../../config/api') + + // adal error + const authError = () => { + alert('Unable to authenticate. Please try again later.') + } + + // adal callback + const userSignedIn = (err, token) => { + //window.bms.default.spinner.show() + if (!err) { + window.ADAL.acquireToken("https://graph.microsoft.com", function (error, token) { + if(token.length) { + getGraph(token) + } else { + authError() + } + }) + } + } + + + +const loginOnPremiseServer = (data) => { + return new Promise((resolve, reject) => { + XHR.then(res => { + const XHReq = new res.default() + const payload = { + url:`${ApiConfig.default.url}/auth/`, + method:'POST', + body: JSON.stringify({ + data, + }), + } + + XHReq.request(payload).then((json) => { + const data = JSON.parse(json) + try{ + resolve(data) + }catch(err) { + reject(err) + } + }) + }) + + }) +} + +// authenticate to remote server +const loginOnPremise = (data) => { + loginOnPremiseServer (data).then((json) => { + // account not yet verified + if (!json.role) { + window.location = 'authentication/confirmation.html' + return 0 + } + + // window.bms.default.spinner.hide() + localStorage.setItem('token', json.token) + localStorage.setItem('role', json.role) + localStorage.setItem('username', data.mail) + window.localStorage.setItem('id', data.id) + window.localStorage.setItem('givenName', data.displayName) + window.localStorage.setItem('department', data.department) + window.localStorage.setItem('position', data.jobTitle) + window.location = './#/home/' + + }).catch((err) => { + // window.bms.default.spinner.hide() + authError() + document.querySelector('.btn-office365').removeAttribute('disabled') + console.log(err) + }) + +} + +// get msgraph +const getGraph = (token) => { + fetch('https://graph.microsoft.com/beta/me/',{ + headers:{'Authorization':'Bearer '+token}, + method: 'GET'} + ).then(response => response.json()).catch((err) => { + authError() + }).then(data => { + // auth to onpremise + if(data.id) { + loginOnPremise(data) + } + }) +} + + + // add callback to config + AdalConfig.default.callback = userSignedIn + // adal global instance + window.AuthenticationContext = AuthenticationContext + window.ADAL = new window.AuthenticationContext(AdalConfig.default) + // adal cllback + window.ADAL.handleWindowCallback() + + + + console.log("%cUnauthorized Access","color: red; font-size: x-large") + console.log("%cBidding Management System : You are trying to view the code in console","color: grey") + + // button + document.querySelector('.btn-office365').addEventListener('click', (e) => { + e.target.disabled = 'disabled' + window.ADAL.login() + }) + +})() diff --git a/src/pages/auth-section/style.styl b/src/pages/auth-section/style.styl new file mode 100644 index 0000000..e10151b --- /dev/null +++ b/src/pages/auth-section/style.styl @@ -0,0 +1,46 @@ +body + font-family: 'Roboto', sans-serif + overflow: hidden + +.go-to-app-btn + border-radius: 50px + +.backdrop + position: fixed + top:0 + left: 0 + width: 100% + height: 100% + background: rgba(0,0,0,0.82) linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(0,0,0, 0.9) 93%) repeat scroll 0 0 + z-index: 1 + +.backdrop-content + position: fixed + top:0vh + left: 0 + width: 100% + height: 100% + z-index: 2 + color: #fff + padding-top:15vh + opacity: 1 + animation: goUp 1.2s ease 0s + +.backdrop-header + z-index: 3 + color: #fff + position: fixed + top: 0 + width: 100% + height: 100px + +@keyframes goUp + 0% + opacity: 0.3 + top:30vh + + + 100% + opacity: 1 + top:0 + diff --git a/src/pages/authentication/index.html b/src/pages/authentication/index.html deleted file mode 100644 index 54a76e0..0000000 --- a/src/pages/authentication/index.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - Login - - - - - - - -
- -
-
-
-

-

Sign-in

-

SEARCA's Bidding Management System

-
-
-
- -
-
-
-


- -

-

- -

- - - -

By signing to BMS you agree to Users License agreement and
Data policy settings of SEARCA

-

- -


-

- - ©www.searca.org - v2.0 - -

-

-
-
-
-
-
- - - - - - - - diff --git a/src/pages/authentication/o365.html b/src/pages/authentication/o365.html deleted file mode 100644 index fbd1a62..0000000 --- a/src/pages/authentication/o365.html +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - - - - - - - - - - Login - - - - - -
-
- -
-
-

Sign In

-

Bidding Management System

-
-
- -
- -
-
-
-
- -
-
- -

By signing to BMS you agree to Users License agreement and
Data policy settings of SEARCA

-
-
- - -
- -
- -

© 2017 Information Technology Services Unit

-
-
- - -
-
- -
- -
- - - - - - - - - - - diff --git a/src/pages/bidding-info-section/actions.js b/src/pages/bidding-info-section/actions.js new file mode 100644 index 0000000..f2cb1b3 --- /dev/null +++ b/src/pages/bidding-info-section/actions.js @@ -0,0 +1,192 @@ +const BiddingServ = import('../../services/bidding-list-service') +const ListItem = import('../../components/list-item') +const ListSection = import('../../components/list-section') +const DropdownLoader = import('../../utils/dropdown-loader') + +let timeout = {} + +const loadPopup = () => { + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + popupes.then(loader => new loader.default()) +} + +const showEmptySearch = () => { + const targ = document.querySelector('.list-search-bidding-section') + targ.innerHTML = ` +
+ search +

+ No matches found.
Please try another keyword
+

+
` +} + +const search = (e) => { + const targ = document.querySelector('.list-search-bidding-section') + + let __payload = { + token : window.localStorage.getItem('token'), + param: e.target.value, + page: 1, + } + + if (e.target.value.length > 0) { + + // hide list and show search results + document.querySelector('.list-bidding-section').classList.add('hide') + targ.classList.remove('hide') + + // clear area + if (__payload.page === 1) targ.innerHTML = '
searching . . .
' + + // search + clearTimeout(timeout) + timeout = setTimeout(() => { + + BiddingServ.then(loader => { + const a = new loader.default() + a.search(__payload).then(res => { + + // clear section for page 1 + if(__payload.page < 2) targ.innerHTML = '' + + // items + ListItem.then(item => { + // items + res.forEach((el, index) => { + const lItem = new item.default({class: 'col-12 list', id: el.id, profile_name: el.profile_name, date_created: el.date_created}) + targ.append(lItem) + }) + + // empty result + if (__payload.page === 1 && res.length === 0) { + showEmptySearch() + } + + }).catch((err) => showEmptySearch()) + }) + }) + + },500) + + } else { + // revert to normal + document.querySelector('.list-bidding-section').classList.remove('hide') + document.querySelector('.list-search-bidding-section').classList.add('hide') + } + +} + +const bindSearch = () => { + document.querySelector('#search').addEventListener('keyup', search) +} + +/** + * Load List section + */ +const loadListSection = () => { + return new Promise((resolve, reject) => { + ListSection.then(sec => { + const lSec = document.querySelector('list-section') || document.querySelector('.list-bids-container') + if (!lSec) return 0 + // list section + const newLSec = document.createElement('article') + newLSec.classList.add('list-bids-container', 'd-none', 'd-lg-block', 'col-lg-2') + newLSec.style.zIndex = 1 + newLSec.innerHTML = sec.default + lSec.replaceWith(newLSec) + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + bindSearch() + // resolve + resolve() + }) + }).catch(err => { + reject(err) + }) +} + +/** + * Bidding List AJAX + */ +const getBiddingList = (opt = {}) => { + opt.page = opt.page || 1 + console.log(opt.page) + // load Via AJAX + const listSection = document.querySelector('.list-bidding-section') + BiddingServ.then(loader => { + const a = new loader.default() + a.lists({ token : window.localStorage.getItem('token'), filter: opt.filter || 'all', page: opt.page }).then(res => { + + // clear section for page 1 + if(opt.page < 2) listSection.innerHTML = '' + + // items + ListItem.then(item => { + // items + res.forEach((el, index) => { + const lItem = new item.default({class: 'col-12 list', id: el.id, profile_name: el.profile_name, date_created: el.date_created}) + listSection.append(lItem) + }) + + // more btn + setTimeout(() => { + const moreBtn = document.createElement('a') + moreBtn.href = '#' + moreBtn.textContent = 'More' + moreBtn.classList.add('col-12') + moreBtn.style.textAlign = 'center' + moreBtn.style.paddingBottom = '100px' + moreBtn.style.paddingTop = '10px' + moreBtn.opt = {} + moreBtn.opt.filter = opt.filter + moreBtn.opt.page = opt.page+1 + moreBtn.addEventListener('click', (e) => { + e.preventDefault() + getBiddingList(e.target.opt) + moreBtn.remove() + }) + + return res.length > 0 ? listSection.append(moreBtn) : 0 + + }, 1000) + + }) + }) + }) +} + + + /** + * Attachment Components + */ + const loadAttachments = (target, data) => { + + import('../../components/attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // empty section + targ.innerHTML = '' + // append files + data.forEach((val ,index) => { + if(!val.locked) val.menus = ['remove'] + targ.append(new res.default(val)) + }) + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + loadPopup() + },2000) + }) + } + +export { loadListSection, getBiddingList, loadAttachments } \ No newline at end of file diff --git a/src/pages/bidding-info-section/index.js b/src/pages/bidding-info-section/index.js new file mode 100644 index 0000000..fc819e2 --- /dev/null +++ b/src/pages/bidding-info-section/index.js @@ -0,0 +1,403 @@ +import style from './style' +import styleRatings from '../../components/general-style/star-ratings' +import Network from '../../config/api' + +const infoMenu = import('../../components/bidding-info-menu') +const infoStatus = import('../../components/bidding-status') +const info = import('../../services/bidding-list-service') +const statusMessage = import('../../components/status-message') + +class template { + constructor (params) { + this.__params = params + this.__info = {} + this.criteria = [{ + name: 'price', + alias: 'Price' + },{ + name: 'quality', + alias: 'Goods/ Service Quality' + },{ + name: 'time', + alias: 'Delivery Time' + }] + } + + __getFeedbacks (opt) { + + this.template.querySelectorAll('.reviews-section').forEach((el, index) => { + // id + const __payload = { + id: this.__info.id, + token: window.localStorage.getItem('token') + } + + // get from DB + info.then(res => { + new res.default().reviews(__payload).then(json => { + if (json.length > 0) { + el.innerHTML = '

What other say about this supplier expand_more

' + } + + + json.forEach((val, ind) => { + const nameAlias = val.author[0].profile_name.substr(0,2).toUpperCase() + const art = document.createElement('article') + + art.classList.add('row') + art.innerHTML+=` +
+
+
+
${nameAlias}
+
+

${val.author[0].profile_name}
+ ${val.author[0].department} + + print PRINT + +

+
+
+
+ +


+

${val.feedback}
${val.company_name} ${val.product_name}


+


+ ` + const rateSec = art.querySelector('.rate-section') + val.ratings.forEach((rateVal, rateIndex) => { + const span = document.createElement('span') + span.innerHTML = this.criteria[rateVal.name] || `${rateVal.name}` + span.innerHTML += ' ' + + for (let x = 0; x < 4; x++) { + const star = document.createElement('i') + star.classList.add('material-icons', 'md-18', 'star-ratings', `star-${rateVal.name}`) + + if (x < rateVal.value) { + star.textContent = 'star' + star.classList.add('active') + } else { + star.textContent = 'star_border' + } + + star.position = x + star.criteria = rateVal.name + span.append(star) + } + + span.append(document.createElement('br')) + + rateSec.append(span) + + }) + + el.append(art) + }) + }) + }) + }) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + this.__info = await this.__getInfo(this.__params.id) + this.template = document.createElement('section') + + // reviewed by + let arr = this.__info.collaborators.map(val => { + return `${val.profile_name}` + }) + // template settings + this.template.setAttribute('style', 'margin-top:50px;padding-bottom:40px;height:100vh;overflow-y:auto;') + this.template.classList.add('col-lg-10') + this.template.id = 'bids-info-container' + this.template.innerHTML = ` + +
+
+ + +
+ +
+ +

Bidding Request #${this.__info.id}

+ Bidding Exemption : ${parseInt(this.__info.excemption) ? 'Yes' : 'No'}
+

Reviewed By : ${arr.join(' ')}

+ + +
+
+
${this.__info.profile_name.substring(0,2).toUpperCase()}
+

+ ${this.__info.profile_name}
+ ${this.__info.date_created} +

+
+
+ + +
+
Attachments
+
+ + +

+
Particulars
+
+
+

+
Customer Reviews
+
+
+
` + this.__getFeedbacks(this.__info.id) + return this.template + } + + async __bindListeners (loader, template) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // request for approval + if (this.__info.status == 1 && loader.isCBAAsst()) { + const __serv = (await import('../../components/bidding-info-menu/actions/approve')).default + return new __serv({ id: this.__info.id, selector: '#btn-approve'}) + + } + + + if (this.__info.status == 3 && loader.isCBAAsst()) { + // close request + const __serv = (await import('../../components/bidding-info-menu/actions/close')).default + const __c = new __serv({ id: this.__info.id, selector: '#btn-close'}) + // failure of bidding + const __servF = (await import('../../components/bidding-info-menu/actions/fail')).default + const __f = new __servF({ id: this.__info.id, selector: '#btn-fail'}) + } + + + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + info.then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res.data[0]) + }).catch(err => reject(err)) + }) + }) + } + + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getReviews(id) { + // fetch details + return new Promise((resolve, reject) => { + info.then(loader => { + const a = new loader.default().reviews({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + /** + * Attachment Components + */ + async getAttachments() { + const loadAttachments = (await import('./actions')).loadAttachments + // prevent deletion for already closed bidding + if(this.__info.status == 5 || this.__info.status == 3) this.__info.attachments.map(t => { + return t.locked = true + }) + + return loadAttachments('.attachments-info-section', this.__info.attachments) + } + + /** + * Show menu based on payload + * + * @param {Object} opt + */ + showMenu (opt) { + return new Promise((resolve, reject) => { + infoMenu.then(res => { + return new res.default(opt).then((html) => { + const targ = document.querySelector('bidding-info-menu') + if (targ) { + targ.replaceWith(html) + } else { + document.querySelector('#detail-info-menu').replaceWith(html) + } + + resolve() + }) + }) + }) + } + + + /** + * Get users privilege via privilege API and assign message per status + * + * @param {Object} loader + * @param {Object} status + */ + setPayload (loader, status) { + this.__payload = {} + this.__payload_menu = { + id: this.__params.id, + menus: [], + } + + // draft (For all users) + if (this.__info.status == 0) { + this.__payload = {} + this.__payload_menu = { + id: this.__params.id, + menus: ['print'] + } + } + + if (this.__info.status == 0) this.__payload_menu.menus = ['send', 'attach', 'remove', 'update', 'particulars', 'print', 'sign'] + + // for regular USER + if (this.__info.status == 1 && !loader.isCBAAsst()) this.__payload_menu.menus = ['attach', 'print'] | (this.__payload = status.showBiddingReqSent(this.__info.id)) + + // for CBA Asst /APPROVE + if (this.__info.status == 1 && loader.isCBAAsst()) { + this.__payload_menu = { + id: this.__params.id, + menus: ['return', 'attach', 'update', 'print', 'sign', 'particulars'], + } + + this.__payload = status.showBiddingReqApprove(this.__info.id) + } + + // for both + // must change to send to resend + if (this.__info.status == 2) (this.__payload_menu.menus = ['resend', 'attach', 'update', 'print', 'sign', 'particulars']) | (this.__payload = status.showBiddingReqReturned(this.__info.id)) + + if (this.__info.status == 3 && loader.isCBAAsst()) (this.__payload_menu.menus = ['return', 'attach', 'print']) | (this.__payload = status.showBiddingApproved(this.__info.id)) + + // for GSU + // enable commands for approved request + if (this.__info.status == 3 && (loader.isGSU() || !loader.isCBAAsst())) this.__payload = status.showBiddingApproveReadOnlyStandard(this.__info.id) + if (this.__info.status == 3 && loader.isGSU()) this.__payload_menu.menus = ['attach', 'print'] + + // closed + if (this.__info.status == 5) { + this.__payload = status.showBiddingClosed(this.__info.id) + this.__payload_menu.menus = ['print'] + } + + // disapproved + if (this.__info.status == 6) this.__payload = status.showBiddingDisapproved(this.__info.id) + + // failed + if (this.__info.status == 6) this.__payload = status.showBiddingFailed(this.__info.id) + + } + + /** + * Render Status in DOM + * + * @param {Object} payload + */ + getStatus(payload) { + // render + return new Promise((resolve, reject) => { + if (this.__info.status > 0) { + infoStatus.then(res => { + return new res.default(this.__payload).then(html => { + const a = (!document.querySelector('#bidding-status')) ? document.querySelector('#detail-info-menu-status').prepend(html) : document.querySelector('#bidding-status').replaceWith(html) + resolve(html) + }) + }) + } else { + // remove + const a = (!document.querySelector('#bidding-status')) ? document.querySelector('#detail-info-menu-status').innerHTML = '' : document.querySelector('#bidding-status').innerHTML = '' + resolve({}) + } + + }) + } + + + async setStatus () { + const status = await statusMessage + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // set menu and payload based on user privilege + return await import('../../utils/privilege-loader').then(loader => { + this.privilegeLoader = loader + this.setPayload(loader, status) + this.showMenu(this.__payload_menu).then(() => { + console.log('show') + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + popupes.then(loader => { + const a = new loader.default() + }) + }) + + this.getStatus(this.__payload).then(temp => { + this.__bindListeners(loader, temp) + }) + }) + } + + getParticulars () { + import('../../components/particulars-item').then(res => { + const targ = document.querySelector('.particulars-section') + this.__info.particulars.forEach((el, index) => { + targ.append(new res.default({ + id: el.id, name: el.name, + deadline: el.deadline, + requirements: el.requirements, + biddingId: this.__info.id, + menus: (this.__info.status == 0 || this.__info.status == 1) ? ['remove', 'update', 'add'] : []})) + }) + }) + } +} + + +export { template } \ No newline at end of file diff --git a/src/pages/bidding-info-section/style.styl b/src/pages/bidding-info-section/style.styl new file mode 100644 index 0000000..221c671 --- /dev/null +++ b/src/pages/bidding-info-section/style.styl @@ -0,0 +1,3 @@ +.reviews-section + font-size: smaller + padding-bottom: 100px diff --git a/src/pages/bidding-part-form/actions.js b/src/pages/bidding-part-form/actions.js new file mode 100644 index 0000000..585f759 --- /dev/null +++ b/src/pages/bidding-part-form/actions.js @@ -0,0 +1,62 @@ +const Serv = import('../../services/bidding-part-service') +const ListItem = import('../../components/list-item') + +/** + * Submission failed + */ +const showError = () => { + document.getElementById('reg-notif-area').innerHTML = ` +
+ Unable to process request. Please try again later +
` +} + + +/** + * Submission success + */ +const showSuccess = (json) => { + // redirect to nex step + window.location.hash = `#/bids/forms/registration/${json.data}/step/3` +} + + + +/** + * Create new Bidding + * @param {*} e + */ +const register = (e) => { + e.preventDefault() + // payload + const __nameField = document.querySelector('form[name="bidding-request-particulars"] input[name="name"]') + const __deadlineField = document.querySelector('form[name="bidding-request-particulars"] input[name="deadline"]') + const payload = { + name: __nameField.value, + deadline: __deadlineField.value, + id: e.target.biddingId, + action: 'create', + token: window.localStorage.getItem('token'), + } + + if (__nameField.value.length < 1) return __nameField.classList.add('error') + + // remove error + __nameField.classList.remove('error') + + // create + Serv.then(loader => { + return new loader.default().create(payload).then(json => { + if(json.data) { + // clear and redirect to next step if any + return showSuccess(json) + } + // failed + showError() + }) + }).catch(err => { console.log(err) + showError() + }) +} + +export {register} \ No newline at end of file diff --git a/src/pages/bidding-part-form/actions/create.js b/src/pages/bidding-part-form/actions/create.js new file mode 100644 index 0000000..9c50a43 --- /dev/null +++ b/src/pages/bidding-part-form/actions/create.js @@ -0,0 +1,77 @@ +const Serv = import('../../../services/bidding-part-service') +const ListItem = import('../../../components/list-item') + +const hideSpinner = () => { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() +} + +/** + * Submission failed + */ +const showError = () => { + document.getElementById('reg-notif-area').innerHTML = ` +
+ Unable to process request. Please try again later +
` + hideSpinner() +} + + +/** + * Submission success + */ +const showSuccess = (json) => { + hideSpinner() + // redirect to nex step + window.location.hash = `#/bids/forms/registration/${json.data}/step/3` +} + + + +/** + * Create new Bidding + * @param {*} e + */ +const register = (e) => { + e.target.disabled = 'disabled' + e.preventDefault() + // payload + const __nameField = document.querySelector('form[name="bidding-request-particulars"] input[name="name"]') + const __deadlineField = document.querySelector('form[name="bidding-request-particulars"] input[name="deadline"]') + const __payload = { + name: __nameField.value, + deadline: __deadlineField.value, + id: e.target.id, + action: 'create', + token: window.localStorage.getItem('token'), + } + + if (__nameField.value.length < 1) return __nameField.classList.add('error') + + // remove error + __nameField.classList.remove('error') + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + + // create + Serv.then(loader => { + return new loader.default().create(__payload).then(json => { + if(json.data) { + // clear and redirect to next step if any + return showSuccess(json) + } + // failed + showError() + e.target.disabled = false + }) + }).catch(err => { console.log(err) + showError() + e.target.disabled = false + }) +} + +export { register } \ No newline at end of file diff --git a/src/pages/bidding-part-form/actions/update.js b/src/pages/bidding-part-form/actions/update.js new file mode 100644 index 0000000..5fb56b6 --- /dev/null +++ b/src/pages/bidding-part-form/actions/update.js @@ -0,0 +1,86 @@ +const Serv = import('../../../services/bidding-part-service') +const ListItem = import('../../../components/list-item') + +const hideSpinner = () => { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() +} +/** + * Submission failed + */ +const showError = () => { + document.getElementById('reg-notif-area').innerHTML = ` +
+ Unable to process request. Please try again later +
` + hideSpinner() +} + + +/** + * Submission success + */ +const showSuccess = (id) => { + hideSpinner() + // redirect to nex step + window.location.hash = `#/bids/${id}/info` +} + + + +/** + * Create new Bidding + * @param {*} e + */ +const update = (e) => { + e.target.disabled = 'disabled' + e.preventDefault() + // payload + const __nameField = document.querySelector('form[name="bidding-request-particulars"] input[name="name"]') + const __deadlineField = document.querySelector('form[name="bidding-request-particulars"] input[name="deadline"]') + const __payload = { + name: __nameField.value, + deadline: __deadlineField.value, + id: e.target.id, + action: 'update', + token: window.localStorage.getItem('token'), + } + + if (__nameField.value.length < 1) return __nameField.classList.add('error') + + // remove error + __nameField.classList.remove('error') + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + + // create + Serv.then(loader => { + return new loader.default().create(__payload).then(json => { + if(json.data) { + // clear and redirect to next step if any + return showSuccess(e.target.biddingId) + } + // failed + showError() + e.target.disabled = false + }) + }).catch(err => { console.log(err) + showError() + e.target.disabled = false + }) +} + +const view = (params) => { + return new Promise((resolve, reject) => { + Serv.then(res => { + return new res.default().view(params).then(json => { + resolve(json) + }) + }) + }) +} + +export { update, view } \ No newline at end of file diff --git a/src/pages/bidding-part-form/index.js b/src/pages/bidding-part-form/index.js new file mode 100644 index 0000000..b55c0f5 --- /dev/null +++ b/src/pages/bidding-part-form/index.js @@ -0,0 +1,95 @@ +import style from '../../components/general-style/circle-marker' +import { resolve } from 'upath'; + +export default class { + constructor (opt) { + this.opt = opt + this.template = {} + this.__info = {} + return this.render() + } + + __loadUpdateAction () { + import('./actions/update').then(loader => { + let btn = this.template.querySelector('.add-supplier-button') + btn.addEventListener('click', loader.update) + btn.id = this.__info.id + btn.biddingId = this.__info.bidding_id + }) + } + + __loadCreateAction () { + import('./actions/create').then(loader => { + let btn = this.template.querySelector('.add-supplier-button') + btn.addEventListener('click', loader.register) + btn.id = this.opt.id + }) + } + + __bindListeners () { + if (this.opt.action === 'update') this.__loadUpdateAction() + if (this.opt.action === 'create') this.__loadCreateAction() + } + + __getInfo() { + return new Promise((resolve, reject) => { + import('./actions/update').then(loader => { + loader.view({ + id: this.opt.id, + token: window.localStorage.getItem('token') + }).then(json => resolve(json[0])).catch(err => reject(err)) + + //this.template.querySelector('.add-supplier-button').addEventListener('click', loader.update) + }) + }) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + this.__info = (await this.__getInfo()) || {} + + this.template = document.createElement('section') + this.template.classList.add('row') + this.template.innerHTML = ` + +
+
+ +
+
+

+ +

+ + add_shopping_cart + + + Particulars + (Required) + +
+ +

This will help to easily identify what this request is intended for

+ +

+ +

+

+ date_range + Date needed +
+ +

+
+ +
+ +
+
+
` + this.__bindListeners() + return this.template + } +} diff --git a/src/pages/bidding-prod-form/actions/create.js b/src/pages/bidding-prod-form/actions/create.js new file mode 100644 index 0000000..d034717 --- /dev/null +++ b/src/pages/bidding-prod-form/actions/create.js @@ -0,0 +1,274 @@ +const Serv = import('../../../services/bidding-prod-service') + +const hideSpinner = () => { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() +} +/** + * Submission failed + */ +const showError = () => { + document.getElementById('reg-notif-area').innerHTML = ` +
+ Unable to process request. Please try again later +
` + hideSpinner() +} + + +/** + * Submission success + */ +const showSuccess = (id, json) => { + hideSpinner() + // redirect to nex step + window.location.hash = `/bids/forms/registration/${id}/step/4/${json.data}` +} + +const bindAddSpecsSection = () => { + // funding + document.querySelectorAll('.add-specs-btn').forEach((val, index) => { + val.removeEventListener('click', addSpecsField) + val.addEventListener('click', addSpecsField) + }) +} + + +const bindRemoveSpecsSection = () => { + // funding + document.querySelectorAll('.remove-specs-btn').forEach((val, index) => { + val.removeEventListener('click', removeFundSelection) + val.addEventListener('click',removeSpecsSection) + }) +} + +const removeSpecsSection = (e) => { + e.target.parentNode.parentNode.parentNode.remove() +} + + +// specs +const addSpecsField = () => { + let sel = document.createElement('span') + sel.classList.add('row', 'specs-input-section') + sel.style.marginTop = '15px' + sel.innerHTML = ` +
+ +
+
+ +
+
+
remove
+
add
+
+ ` + + document.querySelector('.specs-section').append(sel) + bindAddSpecsSection() + bindRemoveSpecsSection() +} + + + +const bindAddFundSelection = () => { + // funding + document.querySelectorAll('.add-fund-btn').forEach((val, index) => { + val.removeEventListener('click', addFundSelection) + val.addEventListener('click', addFundSelection) + }) +} + +const bindRemoveFundSelection = () => { + // funding + document.querySelectorAll('.remove-fund-btn').forEach((val, index) => { + val.removeEventListener('click', removeFundSelection) + val.addEventListener('click', removeFundSelection) + }) +} + +const removeFundSelection = (e) => { + e.target.parentNode.parentNode.parentNode.remove() +} + +const addFundSelection = () => { + let sel = document.createElement('span') + sel.classList.add('row', 'funds-input-section') + sel.style.marginTop = '15px' + sel.innerHTML = ` +
+ +
+
+ +
+
+ +
+
+
remove
+
add
+ +
+ ` + + document.querySelector('.source_of_fund_section').append(sel) + bindAddFundSelection() + bindRemoveFundSelection() +} + + +/** + * Create new Bidding + * @param {*} e + */ +const register = (e) => { + e.preventDefault() + e.target.disabled = 'disabled' + // payload + let __nameField = document.querySelector('form[name="bidding-request-requirements"] input[name="name"]') + let __quantityField = document.querySelector('form[name="bidding-request-requirements"] input[name="quantity"]') + let __unitField = document.querySelector('form[name="bidding-request-requirements"] input[name="unit"]') + let __currencyField = document.querySelector('form[name="bidding-request-requirements"] select[name="currency"]') + let __amountField = document.querySelector('form[name="bidding-request-requirements"] input[name="amount"]') + let __fundFields = document.querySelectorAll('form[name="bidding-request-requirements"] .funds-input-section') + let __excemptionField = document.querySelector('form[name="bidding-request-requirements"] input[name="excemption"]:checked') + + let __funds =[] + let errors = 0 + + let __specsFields = document.querySelectorAll('.specs-input-section') + let __specsArr = [] + __specsFields.forEach((el, index) => { + let specs = {} + el.querySelectorAll('input').forEach((input, i) => { + if (input.name === 'specs-name') specs.name = input.value + if (input.name === 'specs-value') specs.value = input.value + }) + + __specsArr.push(specs) + }) + + + + if (__nameField.value.length < 1) { + __nameField.classList.add('error') + errors++ + }else{ + __nameField.classList.remove('error') + errors -- + } + + if (__amountField.value.length < 1) { + __amountField.classList.add('error') + errors++ + }else{ + __amountField.classList.remove('error') + errors -- + } + + if (__unitField.value.length < 1) { + __unitField.classList.add('error') + errors++ + }else{ + __unitField.classList.remove('error') + errors -- + } + + if (__quantityField.value.length < 1) { + __quantityField.classList.add('error') + errors++ + }else{ + __quantityField.classList.remove('error') + errors -- + } + + __fundFields.forEach((val, index) => { + __funds[index] = {} + + val.querySelectorAll('input, select').forEach((v, i) => { + __funds[index][v.name] = v.value + }) + }) + + // data + const __payload = { + name: __nameField.value, + quantity: __quantityField.value, + unit: __unitField.value, + amount: __amountField.value, + currency: __currencyField.value, + funds: __funds, + id: e.target.id, + excempted: __excemptionField.value, + specs: __specsArr, + token: localStorage.getItem('token'), + action: 'create', + } + + + if (__nameField.value.length < 1) return __nameField.classList.add('error') + + // remove error + __nameField.classList.remove('error') + + setTimeout(() => { + if (errors >= 0) return e.target.disabled = 'false' + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + + // create + Serv.then(loader => { + return new loader.default().create(__payload).then(json => { + if(json.data) { + // clear and redirect to next step if any + return showSuccess(__payload.id, json) + } + // failed + showError() + e.target.disabled = false + }) + }).catch(err => { console.log(err) + showError() + e.target.disabled = false + }) + }, 1000) +} + +export { register, bindAddSpecsSection, bindRemoveSpecsSection, bindAddFundSelection } \ No newline at end of file diff --git a/src/pages/bidding-prod-form/actions/update.js b/src/pages/bidding-prod-form/actions/update.js new file mode 100644 index 0000000..dcfe888 --- /dev/null +++ b/src/pages/bidding-prod-form/actions/update.js @@ -0,0 +1,318 @@ +const Serv = import('../../../services/bidding-prod-service') +let fundToRemove = [] +let specsToRemove = [] + +const hideSpinner = () => { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() +} +/** + * Submission failed + */ +const showError = () => { + document.getElementById('reg-notif-area').innerHTML = ` +
+ Unable to process request. Please try again later +


` + hideSpinner() +} + + +/** + * Submission success + */ +const showSuccess = (id) => { + hideSpinner() + // redirect to nex step + window.location.hash = `#/bids/requirements/${id}` +} + + +const bindAddFundSelection = () => { + // funding + document.querySelectorAll('.add-fund-btn').forEach((val, index) => { + val.removeEventListener('click', addFundSelection) + val.addEventListener('click', addFundSelection) + }) +} + + +const bindRemoveFundSelection = () => { + // funding + document.querySelectorAll('.remove-fund-btn').forEach((val, index) => { + val.removeEventListener('click', removeFundSelection) + val.addEventListener('click', removeFundSelection) + }) +} + + +const removeFundSelection = (e) => { + // items from database contain id + const resourceID = e.target.getAttribute('data-resources') + if (resourceID) { + // mark to be deleted + fundToRemove[resourceID] = true + } + + e.target.parentNode.parentNode.parentNode.remove() +} + +const addFundSelection = (param = {}) => { + const targ = param.template || document + let sel = document.createElement('span') + sel.classList.add('row', 'funds-input-section') + sel.style.marginTop = '15px' + // set id + if(param.id) sel.setAttribute('data-resources',param.id) + + sel.innerHTML = ` +
+ +
+
+ +
+
+ +
+
+
remove
+
add
+
+ ` + + targ.querySelector('.source_of_fund_section').append(sel) + bindAddFundSelection() + bindRemoveFundSelection() +} + + + +const bindAddSpecsSection = () => { + // funding + document.querySelectorAll('.add-specs-btn').forEach((val, index) => { + val.removeEventListener('click', addSpecsField) + val.addEventListener('click', addSpecsField) + }) +} + + +const bindRemoveSpecsSection = () => { + // funding + document.querySelectorAll('.remove-specs-btn').forEach((val, index) => { + val.removeEventListener('click', removeSpecsSection) + val.addEventListener('click',removeSpecsSection) + }) +} + +const removeSpecsSection = (e) => { + // items from database contain id + const resourceID = e.target.getAttribute('data-resources') + if (resourceID) { + // mark to be deleted + specsToRemove[resourceID] = true + } + + e.target.parentNode.parentNode.parentNode.remove() +} + + + // specs +const addSpecsField = (param = {}) => { + const targ = param.template || document + let sel = document.createElement('span') + sel.classList.add('row', 'specs-input-section') + sel.style.marginTop = '15px' + + // set id + if(param.id) sel.setAttribute('data-resources',param.id) + + sel.innerHTML = ` +
+ +
+
+ +
+
+
remove
+
add
+
+ ` + + targ.querySelector('.specs-section').append(sel) + bindAddSpecsSection() + bindRemoveSpecsSection() +} + + +const view = (params) => { + return new Promise((resolve, reject) => { + Serv.then(res => { + return new res.default().view(params).then(json => { + resolve(json) + }) + }) + }) +} + + +const update = (e) => { + e.preventDefault() + e.target.disabled = 'disabled' + // payload + let __nameField = document.querySelector('form[name="bidding-request-requirements"] input[name="name"]') + let __quantityField = document.querySelector('form[name="bidding-request-requirements"] input[name="quantity"]') + let __unitField = document.querySelector('form[name="bidding-request-requirements"] input[name="unit"]') + let __currencyField = document.querySelector('form[name="bidding-request-requirements"] select[name="currency"]') + let __amountField = document.querySelector('form[name="bidding-request-requirements"] input[name="amount"]') + let __fundFields = document.querySelectorAll('form[name="bidding-request-requirements"] .funds-input-section') + let __excemptionField = document.querySelector('form[name="bidding-request-requirements"] input[name="excemption"]:checked') + + let __funds =[] + let errors = 0 + + let __specsFields = document.querySelectorAll('.specs-input-section') + let __specsArr = [] + __specsFields.forEach((el, index) => { + let specs = {} + el.querySelectorAll('input').forEach((input, i) => { + if (input.name === 'specs-name') specs.name = input.value + if (input.name === 'specs-value') specs.value = input.value + }) + + // id + if(el.getAttribute('data-resources')){ + specs.id = parseInt(el.getAttribute('data-resources')) + } + __specsArr.push(specs) + }) + + + + if (__nameField.value.length < 1) { + __nameField.classList.add('error') + errors++ + }else{ + __nameField.classList.remove('error') + errors -- + } + + if (__amountField.value.length < 1) { + __amountField.classList.add('error') + errors++ + }else{ + __amountField.classList.remove('error') + errors -- + } + + if (__unitField.value.length < 1) { + __unitField.classList.add('error') + errors++ + }else{ + __unitField.classList.remove('error') + errors -- + } + + if (__quantityField.value.length < 1) { + __quantityField.classList.add('error') + errors++ + }else{ + __quantityField.classList.remove('error') + errors -- + } + + __fundFields.forEach((val, index) => { + __funds[index] = {} + + // id + if(val.getAttribute('data-resources')){ + __funds[index]['id'] = parseInt(val.getAttribute('data-resources')) + } + + val.querySelectorAll('input, select').forEach((v, i) => { + __funds[index][v.name] = v.value + }) + }) + + // data + const __payload = { + name: __nameField.value, + quantity: __quantityField.value, + unit: __unitField.value, + amount: __amountField.value, + currency: __currencyField.value, + funds: __funds, + id: e.target.id, + excempted: __excemptionField.value, + specs: __specsArr, + token: localStorage.getItem('token'), + fundsToRemove : fundToRemove, + specsToRemove : specsToRemove, + action: 'update', + } + + + if (__nameField.value.length < 1) return __nameField.classList.add('error') + + // remove error + __nameField.classList.remove('error') + + setTimeout(() => { + if (errors >= 0) return + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + // create + Serv.then(loader => { + return new loader.default().create(__payload).then(json => { + if(json.data) { + // clear and redirect to next step if any + return showSuccess(__payload.id) + } + // failed + showError() + e.target.disabled = false + }) + }).catch(err => { console.log(err) + showError() + e.target.disabled = false + }) + }, 1000) +} + +export { update, view ,addFundSelection, addSpecsField } \ No newline at end of file diff --git a/src/pages/bidding-prod-form/confirmation.js b/src/pages/bidding-prod-form/confirmation.js new file mode 100644 index 0000000..785728a --- /dev/null +++ b/src/pages/bidding-prod-form/confirmation.js @@ -0,0 +1,39 @@ +export default class { + constructor (opt) { + this.opt = opt + this.template = {} + return this.render() + } + + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + + this.template = document.createElement('section') + this.template.classList.add('row') + this.template.innerHTML = ` +
+
+ +



+ +
+ +
+

check_circle Success!

+

Thank you for submitting an item for bidding. Do you want to submit more ?

+
+ +
+
+ Add more  + Done +
+ +
+
` + return this.template + } +} diff --git a/src/pages/bidding-prod-form/index.js b/src/pages/bidding-prod-form/index.js new file mode 100644 index 0000000..a1eea66 --- /dev/null +++ b/src/pages/bidding-prod-form/index.js @@ -0,0 +1,231 @@ +import style from '../../components/general-style/circle-marker' + +export default class { + constructor (opt) { + this.opt = opt + this.template = {} + this.__info = {} + return this.render() + } + + __loadUpdateAction () { + import('./actions/update').then(loader => { + let btn = this.template.querySelector('.add-supplier-button') + btn.addEventListener('click', loader.update) + btn.id = this.__info.id + btn.biddingId = this.__info.bidding_id + }) + } + + __loadCreateAction () { + import('./actions/create').then(loader => { + let btn = this.template.querySelector('.add-supplier-button') + btn.addEventListener('click', loader.register) + btn.id = this.opt.id + // fund + loader.bindAddFundSelection() + // specs + loader.bindAddSpecsSection() + + }) + } + + __loadFundsAndSpecs () { + import('./actions/update').then(loader => { + // source of fund + for (let x = 0; x < this.__info.funds.length; x++) { + if (x == 0) this.template.querySelector('.source_of_fund_section').innerHTML = '' + loader.addFundSelection({type:this.__info.funds[x].fund_type, cost_center:this.__info.funds[x].cost_center, line_item:this.__info.funds[x].line_item, id:this.__info.funds[x].id, template: this.template}) + } + //currency + //currencyField.innerHTML = `` + currencyField.innerHTML + // specs + for (let x = 0; x < this.__info.specs.length; x++) { + if (x == 0) this.template.querySelector('.specs-section').innerHTML = '' + loader.addSpecsField({name:this.__info.specs[x].name, value:this.__info.specs[x].value, id:this.__info.specs[x].id, template: this.template}) + } + }) + } + + __bindListeners () { + if (this.opt.action === 'update') this.__loadUpdateAction() + if (this.opt.action === 'create') this.__loadCreateAction() + if (this.opt.action === 'update') { + this.__loadFundsAndSpecs() + } + } + + __getInfo() { + return new Promise((resolve, reject) => { + import('./actions/update').then(loader => { + loader.view({ + id: this.opt.id, + token: window.localStorage.getItem('token') + }).then(json => resolve(json[0])).catch(err => reject(err)) + }) + }) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + if (this.opt.action === 'update') this.__info = (await this.__getInfo()) || {} + + this.template = document.createElement('section') + this.template.classList.add('row') + this.template.innerHTML = ` + +
+
+ +



+

add_shopping_cart Products / Services

+
+ +
+

Please complete all the fields below. Make sure that you checked all information before submitting.

+
+ +
+
+
+ +

+ Item Name / Service (Required) +

e.i. HP Eliteone 800 G3, APC UPS, etc...

+ +

+ + +
+ Quantity +

Number of items needed

+ +
+
+ Unit +

pack , piece , sacks etc..

+ +
+
+ +
+

Source of Fund

+
+ +
+ + +
+
+ +
+
+ + +
+
+
add
+
+
+
+ +
+

Budget

+ +
+ +
+
+ +
+
+ + +
+
+

Bidding exception ?

+
+
+ +
+
+ +
+
+
+ + +
+ +

Specifications

+

Write complete specification for this product/service

+ +
+ +
+ +
+
+ +
+
+
add
+
+
+
+
+ +
+
+ +
+
+ + +
` + this.__bindListeners() + return this.template + } +} diff --git a/src/pages/bidding-reg-form/actions.js b/src/pages/bidding-reg-form/actions.js new file mode 100644 index 0000000..a76b6d8 --- /dev/null +++ b/src/pages/bidding-reg-form/actions.js @@ -0,0 +1,121 @@ +const Serv = import('../../services/bidding-list-service') +const ListItem = import('../../components/list-item') + +const hideSpinner = () => { + const targ = document.querySelector('registration-section > .spinner') + if (targ) targ.hide() +} +/** + * Submission failed + */ +const showError = () => { + document.getElementById('bid-form-status').innerHTML = ` +
+ Unable to process request. Please try again later +
` + hideSpinner() +} + + +/** + * Submission success + */ +const showSuccess = (json) => { + hideSpinner() + document.getElementById('bid-form-status').innerHTML = '' + // remove empty message + if (document.querySelector('.empty-list-message-section')) document.querySelector('.empty-list-message-section').remove() + // redirect to nex step + window.location.hash = `#/bids/forms/registration/${json.data}/step/2` +} + + +/** + * Insert newly created bidding request in DOM + * @param {HTMLElement} target + * @param {Object} data + */ +const appendItems = (target, data) => { + // items + ListItem.then(item => { + const lItem = new item.default(data) + target.prepend(lItem) + }) +} + + +/** + * Create new Bidding + * @param {*} e + */ +const register = (e) => { + // spinner + import('../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) + e.target.disabled = 'disabled' + e.preventDefault() + // payload + const __exemptionField = document.querySelector('form[name="bidding-request-registration"] input[name="forExemption"]:checked') + const payload = { + excemption: parseInt(__exemptionField.value), + action: 'create', + token: localStorage.getItem('token'), + } + + + // create + Serv.then(loader => { + return new loader.default().create(payload).then(json => { + if(json.data) { + const item = { + class: 'col-12 list', + id: json.data, + profile_name: window.localStorage.getItem('givenName'), + date_created: 'Just now', + } + // append to DOM + appendItems(document.querySelector('.list-bidding-section'),item) + // clear and redirect to next step if any + return showSuccess(json) + } + // failed + showError() + e.target.disabled = false + }) + }).catch(err => { + showError() + e.target.disabled = false + }) +} + +/** + * Create new Bidding + * @param {*} e + */ +const update = (e) => { + // payload + const __exemptionField = document.querySelector('form[name="bidding-request-registration"] input[name="forExemption"]:checked') + const __payload = { + id: e.target.id, + excemption: parseInt(__exemptionField.value), + action: 'update', + token: localStorage.getItem('token'), + } + + // update + Serv.then(loader => { + return new loader.default().create(__payload).then(json => { + // success + if(json.data == 1) { + return window.location.hash = `#/bids/${__payload.id}/info` + } + // failed + return showError() + }) + }).catch(err => { + return showError() + }) +} + +export { showError, register, update } \ No newline at end of file diff --git a/src/pages/bidding-reg-form/index.js b/src/pages/bidding-reg-form/index.js new file mode 100644 index 0000000..702f4d2 --- /dev/null +++ b/src/pages/bidding-reg-form/index.js @@ -0,0 +1,48 @@ +import style from './style' + +const template = document.createElement('section') +template.classList.add('row') +template.innerHTML = ` + +
+
+ +
+

+

One moment please . . .

+

+ We always like to make everything easier for you.
Simply answer the question below and we are good to go! +

+

+

+

+

+ + Answer
+ expand_more +
+

+
+

+

+


+
+ +
+ +
+
+
+ + Do you PREFER bidding exemption ? +

This will allow CBA assistant to know if you want a bidding exemption.
Please keep in mind that the final decision is still under CBA Assistant's control

+
+ No (Normal Bidding Process)   + Yes

+ +
+
+
+
+` +export { template } \ No newline at end of file diff --git a/src/pages/bidding-reg-form/style.styl b/src/pages/bidding-reg-form/style.styl new file mode 100644 index 0000000..7d46979 --- /dev/null +++ b/src/pages/bidding-reg-form/style.styl @@ -0,0 +1,16 @@ +.throbbing + position: absolute + width: 20% + left: 40% + animation: throb 1.2s ease .0s infinite + +@keyframes throb + 0% + top: 0 + + 50% + top: -30px + + 100% + top: 0 + diff --git a/src/pages/bidding-report-section/actions/summary.js b/src/pages/bidding-report-section/actions/summary.js new file mode 100644 index 0000000..0f4e64d --- /dev/null +++ b/src/pages/bidding-report-section/actions/summary.js @@ -0,0 +1,236 @@ +import Highcharts from 'highcharts'; + +const repService = import('../../../services/bidding-reports-service') + +let changeBiddingSummaryDOM = '' + +export class Summary { + constructor () { + + } + + async __getBiddingSummary (json) { + const serv = (await repService).default + return new serv().summary({ + token: window.localStorage.getItem('token'), + from: json.from, + to: json.to + }) + } + + create (json) { + return new Promise((resolve, reject) => { + this.__getBiddingSummary(json).then(json => { + this.generate(json) + resolve(json) + }) + }) + } + + generate (json) { + const myChart = Highcharts.chart('container', { + chart: { + plotBackgroundColor: null, + plotBorderWidth: null, + plotShadow: false, + type: 'pie' + }, + title: { + text: 'Bidding Summary Report' + }, + tooltip: { + pointFormat: '{series.name}: {point.percentage:.1f}%' + }, + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + dataLabels: { + enabled: true, + format: '{point.name}: {point.percentage:.1f} %', + style: { + color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black' + } + } + } + }, + credits: { + enabled: false + }, + series: [{ + name: 'Status', + colorByPoint: true, + data: [{ + name: 'Closed', + y: json.closed, + sliced: true, + selected: true + }, { + name: 'In Progress', + y: json.in_progress + }, { + name: 'In Review', + y: json.in_review + }, { + name: 'Failed', + y: json.failed + }] + }] + }) + } + + changeDate () { + // close modal + document.getElementById('general-modal').close() + // fields + const from = document.getElementById('date-from-field') + const to = document.getElementById('date-to-field') + + if (from.value.length && to.value.length) { + // render new graph + this.create({ + from: from.value, + to: to.value, + }).then(json => { + changeBiddingSummaryDOM = new CustomEvent('changeBiddingSummaryDOM', { detail: json }) + window.dispatchEvent(changeBiddingSummaryDOM) + }) + } + } + + loadChangeDate (e) { + // const URL = 'pages/bidding/modal/change_date.html' + const id = e.target.id + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + return import('../../../components/report-change-date-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.changeDate.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + + + return this.XHR.request({method:'GET',url:URL}).then(res=>{ + let modalTarget=document.getElementById('modal-bidding-body') + modalTarget.innerHTML=res + + setTimeout(()=>{ + window.bms.default.scriptLoader(modalTarget) + },50) + + setTimeout(()=>{ + //remove cancel + document.getElementById('modal-dialog-close-button').addEventListener('click',()=>{ + document.getElementById('bidding-modal').close() + }) + + let btn = document.getElementById('modal-dialog-send-button') + btn.el = e.target + btn.addEventListener('click', this.changeDate.bind(proto)) + }) + }).catch(e=>{}) + } + + bindChangeDate () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + document.querySelectorAll('.change-date-modal-btn').forEach((val, index) => { + val.addEventListener('click',this.loadChangeDate.bind(proto)) + }) + } +} + + +// change DOM +const changeGraphDisplay = (json) => { + const analysis = document.getElementById('analysis-section') + // date + if (json.from) { + const from = document.getElementById('from-section') + const to = document.getElementById('to-section') + + from.textContent = json.from + to.textContent = json.to + + const targ = document.getElementById('print-pdf-href') + targ.target = `_blank` + // remove prevent default + targ.setAttribute('onclick', '') + import('../../../config/api').then(net => { + targ.href = `${net.default.url}/bidding/reports/bidding_summary.php?from=${json.from}&to=${json.to}` + }) + } + // total + if (json.total) { + const total = document.getElementById('total-count-section') + total.textContent = json.total + } + // get detailed values + if (json.breakdown) { + const norm = document.getElementById('normal-count-section') + const exe = document.getElementById('exempted-count-section') + // change + norm.textContent = json.breakdown.normal + exe.textContent = json.breakdown.exempted + } + + analysis.innerHTML = '' + json.analysis.forEach((val, index) => { + let classA = '' + if (val.severity == 'warning') classA = "text-warning" + if (val.severity == 'severe') classA = "text-danger" + + analysis.innerHTML+=` +

[${val.severity}] ${val.message}

+ ` + }) + + // utilization per department + const deptTarg = document.getElementById('utilization-per-department-section') + deptTarg.innerHTML =''; + + for (let val in json.department) { + deptTarg.innerHTML+= ` +
+
+
+

${json.department[val].engagement}

+
+

${json.department[val].alias || 'Others'}
+ ${json.department[val].name || 'N/A'}
+ ${json.department[val].total} Total   + ${json.department[val].normal} Normal   + ${json.department[val].exempted} Exempted
+ ${json.department[val].closed} Closed   + ${json.department[val].in_progress} In Progress   + ${json.department[val].in_review} In Review   + ${json.department[val].failed} Failed   +

+
+
+
+

` + } + +} + +// Initialize +const Sum = new Summary() +// change date option +Sum.bindChangeDate() +// get data from remote server + +Sum.create({ + from: '2018-04-01', + to: '2018-05-31', + }) + .then(json => { + changeBiddingSummaryDOM = new CustomEvent('changeBiddingSummaryDOM', { + detail: json + }) + window.dispatchEvent(changeBiddingSummaryDOM) +}) + +window.addEventListener('changeBiddingSummaryDOM', (e) => { + changeGraphDisplay(e.detail) +}) \ No newline at end of file diff --git a/src/pages/bidding-report-section/index.js b/src/pages/bidding-report-section/index.js new file mode 100644 index 0000000..6bff7da --- /dev/null +++ b/src/pages/bidding-report-section/index.js @@ -0,0 +1,118 @@ +import style from './style' +import styleRatings from '../../components/general-style/star-ratings' +import Network from '../../config/api' + +const infoMenu = import('../../components/bidding-info-menu') +const infoStatus = import('../../components/bidding-status') +const info = import('../../services/bidding-list-service') +const statusMessage = import('../../components/status-message') + +class template { + constructor (params) { + this.__params = params + this.__info = {} + + } + + __bindListeners () { + import('./actions/summary') + // load popup + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + popupes.then(loader => { + const a = new loader.default() + }) + + // load dropdown + import('../../utils/dropdown-loader').then(loader => new loader.default('device-dropdown')) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + + this.template = document.createElement('section') + + // template settings + this.template.setAttribute('style', 'margin-top:50px;padding-bottom:40px;height:100vh;overflow-y:auto;') + this.template.classList.add('col-lg-12') + this.template.id = 'bids-report-section' + this.template.innerHTML = ` + +
+
+
+
+
Results
+
+
+
+ + + + +   + Change  + 10/2018 - 10/2018 +
+

+

+ 0 Total number of bidding requests +

+ +

+ 0 Normal +

+

+ 0 Exempted +

+
+
+
+ +
+ +
+
Utilization per Unit / Department
+
+ +
+ trending_up +
+
Engagement
+ This shows the actual number of bidding requests created by each department. By simply looking on this table, + you can analyze their engagement in bidding process +
+


+ +
+ +
+ +
+
+ ` + this.__bindListeners() + return this.template + } + +} + + +export { template } \ No newline at end of file diff --git a/src/pages/bidding-report-section/style.styl b/src/pages/bidding-report-section/style.styl new file mode 100644 index 0000000..221c671 --- /dev/null +++ b/src/pages/bidding-report-section/style.styl @@ -0,0 +1,3 @@ +.reviews-section + font-size: smaller + padding-bottom: 100px diff --git a/src/pages/bidding-req-section/actions.js b/src/pages/bidding-req-section/actions.js new file mode 100644 index 0000000..ec14ea1 --- /dev/null +++ b/src/pages/bidding-req-section/actions.js @@ -0,0 +1,73 @@ +const BiddingServ = import('../../services/bidding-list-service') +const ListItem = import('../../components/list-item') +const ListSection = import('../../components/list-section') +const DropdownLoader = import('../../utils/dropdown-loader') + +const loadPopup = () => { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + +} + + + + /** + * Attachment Components + */ + const loadAttachments = (target, data) => { + + import('../../components/requirement-attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // empty section + targ.innerHTML = '' + // append files + data.forEach((val ,index) => { + if(!val.locked) val.menus = ['remove'] + targ.append(new res.default(val)) + }) + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + loadPopup() + },2000) + }) + } + + + + /** + * Attachment Components + */ + const loadAwardees = (target, data) => { + + import('../../components/requirement-awardees-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // empty section + targ.innerHTML = '' + // append files + data.forEach((val ,index) => { + targ.append(new res.default(val)) + }) + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + loadPopup() + },2000) + }) + } + +export { loadAwardees, loadAttachments, loadPopup } \ No newline at end of file diff --git a/src/pages/bidding-req-section/index.js b/src/pages/bidding-req-section/index.js new file mode 100644 index 0000000..26a62f9 --- /dev/null +++ b/src/pages/bidding-req-section/index.js @@ -0,0 +1,315 @@ +const infoMenu = import('../../components/requirement-menu') +const info = import('../../services/bidding-req-service') +const infoStatus = import('../../components/bidding-status') +const statusMessage = import('../../components/requirement-status') + +class template { + constructor (params) { + this.__params = params + this.__info = {} + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + info.then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + async __bindListeners (loader, template) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // request for approval + if (loader.isCBAAsst()) { + document.querySelector('.file-attachment-requirement-dialog-btn').addEventListener('click', async () => { + const __serv = (await import('../../components/requirement-attachments-dialog')).default + return new __serv({ id: this.__info.id, selector: '.file-attachment-requirement-dialog-btn', target: 'body'}) + }) + } + } + + __loadPopup () { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + + } + + __loadAdditionalComponents () { + setTimeout(() => { + // dropdown + import('../../utils/dropdown-loader').then(loader => loader.default('device-dropdown')) + this.__loadPopup() + },2000) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + /** + * Attachment Components + */ + async getAttachments() { + const loadAttachments = (await import('./actions')).loadAttachments + // prevent deletion for already closed bidding + if(this.__info.bidding_status == 5) this.__info.attachments.map(t => { + return t.locked = true + }) + + return loadAttachments('#attachments-requirements-info-section', this.__info.attachments) + } + + + /** + * Attachment Components + */ + async getAwardees() { + const loadAwardees = (await import('./actions')).loadAwardees + return this.__info.awardees.length ? (loadAwardees('#awardees-section-list', this.__info.awardees)) | (document.getElementById('awardees-section').classList.remove('hide')) : '' + } + + + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + this.__info = await this.__getInfo(this.__params.id) + const template = document.createElement('section') + let __funds = '' + let __specs = '' + + this.__info.funds.map(el => { + return __funds+=`${el.fund_type} - ${el.cost_center} ${el.line_item} ` + }) + + this.__info.specs.map(el => { + return __specs+=`
${el.name}
+

${el.value}

` + }) + + const __price = new Intl.NumberFormat('en-us').format(this.__info.budget_amount) + + // this.__info.specs.forEach(el,) + + // template settings + template.setAttribute('style', 'background:#fff;margin-top:50px;position:relative;box-shadow:0px 0px 10px rgba(200,200,200,0.4);height:100vh;overflow-y:auto;padding-bottom:200px;') + template.classList.add('col-lg-7') + template.id = 'requirement-container' + template.innerHTML = ` +
+
+ +
+ +
+ + +
+
+ +
+

${this.__info.name}

+ +

+ Reference # : ${this.__info.id}
+ Amount : + ${this.__info.budget_currency} + ${__price}
+ Quantity : ${this.__info.quantity} ${this.__info.unit}

+ ${__funds}
+ Deadline: ${this.__info.deadline || 'Not Set'} +

+ +

+ +

+

+ +
+
+ Winning Bidder(s) +
+
+
+
+
+ add_shopping_cart + Specifications +

+ +
+
` + this.template = template + return template + } + + + /** + * Show menu based on payload + * + * @param {Object} opt + */ + showMenu (opt) { + return new Promise((resolve, reject) => { + infoMenu.then(res => { + return new res.default(opt).then((html) => { + const targ = document.querySelector('requirement-info-menu') + if (targ) { + targ.replaceWith(html) + } else { + document.querySelector('#requirement-info-menu').replaceWith(html) + } + + resolve() + }) + }) + }) + } + + + + /** + * Get users privilege via privilege API and assign message per status + * + * @param {Object} loader + * @param {Object} status + */ + setPayload (loader, status) { + this.__payload = {} + this.__payload_menu = { + id: this.__params.id, + bidding_id: this.__info.bidding_id, + menus: ['back', 'attach'] + } + + + if(this.__info.awardees.length) this.__payload = status.showAwardedStatus() + + if(this.__info.bidding_status == 5) return this.__payload_menu.menus = ['back'] + + + if(this.__info.awardees.length && loader.isCBAAsst()) this.__payload_menu.menus.push('winner') + + if(loader.isGSU()) this.__payload_menu.menus = ['back', 'attach', 'deadline', 'invite'] + if(!this.__info.awardees.length && loader.isCBAAsst()) this.__payload_menu.menus = ['back', 'attach', 'winner', 'deadline', 'invite'] + } + + + /** + * Render Status in DOM + * + * @param {Object} payload + */ + getStatus(payload) { + // render + return infoStatus.then(res => { + return new Promise((resolve, reject) => { + return new res.default(this.__payload).then(html => { + if(!Object.keys(this.__payload).length) return resolve() + const __targ = document.querySelector('#requirement-menu-status') + __targ.innerHTML = '' + __targ.append(html) + resolve(html) + }) + }) + }) + } + + + async setStatus () { + const status = await statusMessage + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // set menu and payload based on user privilege + return await import('../../utils/privilege-loader').then(loader => { + this.privilegeLoader = loader + this.setPayload(loader, status) + this.showMenu(this.__payload_menu).then(() => { + console.log('show') + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + popupes.then(loader => { + const a = new loader.default() + }) + }) + + this.getStatus(this.__payload).then(temp => { + this.__bindListeners(loader, temp) + // change background + // hack ONLY + const __styl = document.createElement('style') + __styl.innerHTML = ` + .congrats-banner:after { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom:0; + left: 0; + background:url('assets/img/confetti.png') repeat center; + z-index:-1; + opacity:0.15; + }` + const __targ = document.getElementById('bidding-status') + if(this.__info.awardees.length) __targ.append(__styl) | __targ.classList.add('congrats-banner') + }) + + // get recepients + import('../../components/requirement-recepient-item').then(loader => { + const __recSection = document.querySelector('.attachment-recepients-section') + this.__info.recepients.forEach((val, index) => { + __recSection.append(new loader.default({ + id: val.id, + name: val.name, + })) + }) + + }).then(() => { + this.__loadAdditionalComponents() + }) + }) + } + + +} + + + + +export { template } \ No newline at end of file diff --git a/src/pages/bidding/forms/proposals/index.html b/src/pages/bidding/forms/proposals/index.html deleted file mode 100644 index 57c7e3f..0000000 --- a/src/pages/bidding/forms/proposals/index.html +++ /dev/null @@ -1,118 +0,0 @@ -
- - - - -
-
-
-
- - -
-
-
-

-

N/A

- - -

- Unit Price : - PHP - - - - -

- -

- Total Amount (Unit Price X Quantity) :
- - - - This will be automatically filled out - - -

- -

- Discount (for all items) : - - - - -

- -

Quantity :

- -

-
- -
-

-

- Specifications
- Please fill up all the fields below - -

-
- -
- - -
-


-

- - Others - (optional) - - - add_circle - -
- - These are additional specifications not included in the original bidding request. - - -

- -
- -
- - -
-


-

- - Remarks - (optional) - -
- - Add additional content in your proposal. Please make it short and simple - - - - - -

- -
- -
- -
- - -
-
diff --git a/src/pages/bidding/forms/registration/attachments.html b/src/pages/bidding/forms/registration/attachments.html deleted file mode 100644 index 2e415d9..0000000 --- a/src/pages/bidding/forms/registration/attachments.html +++ /dev/null @@ -1,31 +0,0 @@ -
-
-
- -
-

4/4 Steps

-

Attachments

-
- -
-

File Attachments

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut a.

-
- -
-
- - -
- - -
- - - -
-
- - -
-
diff --git a/src/pages/bidding/forms/registration/particulars.html b/src/pages/bidding/forms/registration/particulars.html deleted file mode 100644 index 5345ecf..0000000 --- a/src/pages/bidding/forms/registration/particulars.html +++ /dev/null @@ -1,40 +0,0 @@ -
-
-
- - - - -
-
-

- - add_shopping_cart -

Particulars (Required)
-

This will help to easily identify what this request is intended for

- -

- -

- date_range -

Date needed
- -

- -
- - -
- - - -
- - - -
-
- \ No newline at end of file diff --git a/src/pages/bidding/forms/registration/products.html b/src/pages/bidding/forms/registration/products.html deleted file mode 100644 index 3fd5c6b..0000000 --- a/src/pages/bidding/forms/registration/products.html +++ /dev/null @@ -1,160 +0,0 @@ -
-
-
- - -



-

add_shopping_cart Products / Services

-
-
-

Please complete all the fields below. Make sure that you checked all information before submitting.

-
- -
-
-
- -

- Item Name / Service (Required) -

e.i. HP Eliteone 800 G3, APC UPS, etc...

- -

- - -
- Quantity -

Number of items needed

- -
-
- Unit -

pack , piece , sacks etc..

- -
-
- - - -
- -

Source of Fund

-
- -
- - -
-
- -
-
- - -
-
-
add
-
-
-
- - - -
-

Budget

- -
- -
-
- -
-
- - -
-
-

Bidding exception ?

-
-
- -
-
- -
-
-
- - -
- -

Specifications

-

Write complete specification for this product/service

- -
- -
- -
-
- -
-
-
add
-
-
-
- -
- - -
-
- - -
-
- - -
-
- \ No newline at end of file diff --git a/src/pages/bidding/forms/registration/registration.html b/src/pages/bidding/forms/registration/registration.html deleted file mode 100644 index 380c3f6..0000000 --- a/src/pages/bidding/forms/registration/registration.html +++ /dev/null @@ -1,85 +0,0 @@ - -
-
-
- -
-

-

One moment please . . .

-

- We always like to make everything easier for you.
Simply answer the question below and we are good to go! -

- -

-

-

-

- - Answer
- expand_more -
-

-
-

-

-


-
-
- -
-
- - - -
- - Do you PREFER bidding exemption ? -

This will allow CBA assistant to know if you want a bidding exemption.
Please keep in mind that the final decision is still under CBA Assistant's control

-
- -

- No (Normal Bidding Process)   - Yes

- -
- - - - - - -
- -
- - -
-
- \ No newline at end of file diff --git a/src/pages/bidding/forms/registration/success.html b/src/pages/bidding/forms/registration/success.html deleted file mode 100644 index 39c66e3..0000000 --- a/src/pages/bidding/forms/registration/success.html +++ /dev/null @@ -1,22 +0,0 @@ -
-
-
- - -



- -
- -
-
-

check_circle Success!

-

Thank you for submitting an item for bidding. Do you want to submit more ?

-
- - -
-
- - -
-
diff --git a/src/pages/bidding/info.html b/src/pages/bidding/info.html deleted file mode 100644 index b263e74..0000000 --- a/src/pages/bidding/info.html +++ /dev/null @@ -1,121 +0,0 @@ -
- - - - - - -
- -
- -
-
- -
- - -

Bidding Request

- -
- - print PRINT - - -
- -
- - -

- Bidding Exemption : N/A
- Sent to :

-

-
-
-

-
- -

-
-
- -
-

-
-
\ No newline at end of file diff --git a/src/pages/bidding/invitation/info.html b/src/pages/bidding/invitation/info.html deleted file mode 100644 index 2ce405c..0000000 --- a/src/pages/bidding/invitation/info.html +++ /dev/null @@ -1,113 +0,0 @@ - - -
- - x -
- -
-
-
- - - - - - - - -
- -
- -
- - -
- -
- -
-

- -
- - -

- Bidding Item #: Not Set
- Reference #: Not Set
- Quantity :
- Deadline: Not Set -


-

- Attachments -

- - -
- -

- -

- -

-

- - -
-
-
- add_shopping_cart - Specification -

- -
\ No newline at end of file diff --git a/src/pages/bidding/invitation/list.html b/src/pages/bidding/invitation/list.html deleted file mode 100644 index ca8c38c..0000000 --- a/src/pages/bidding/invitation/list.html +++ /dev/null @@ -1,73 +0,0 @@ - - \ No newline at end of file diff --git a/src/pages/bidding/list.html b/src/pages/bidding/list.html deleted file mode 100644 index d0c81df..0000000 --- a/src/pages/bidding/list.html +++ /dev/null @@ -1,75 +0,0 @@ - - - \ No newline at end of file diff --git a/src/pages/bidding/modal/approve.html b/src/pages/bidding/modal/approve.html deleted file mode 100644 index 9cdc488..0000000 --- a/src/pages/bidding/modal/approve.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Approve

-

You will not be able to make any changes once you approve this bidding request. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/award-attachments-notice.html b/src/pages/bidding/modal/award-attachments-notice.html deleted file mode 100644 index 412cf65..0000000 --- a/src/pages/bidding/modal/award-attachments-notice.html +++ /dev/null @@ -1,19 +0,0 @@ - -
-

-

Upload Award Letter / Resolution

-

Please attach scanned award letter.

- - -
- -
- - - -
-
diff --git a/src/pages/bidding/modal/award-proposal.html b/src/pages/bidding/modal/award-proposal.html deleted file mode 100644 index e1ce31f..0000000 --- a/src/pages/bidding/modal/award-proposal.html +++ /dev/null @@ -1,52 +0,0 @@ - -
-
- -

Award

-

Officiay award this bidding to the selected proposal/supplier

-
- - -
-
- -
diff --git a/src/pages/bidding/modal/change_date.html b/src/pages/bidding/modal/change_date.html deleted file mode 100644 index f014b68..0000000 --- a/src/pages/bidding/modal/change_date.html +++ /dev/null @@ -1,27 +0,0 @@ - -
-
- date_range -

Select Date

- Plese avoid date not more than 5 years as it will yield too much information and might take long to display -
-
-
- From :
- -
- -
- To :
- -
-
-
- - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/deadline.html b/src/pages/bidding/modal/deadline.html deleted file mode 100644 index c36e97d..0000000 --- a/src/pages/bidding/modal/deadline.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
-

Set Deadline

-

This will only allow suppliers to submit their proposals within specified date.

- -

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/disapprove.html b/src/pages/bidding/modal/disapprove.html deleted file mode 100644 index 5cf1e5b..0000000 --- a/src/pages/bidding/modal/disapprove.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Disapprove

-

You will not be able to make any changes once you disapprove this bidding request. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/pr.html b/src/pages/bidding/modal/pr.html deleted file mode 100644 index e8f0801..0000000 --- a/src/pages/bidding/modal/pr.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Send PR

-

This item will be added to Procsys. Are you sure you want to continue ?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/proposal-attachments-notice.html b/src/pages/bidding/modal/proposal-attachments-notice.html deleted file mode 100644 index da8ad9a..0000000 --- a/src/pages/bidding/modal/proposal-attachments-notice.html +++ /dev/null @@ -1,26 +0,0 @@ - -
-

-

Please attach scanned formal quotation

-

Follow the instructions below to upload file

- -
- -
- -
- -
- - -
-
diff --git a/src/pages/bidding/modal/request-proposal.html b/src/pages/bidding/modal/request-proposal.html deleted file mode 100644 index 5698dfa..0000000 --- a/src/pages/bidding/modal/request-proposal.html +++ /dev/null @@ -1,38 +0,0 @@ - -
-

Request new proposal

-

This will notify the supplier that you are requesting for changes with their proposal

- -

- - * Required - -

- - - -

-
- - -
-
diff --git a/src/pages/bidding/modal/save-proposals.html b/src/pages/bidding/modal/save-proposals.html deleted file mode 100644 index 995c91b..0000000 --- a/src/pages/bidding/modal/save-proposals.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

save Save

-

Please review the details before proceeding. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/send-proposals.html b/src/pages/bidding/modal/send-proposals.html deleted file mode 100644 index ed9aeae..0000000 --- a/src/pages/bidding/modal/send-proposals.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Send

-

You will not be able to update the content of this proposal. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/send-requirements-item.html b/src/pages/bidding/modal/send-requirements-item.html deleted file mode 100644 index e69d88d..0000000 --- a/src/pages/bidding/modal/send-requirements-item.html +++ /dev/null @@ -1,55 +0,0 @@ - -
-

mail Send

-

Please select item(s) and supplier(s) below

-
- -
- Items / Services -
-
-
-
- - -
- Suppliers - - -
- - -
-
-
-
-
- -

- -

Are you sure you want to continue?

- - -
- - - \ No newline at end of file diff --git a/src/pages/bidding/modal/send-requirements.html b/src/pages/bidding/modal/send-requirements.html deleted file mode 100644 index 41c54bd..0000000 --- a/src/pages/bidding/modal/send-requirements.html +++ /dev/null @@ -1,44 +0,0 @@ - -
-

mail Send

-

Please select a supplier

- - -
- -

- Suppliers -
- -
-
-
-
- - -

-

Are you sure you want to continue?

- - -
- - \ No newline at end of file diff --git a/src/pages/bidding/modal/send-to-cba.html b/src/pages/bidding/modal/send-to-cba.html deleted file mode 100644 index 42130eb..0000000 --- a/src/pages/bidding/modal/send-to-cba.html +++ /dev/null @@ -1,47 +0,0 @@ - -
-

Send

- -

Please review before sending. You will not be able to make any changes after you send it to the recepient. - Only proceed if you know what you are doing. -

-
-
- -

- CBA Assistant(s) -
- -
-
-
- -
- - -

-

Are you sure you want to continue?

- - -
- - \ No newline at end of file diff --git a/src/pages/bidding/modal/send.html b/src/pages/bidding/modal/send.html deleted file mode 100644 index b27b402..0000000 --- a/src/pages/bidding/modal/send.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Send / Receive

-

You will not be able to update the content of this item after you send it to the recepient. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/set-reference.html b/src/pages/bidding/modal/set-reference.html deleted file mode 100644 index 9a0c11c..0000000 --- a/src/pages/bidding/modal/set-reference.html +++ /dev/null @@ -1,19 +0,0 @@ - -
-
-

format_list_numbered PR/PO

-

Please specify a unique number for this item.

-

- -

- - -
-
\ No newline at end of file diff --git a/src/pages/bidding/modal/status.html b/src/pages/bidding/modal/status.html deleted file mode 100644 index 219c673..0000000 --- a/src/pages/bidding/modal/status.html +++ /dev/null @@ -1,12 +0,0 @@ - -
-

Change Status

-

Users received this request might not longer able to modify its content. Are you sure you want to continue?

- - - -
diff --git a/src/pages/bidding/modal/winner-proposal.html b/src/pages/bidding/modal/winner-proposal.html deleted file mode 100644 index 71b20e7..0000000 --- a/src/pages/bidding/modal/winner-proposal.html +++ /dev/null @@ -1,43 +0,0 @@ - -
-
-

Winner

- -

This proposal will be tagged as the official winner for this bidding.

-
- -
-
- Remarks
- -
-

-
-
-

Are you sure you want to continue?

- - -
-
diff --git a/src/pages/bidding/reports/index.html b/src/pages/bidding/reports/index.html deleted file mode 100644 index 36cabba..0000000 --- a/src/pages/bidding/reports/index.html +++ /dev/null @@ -1,104 +0,0 @@ -
-
-
- -
-
Results
-
-
-
- - - - -   - Change  - 10/2018 - 10/2018 -
-

-

- 0 Total number of bidding requests -

- -

- 0 Normal -

-

- 0 Exempted -

-
-
-
- -
- -
-
Utilization per Unit / Department
-
- -
- trending_up -
-
Engagement
- This shows the actual number of bidding requests created by each department. By simply looking on this table, - you can analyze their engagement in bidding process -
-


- -
- -
- -
- - -
- - \ No newline at end of file diff --git a/src/pages/bidding/requirements.html b/src/pages/bidding/requirements.html deleted file mode 100644 index 05cc16f..0000000 --- a/src/pages/bidding/requirements.html +++ /dev/null @@ -1,123 +0,0 @@ - - -
- - x -
- -
-
-
- - - - - - - - -
- -
- -
- - -
- -
- -
-

- - -

- Reference # :
- Amount : - PHP - .00
- Quantity :

-
- Deadline: Not Set -

- -

- -

-

- -
-
- Winning Bidder(s) -
- -
- - - - -
-
-
- add_shopping_cart - Specification -

- -
\ No newline at end of file diff --git a/src/pages/feedback-form/index.js b/src/pages/feedback-form/index.js new file mode 100644 index 0000000..f017e77 --- /dev/null +++ b/src/pages/feedback-form/index.js @@ -0,0 +1,95 @@ +import Quill from 'quill' + +class template { + constructor (params) { + this.__params = params + this.__info = {} + this.__quill = '' + + } + + __bindListeners () { + this.__loadQuill() + this.__send() + } + + __send () { + return this.template.querySelector('.add-feedback-button').addEventListener('click', () => { + const content = this.__quill.root.innerHTML + const quillCount = this.__quill.getText().length > 5 + const __payload = { + token: window.localStorage.getItem('token'), + feedback: content, + action: 'create' + } + + if (quillCount) { + import('../../services/feedback-services').then(res => { + return new res.default().feedback(__payload).then((data) => { + if (data > 0) { + // show success + this.template.innerHTML = ` +
+
+ tag_faces Thank you for sharing your experience with us. Your feedback matters! +
+
+ ` + } + }) + }) + } + }) + } + + __loadQuill () { + this.__quill = new Quill(this.template.querySelector('#editor'), { + modules: { + toolbar: [ + [{ header: [1, 2, false] }], + ['bold', 'italic', 'underline', 'link'], + ['code-block'], + [{ 'list': 'ordered'}, { 'list': 'bullet' }], + ] + }, + placeholder: 'Compose an epic...', + theme: 'snow' // or 'bubble' + }) + // stylesheet + const styl = document.createElement('link') + styl.href = 'https://cdn.quilljs.com/1.3.6/quill.snow.css' + styl.rel = 'stylesheet' + styl.type = 'text/css' + this.template.append(styl) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + + this.template = document.createElement('article') + + // template settings + this.template.setAttribute('style', "margin-top: 150px;overflow:auto;padding-bottom: 30px;") + this.template.classList.add('col-12', 'col-lg-7', 'col-md-7', 'offset-lg-2') + this.template.id = 'feedback-section' + this.template.innerHTML = ` +
+
+

+

feedback Feedback

+

We want to hear your experience in using the system. This will help us to improve the system and make the best out of it.

+




+
+
+

+
` + this.__bindListeners() + return this.template + } + +} + + +export { template } \ No newline at end of file diff --git a/src/pages/feedback/forms/registration.html b/src/pages/feedback/forms/registration.html deleted file mode 100644 index 0503b02..0000000 --- a/src/pages/feedback/forms/registration.html +++ /dev/null @@ -1,24 +0,0 @@ - -
-
- -
-

-

feedback Feedback

-

We want to hear your experience in using the system. This will help us to improve the system and make the best out of it.

-

-


-
-
-
-
-
- - -
- - - - - -
\ No newline at end of file diff --git a/src/pages/home-section/index.js b/src/pages/home-section/index.js new file mode 100644 index 0000000..fa63a6a --- /dev/null +++ b/src/pages/home-section/index.js @@ -0,0 +1,15 @@ +export default ` +
+
+
+

+ bms welcome +
+ +
+

+

Bidding Management System

+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

+
+
+
` \ No newline at end of file diff --git a/src/pages/index.html b/src/pages/index.html deleted file mode 100644 index 0432975..0000000 --- a/src/pages/index.html +++ /dev/null @@ -1,191 +0,0 @@ - - - - - - - - - - - - - - - - Login - - - - -
-
-
- -

SEARCA

-


Bidding Management System

-
-
-

- Compare supplier's price easier , faster and better than before! Be the first to use the new and advanced bidding management system -

-

-
-

Sign-in with

- -

- -

- -

OR

- - - -

-
- -
- -
- -
- - - -
- - -
- - -
- -
- - - - - - - - - - diff --git a/src/pages/initial-invitation-section/index.js b/src/pages/initial-invitation-section/index.js new file mode 100644 index 0000000..83f48cd --- /dev/null +++ b/src/pages/initial-invitation-section/index.js @@ -0,0 +1,22 @@ +import style from './style' +const template = document.createElement('div') + +template.name = "/bids/initial" +template.id = 'initial-section' +template.classList.add('col', 'col-lg-10', 'col-sm-12', 'col-xs-12', 'max-height-panel') +template.innerHTML = ` + +
+
+

+

Send your proposal anytime, anywhere

+ +

+ Select an invitation from the list and start your awesome proposal +

+
+ +
` + + +export default template \ No newline at end of file diff --git a/src/pages/initial-invitation-section/style.styl b/src/pages/initial-invitation-section/style.styl new file mode 100644 index 0000000..20e1670 --- /dev/null +++ b/src/pages/initial-invitation-section/style.styl @@ -0,0 +1,3 @@ +#initial-section + padding-top: 70px + background: #fcfcfc \ No newline at end of file diff --git a/src/pages/initial-section/index.js b/src/pages/initial-section/index.js new file mode 100644 index 0000000..1f5846d --- /dev/null +++ b/src/pages/initial-section/index.js @@ -0,0 +1,22 @@ +import style from './style' +const template = document.createElement('div') + +template.name = "/bids/initial" +template.id = 'initial-section' +template.classList.add('col', 'col-lg-9', 'col-sm-12', 'col-xs-12', 'max-height-panel') +template.innerHTML = ` + +
+
+

+

Bidding Management System

+ +

+ Compare supplier's price easier , faster and better than before! Be the first to use the new and advanced bidding management system +

+
+ +
` + + +export default template \ No newline at end of file diff --git a/src/pages/initial-section/style.styl b/src/pages/initial-section/style.styl new file mode 100644 index 0000000..20e1670 --- /dev/null +++ b/src/pages/initial-section/style.styl @@ -0,0 +1,3 @@ +#initial-section + padding-top: 70px + background: #fcfcfc \ No newline at end of file diff --git a/src/pages/invitation-info-section/actions.js b/src/pages/invitation-info-section/actions.js new file mode 100644 index 0000000..679eb3c --- /dev/null +++ b/src/pages/invitation-info-section/actions.js @@ -0,0 +1,98 @@ +const BiddingServ = import('../../services/bidding-list-service') +const ListItem = import('../../components/list-item') +const ListSection = import('../../components/list-section') +const DropdownLoader = import('../../utils/dropdown-loader') + +const loadPopup = () => { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + +} + +/** + * Load List section + */ +const loadListSection = () => { + return new Promise((resolve, reject) => { + ListSection.then(sec => { + const lSec = document.querySelector('list-section') || document.querySelector('.list-bids-container') + if (!lSec) return 0 + // list section + const newLSec = document.createElement('article') + newLSec.classList.add('list-bids-container', 'd-none', 'd-lg-block', 'col-lg-2') + newLSec.style.zIndex = 1 + newLSec.innerHTML = sec.default + lSec.replaceWith(newLSec) + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + // resolve + resolve() + }) + }).catch(err => { + reject(err) + }) +} + +/** + * Bidding List AJAX + */ +const getBiddingList = (opt = {}) => { + // load Via AJAX + const listSection = document.querySelector('.list-bidding-section') + BiddingServ.then(loader => { + const a = new loader.default() + a.lists({ token : window.localStorage.getItem('token'), filter: opt.filter || 'all' }).then(res => { + + // clear section for page 1 + if(opt.page < 2) listSection.innerHTML = '' + + // items + ListItem.then(item => { + // items + res.forEach((el, index) => { + const lItem = new item.default({class: 'col-12 list', id: el.id, profile_name: el.profile_name, date_created: el.date_created}) + listSection.append(lItem) + }) + + }) + }) + }) +} + + + /** + * Attachment Components + */ + const loadAttachments = (target, data) => { + + import('../../components/attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // empty section + targ.innerHTML = '' + // append files + data.forEach((val ,index) => { + if(!val.locked) val.menus = ['remove'] + targ.append(new res.default(val)) + }) + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + loadPopup() + },2000) + }) + } + +export { loadListSection, getBiddingList, loadAttachments } \ No newline at end of file diff --git a/src/pages/invitation-info-section/index.js b/src/pages/invitation-info-section/index.js new file mode 100644 index 0000000..d81a39e --- /dev/null +++ b/src/pages/invitation-info-section/index.js @@ -0,0 +1,205 @@ +const Network = import('../../config/api') +const infoMenu = import('../../components/bidding-info-menu') +const infoStatus = import('../../components/bidding-status') +const info = import('../../services/bidding-inv-service') +const statusMessage = import('../../components/status-inv-message') + +class template { + constructor (params) { + this.__params = params + this.__info = {} + } + + __loadProposalSection () { + // load proposal section + return import('../../pages/requirement-proposal-section').then(res => { + // template + const temp = new res.default({ + id: this.__info.id, + invitation_id: this.__params.id, + }) + + temp.then(html => { + // DOM + const reqSec = document.querySelector('proposal-section') || document.querySelector('#requirement-proposal-container') + if(reqSec) reqSec.replaceWith(html) + }) + }) + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + const net = (await Network).default + const template = document.createElement('section') + let __specs = '' + let __attachments = '' + this.__info = await this.__getInfo(this.__params.id) + // specs + this.__info.specs.map(el => { + return __specs+=`
${el.name}
+

${el.value}

` + }) + //attachments + this.__info.attachments.map(el => { + return __attachments+=`` + }) + + + // template settings + template.setAttribute('style', 'margin-top:50px;padding-bottom:40px;height:100vh;overflow-y:auto;') + template.classList.add('col-lg-7') + template.id = 'inv-info-container' + template.innerHTML = ` +
+
+ +
+ +
+ +
+
+ +
+

${this.__info.name}

+ + +

+ Bidding Item #: ${this.__info.bidding_id}
+ Reference #: ${this.__info.id}
+ Quantity : ${this.__info.quantity} ${this.__info.unit}
+ Deadline: ${this.__info.deadline} +


+ + +

+ Attachments +

+ + + +
+

+

+ +

+

+

+ + +
+ add_shopping_cart + Specification +

+
${__specs}
+ +
` + this.template = template + return template + } + + + + async __bindListeners (loader, template) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + info.then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + + /** + * Get users privilege via privilege API and assign message per status + * + * @param {Object} loader + * @param {Object} status + */ + setPayload (loader, status) { + this.__payload = {} + + if (this.__info.status == 0) { + this.__payload = status.showInitial(this.__info.id) + } + } + + /** + * Render Status in DOM + * + * @param {Object} payload + */ + getStatus(payload) { + // render + return new Promise((resolve, reject) => { + infoStatus.then(res => { + return new res.default(this.__payload).then(html => { + const a = (!document.querySelector('#bidding-status')) ? document.querySelector('#detail-info-menu-status').prepend(html) : document.querySelector('#bidding-status').replaceWith(html) + resolve(html) + }) + }) + }) + } + + async setStatus () { + const status = await statusMessage + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // set menu and payload based on user privilege + return await import('../../utils/privilege-loader').then(loader => { + this.privilegeLoader = loader + + this.setPayload(loader, status) + + this.getStatus(this.__payload).then(temp => { + //this.__bindListeners(loader, temp) + }) + this.__loadProposalSection() + }) + } + + +} + + +export { template } \ No newline at end of file diff --git a/src/pages/invitation-section/actions.js b/src/pages/invitation-section/actions.js new file mode 100644 index 0000000..c4a5508 --- /dev/null +++ b/src/pages/invitation-section/actions.js @@ -0,0 +1,195 @@ +const Serv = import('../../services/bidding-inv-service') +const ListSection = import('../../components/list-section') +const DropdownLoader = import('../../utils/dropdown-loader') + +let timeout = {} + +const loadPopup = () => { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + +} + + +const showEmptySearch = () => { + const targ = document.querySelector('.list-search-bidding-section') + targ.innerHTML = ` +
+ search +

+ No matches found.
Please try another keyword
+

+
` +} + +const search = (e) => { + const targ = document.querySelector('.list-search-bidding-section') + + let __payload = { + token : window.localStorage.getItem('token'), + param: e.target.value, + page: 1, + } + + if (e.target.value.length > 0) { + + // hide list and show search results + document.querySelector('.list-bidding-section').classList.add('hide') + targ.classList.remove('hide') + + // clear area + if (__payload.page === 1) targ.innerHTML = '
searching . . .
' + + // search + clearTimeout(timeout) + timeout = setTimeout(() => { + + Serv.then(loader => { + const a = new loader.default() + a.search(__payload).then(res => { + + // clear section for page 1 + if(__payload.page < 2) targ.innerHTML = '' + + // items + import('../../components/inv-item').then(item => { + // items + res.forEach((el, index) => { + const lItem = new item.default({class: 'col-12 list', id: el.id, name: el.name, quantity: el.quantity, unit: el.unit, bidding_requirements_id: el.bidding_requirements_id, deadline: el.deadline}) + targ.append(lItem) + }) + + // empty result + if (__payload.page === 1 && res.length === 0) { + showEmptySearch() + } + + }).catch((err) => showEmptySearch()) + }) + }) + + },500) + + } else { + // revert to normal + document.querySelector('.list-bidding-section').classList.remove('hide') + document.querySelector('.list-search-bidding-section').classList.add('hide') + } + +} + +const bindSearch = () => { + document.querySelector('#search').addEventListener('keyup', search) +} + +/** + * Load List section + */ +const loadListSection = () => { + return new Promise((resolve, reject) => { + ListSection.then(sec => { + const lSec = document.querySelector('list-section') || document.querySelector('.list-bids-container') + if (!lSec) return 0 + // list section + const newLSec = document.createElement('article') + newLSec.classList.add('list-bids-container', 'd-none', 'd-lg-block', 'col-lg-2') + newLSec.style.zIndex = 1 + newLSec.innerHTML = sec.default + newLSec.querySelector('.suppliers_new_button').parentNode.remove() + lSec.replaceWith(newLSec) + + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + bindSearch() + // resolve + resolve() + }) + }).catch(err => { + reject(err) + }) +} + +/** + * Bidding List AJAX + */ +const list = (opt = {}) => { + opt.page = opt.page || 1 + // load Via AJAX + const listSection = document.querySelector('.list-bidding-section') + Serv.then(loader => { + new loader.default().list({ token : window.localStorage.getItem('token'), filter: opt.filter || 'all', page: opt.page }).then(res => { + + // clear section for page 1 + if(opt.page < 2) listSection.innerHTML = '' + + // items + import('../../components/inv-item').then(item => { + // items + res.forEach((el, index) => { + const lItem = new item.default({class: 'col-12 list', id: el.id, name: el.name, quantity: el.quantity, unit: el.unit, bidding_requirements_id: el.bidding_requirements_id, deadline: el.deadline}) + listSection.append(lItem) + }) + + // more btn + setTimeout(() => { + const moreBtn = document.createElement('a') + moreBtn.href = '#' + moreBtn.textContent = 'More' + moreBtn.classList.add('col-12') + moreBtn.style.textAlign = 'center' + moreBtn.style.paddingBottom = '100px' + moreBtn.style.paddingTop = '10px' + moreBtn.opt = {} + moreBtn.opt.filter = opt.filter + moreBtn.opt.page = opt.page+1 + moreBtn.addEventListener('click', (e) => { + e.preventDefault() + list(e.target.opt) + moreBtn.remove() + }) + + return res.length > 0 ? listSection.append(moreBtn) : 0 + + }, 1000) + }) + }) + }) +} + + + /** + * Attachment Components + */ + const loadAttachments = (target, data) => { + + import('../../components/attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // empty section + targ.innerHTML = '' + // append files + data.forEach((val ,index) => { + if(!val.locked) val.menus = ['remove'] + targ.append(new res.default(val)) + }) + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + loadPopup() + },2000) + }) + } + +export { loadListSection, list, loadAttachments } \ No newline at end of file diff --git a/src/pages/invitation-section/index.js b/src/pages/invitation-section/index.js new file mode 100644 index 0000000..d60f6dc --- /dev/null +++ b/src/pages/invitation-section/index.js @@ -0,0 +1,286 @@ +const infoMenu = import('../../components/bidding-info-menu') +const infoStatus = import('../../components/bidding-status') +const info = import('../../services/bidding-list-service') +const statusMessage = import('../../components/status-message') + +class template { + constructor (params) { + this.__params = params + this.__info = {} + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + this.__info = await this.__getInfo(this.__params.id) + const template = document.createElement('section') + + // reviewed by + let arr = this.__info.collaborators.map(val => { + return `${val.profile_name}` + }) + // template settings + template.setAttribute('style', 'margin-top:50px;padding-bottom:40px;height:100vh;overflow-y:auto;') + template.classList.add('col-lg-10') + template.id = 'bids-info-container' + template.innerHTML = ` +
+
+ + +
+ +
+ +

Bidding Request #${this.__info.id}

+ Bidding Exemption : ${parseInt(this.__info.excemption) ? 'Yes' : 'No'}
+

Reviewed By : ${arr.join(' ')}

+ + +
+
+
${this.__info.profile_name.substring(0,2).toUpperCase()}
+

+ ${this.__info.profile_name}
+ ${this.__info.date_created} +

+
+
+ + +
+
Attachments
+
+ + +

+
Particulars
+
+
+

+
Customer Reviews
+
+
+
` + this.template = template + return template + } + + async __bindListeners (loader, template) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + + // request for approval + if (this.__info.status == 1 && loader.isCBAAsst()) { + const __serv = (await import('../../components/bidding-info-menu/actions/approve')).default + return new __serv({ id: this.__info.id, selector: '#btn-approve'}) + + } + + + if (this.__info.status == 3 && loader.isCBAAsst()) { + // close request + const __serv = (await import('../../components/bidding-info-menu/actions/close')).default + const __c = new __serv({ id: this.__info.id, selector: '#btn-close'}) + // failure of bidding + const __servF = (await import('../../components/bidding-info-menu/actions/fail')).default + const __f = new __servF({ id: this.__info.id, selector: '#btn-fail'}) + } + + + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + info.then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res.data[0]) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + /** + * Attachment Components + */ + async getAttachments() { + const loadAttachments = (await import('./actions')).loadAttachments + // prevent deletion for already closed bidding + if(this.__info.status == 5) this.__info.attachments.map(t => { + return t.locked = true + }) + + return loadAttachments('.attachments-info-section', this.__info.attachments) + } + + /** + * Show menu based on payload + * + * @param {Object} opt + */ + showMenu (opt) { + return new Promise((resolve, reject) => { + infoMenu.then(res => { + return new res.default(opt).then((html) => { + const targ = document.querySelector('bidding-info-menu') + if (targ) { + targ.replaceWith(html) + } else { + document.querySelector('#detail-info-menu').replaceWith(html) + } + + resolve() + }) + }) + }) + } + + + /** + * Get users privilege via privilege API and assign message per status + * + * @param {Object} loader + * @param {Object} status + */ + setPayload (loader, status) { + this.__payload = {} + this.__payload_menu = { + id: this.__params.id, + menus: [], + } + + // draft (For all users) + if (this.__info.status == 0) { + this.__payload = {} + this.__payload_menu = { + id: this.__params.id, + menus: ['send', 'attach', 'remove', 'update', 'particulars', 'print', 'sign'] + } + } + + // for regular USER + if (this.__info.status == 1 && !loader.isCBAAsst()) this.__payload = status.showBiddingReqSent(this.__info.id) + + // for CBA Asst /APPROVE + if (this.__info.status == 1 && loader.isCBAAsst()) { + this.__payload_menu = { + id: this.__params.id, + menus: ['return', 'attach', 'update', 'sign', 'particulars'], + } + + this.__payload = status.showBiddingReqApprove(this.__info.id) + } + + // for both + // must change to send to resend + if (this.__info.status == 2) (this.__payload_menu.menus = ['resend', 'attach', 'update', 'print', 'sign', 'particulars']) | (this.__payload = status.showBiddingReqReturned(this.__info.id)) + + if (this.__info.status == 3 && loader.isCBAAsst()) (this.__payload_menu.menus = ['resend', 'attach', 'print', 'sign', 'particulars']) | (this.__payload = status.showBiddingApproved(this.__info.id)) + + // for GSU + // enable all commands + if (this.__info.status == 3 && (loader.isGSU() || !loader.isCBAAsst())) this.__payload = status.showBiddingApproveReadOnlyStandard(this.__info.id) + + // closed + if (this.__info.status == 5) { + this.__payload = status.showBiddingClosed(this.__info.id) + this.__payload_menu.menus = ['print'] + } + + // disapproved + if (this.__info.status == 6) this.__payload = status.showBiddingDisapproved(this.__info.id) + + // failed + if (this.__info.status == 6) this.__payload = status.showBiddingFailed(this.__info.id) + + } + + /** + * Render Status in DOM + * + * @param {Object} payload + */ + getStatus(payload) { + // render + return new Promise((resolve, reject) => { + if (this.__info.status > 0) { + infoStatus.then(res => { + return new res.default(this.__payload).then(html => { + const a = (!document.querySelector('#bidding-status')) ? document.querySelector('#detail-info-menu-status').prepend(html) : document.querySelector('#bidding-status').replaceWith(html) + resolve(html) + }) + }) + } else { + // remove + const a = (!document.querySelector('#bidding-status')) ? document.querySelector('#detail-info-menu-status').innerHTML = '' : document.querySelector('#bidding-status').innerHTML = '' + resolve({}) + } + + }) + } + + + async setStatus () { + const status = await statusMessage + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // set menu and payload based on user privilege + return await import('../../utils/privilege-loader').then(loader => { + this.privilegeLoader = loader + this.setPayload(loader, status) + this.showMenu(this.__payload_menu).then(() => { + console.log('show') + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + popupes.then(loader => { + const a = new loader.default() + }) + }) + + this.getStatus(this.__payload).then(temp => { + this.__bindListeners(loader, temp) + }) + }) + } + + getParticulars () { + import('../../components/particulars-item').then(res => { + const targ = document.querySelector('.particulars-section') + this.__info.particulars.forEach((el, index) => { + targ.append(new res.default({id: this.__info.id, name: el.name, deadline: el.deadline, requirements: el.requirements})) + }) + }) + } +} + + +export { template } \ No newline at end of file diff --git a/src/pages/proposal-form/actions.js b/src/pages/proposal-form/actions.js new file mode 100644 index 0000000..0dfb858 --- /dev/null +++ b/src/pages/proposal-form/actions.js @@ -0,0 +1,227 @@ +import ApiConfig from '../../config/api' +const DropdownLoader = import('../../utils/dropdown-loader') + +export default class { + constructor (opt) { + this.opt = opt + this.__apiConfig = ApiConfig + this.filesToBeUploaded = {} + + } + + __showError () { + alert('Unable to process this request.Please try again later.') + } + + __appendFileToBeUploaded (e) { + const targ = document.querySelectorAll('.attachment-pool-section') + + // close dialog + document.querySelector('.file-attachment-main-dialog').classList.remove('open') + + // preview + targ.forEach((el, index) => { + for (var i = 0; i < e.target.files.length; i++) { + // add to DOM + el.innerHTML+=` +
+
+ ${e.target.files[i].name} + arrow_drop_down +
+
+
+
+ ` + // add to file uploader + this.save(e.target.opt.id, e.target.files[i], i) + } + }) + + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + } + + __bindSelectDeviceFile (opt) { + const el = document.querySelector(opt.selector) + const __proto = Object.assign({__proto__: this.__proto__}, this) + el.opt = opt + el.removeEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + el.addEventListener('change', this.__appendFileToBeUploaded.bind(__proto)) + } + + __bindSelectFile () { + const el = document.querySelectorAll('.attachment-recent-checkbox-bidding') + const __proto = Object.assign({__proto__: this.__proto__}, this) + + for(let x of el) { + x.removeEventListener('change', this.__enableUploadButton.bind(__proto)) + x.addEventListener('change', this.__enableUploadButton.bind(__proto)) + } + } + + __enableUploadButton(e) { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const id = e.target.getAttribute('data-resources') + const oFilename = e.target.getAttribute('data-original-filename') + + if(id&&e.target.checked) { + this.filesToBeUploaded[id] = oFilename + }else{ + delete this.filesToBeUploaded[id] + } + + // enable button + setTimeout(() => { + Object.keys(this.filesToBeUploaded ).length > 0 ? btn.removeAttribute('disabled') : btn.setAttribute('disabled', 'disabled') + },10) + + } + + + __showRecent (json) { + this.filesToBeUploaded = {} + + const html = document.createElement('div') + html.classList.add('recently-attached') + html.innerHTML = ` +
+ ${json.original_filename} + (${json.size}KB ${json.date_created})` + document.querySelector(`#recently-attached-section-${this.opt.id}`).append(html) + + } + + __bindAttach() { + const btn = document.getElementById(`file-attachment-upload-recent-btn-${this.opt.id}`) + const proto = Object.assign({__proto__: this.__proto__}, this) + btn.addEventListener('click', this.attach.bind(proto)) + } + + + async recent (opt) { + const __serv = (await import('../../services/bidding-attachment-service')).default + this.opt = this.opt || opt + // get from storage + return new __serv().recent(opt).then(data => { + data.data.forEach((val, index) => { + this.__showRecent(val) + }) + }).then(() => { + setTimeout(() => { + this.__bindSelectFile() + this. __bindAttach() + }, 800); + }) + } + + async attach (e) { + // disable button first + e.target.setAttribute('disabled', 'disabled') + + //payload + const __payload = { + attachments: this.filesToBeUploaded, + action: 'create', + id: this.opt.id, + token: window.localStorage.getItem('token') + } + + + const service = (await import('../../services/bidding-attachment-service')).default + return new service().attach(__payload).then(data => { + if (!data.data) return this._showError() + //show in DOM + for (var i = 0; i < data.data.length; i++) { + const __d = { + type: data.data[i].type, + original_filename: data.data[i].original_filename, + id: data.data[i].id, + menus: ['remove'] + } + this.loadAttachments('.attachments-info-section', [__d]) + } + // close dialog + document.querySelectorAll('.file-attachment-main-dialog').forEach((val, res) => val.close()) + + setTimeout(() => { + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + e.target.removeAttribute('disabled') + },1000) + }).catch(err => { + this._showError() + }) + } + + upload(opt) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + this.__bindSelectDeviceFile(opt) + } + + /** + * Attachment Components + */ + loadAttachments (target, data) { + import('../../components/attachments-item').then(res => { + const targ = document.querySelector(target) + if (!targ) return 0 + // append files + data.forEach((val ,index) => { + targ.append(new res.default(val)) + }) + }) + } + + + save(id,file,index) { + //form data + const formData = new FormData() + formData.append('files', file) + formData.append('id', id) + formData.append('token', window.localStorage.getItem('token')) + + //XHR + let request = new XMLHttpRequest(); + request.open("POST", `${this.__apiConfig.url}/bidding/attachments/`); + + request.upload.addEventListener('progress', (e) => { + let targ = document.getElementById(`progress-bar-${index}`) + if (e.lengthComputable) { + let total = (e.total / e.loaded ) * 100 + targ.style.width=`${total}%` + } + }) + + request.addEventListener('load', (e) => { + + let targ = document.getElementById(`progress-bar-${index}`) + + if(request.responseText > 0) { + targ.parentNode.parentNode.remove() + // DOM + const __payload = { + id: request.responseText, + type: file.type.split('/')[1], + original_filename: file.name, + menus: ['remove'] + } + + this.loadAttachments('.attachments-info-section', [__payload]) + // more settings + setTimeout(() => { + // dropdown and popup menu + DropdownLoader.then(loader => new loader.default('device-dropdown')) + import('../../components/popup-es').then(loader => new loader.default()) + + },1000) + + }else{ + targ.parentNode.textContent = 'Error uploading' + } + }) + + request.send(formData); + + } +} \ No newline at end of file diff --git a/src/pages/proposal-form/actions/create.js b/src/pages/proposal-form/actions/create.js new file mode 100644 index 0000000..999e01e --- /dev/null +++ b/src/pages/proposal-form/actions/create.js @@ -0,0 +1,133 @@ +const BiddingServ = import('../../../services/bidding-proposal-service') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bind() + } + + + hideSpinner () { + const targ = document.querySelector('#inv-info-container > .spinner') + if (targ) targ.hide() + } + + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + this.hideSpinner() + } + + /** + * Display a notice to attach their formal quotation before proceeding + * @param {Object} e - MouseEvent + * @returns XMLHttpRequest + */ + __loadProposalAttachmentsNotice () { + const stat = document.querySelector('#bidding-status') + + import('../../../components/proposal-attachments-notice-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }).catch(err=>{ + console.log(err) + }) + + this.hideSpinner() + + //const URL='pages/bidding/modal/proposal-attachments-notice.html' + + // show reload status + stat.innerHTML = '
This bidding requirement has been modified. Please reload this page to see changes reload
' + + // close form + document.querySelector('.file-attachment-main-dialog').close() + // allow attachment + /*let btn = document.getElementById('modal-dialog-send-button') + if (!document.querySelector('.file-prop-attachment-dialog-btn')) { + btn.classList.add('file-prop-attachment-dialog-btn') + } + // autoclose modal + btn.addEventListener('click', () => { + document.getElementById('bidding-modal').close() + }) + + window.bms.default.lazyLoad(['./assets/js_native/assets/js/modules/Invitation/Util/AttachmentsModal.js'])*/ + } + + /** + * @param {Object} e - MouseEvent + */ + async create(e) { + const __serv = (await BiddingServ).default + const amount = document.getElementById('proposal-form-amount').value + const discount = document.getElementById('proposal-form-discount').value + const remarks = document.getElementById('proposal-form-remarks').value + + let original = [] + let others = [] + + e.target.disabled = 'disabled' + + document.querySelectorAll('.specs-input-section-orig').forEach((el,index) => { + const val = el.querySelector('.specs-input-section-value') + if(val) { + const id = val.getAttribute('data-resources') + original.push({id, value: val.value }) + } + }) + + document.querySelectorAll('.specs-input-section-others').forEach((el, index) => { + const name = el.querySelector('.specs-input-section-name') + const val = el.querySelector('.specs-input-section-value') + if (val && name) { + others.push({name: name.value, value: val.value}) + } + }) + + let __payload = { + id: this.opt.id, + amount, + discount, + remarks, + original, + others, + token : window.localStorage.getItem('token'), + action: 'create', + } + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#inv-info-container'}).then(t => t.template.show()) + }) + + new __serv().create(__payload).then(res => { + e.target.disabled = false + return res ? this.__loadProposalAttachmentsNotice() : this.__showError() + }).catch(err => this.__showError() | (e.target.disabled = false) | console.log(err)) + } + + load (e) { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + return import('../../../components/save-modal').then(loader => { + const __target = document.querySelector('#general-modal > .content > .body') + __target.innerHTML = loader.template + __target.querySelector('#modal-dialog-send-button').addEventListener('click', this.create.bind(__proto)) + __target.querySelector('#modal-dialog-close-button').addEventListener('click', () => document.querySelector('#general-modal').close()) + }) + } + + __bind() { + const __proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(`${this.opt.selector}:not(.event-binded)`) + el.forEach((els, index) => { + els.classList.add('event-binded') + els.addEventListener('click',this.load.bind(__proto)) + }) + } + + +} \ No newline at end of file diff --git a/src/pages/proposal-form/index.js b/src/pages/proposal-form/index.js new file mode 100644 index 0000000..f776545 --- /dev/null +++ b/src/pages/proposal-form/index.js @@ -0,0 +1,362 @@ + +export default class { + constructor(opt = {}) { + this.__info = {} + this.opt = opt + return this.loadDialog() + + } + + __bindListeners () { + //this.template.querySelector(`#file-attachment-main-dialog-cancel-btn-${this.opt.id}`).addEventListener('click', () => { + // document.querySelector(`#file-attachment-main-dialog-${this.opt.id}`).close() + //}) + this.template.querySelector('.specs-section-proposal').innerHTML = '' + this.template.querySelector('#specs-other-section').innerHTML = '' + } + + + __autoComputeTotalAmount (quantity) { + const targ = document.querySelector('#proposal-form-amount-per-item:not(.event-binded)') + const totalTarg = document.querySelector('#proposal-form-amount') + + if (targ) { + targ.classList.add('event-binded') + targ.addEventListener('keyup', () => { + const total = (targ.value*quantity) + totalTarg.value = total + }) + } + } + + __cancelSpecsInput (e) { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const val = e.target.value + const id = e.target.id + + let targ = document.getElementById(`orig-req-val-${id}`) + targ.innerHTML = val + + // change link + let btn = document.createElement('a') + btn.href = '#' + btn.setAttribute('onclick', 'event.preventDefault()') + btn.setAttribute('data-resources', id) + btn.setAttribute('data-resources-val', val) + btn.textContent = 'change' + btn.addEventListener('click', this.__changeEventSpecsInput.bind(__proto)) + + e.target.replaceWith(btn) + } + + __changeEventSpecsInput (e) { console.log(e) + const __proto = Object.assign({__proto__: this.__proto__}, this) + const id = e.target.getAttribute('data-resources') + const origValue = e.target.getAttribute('data-resources-val') + const origEl = e.target + let targ = document.getElementById(`orig-req-val-${id}`) + targ.innerHTML = `` + + let link = document.createElement('a') + link.href = '#' + link.setAttribute('onclick','event.preventDefault();') + link.textContent = 'cancel' + link.id = id + link.value = origValue + link.setAttribute('data-resources', id) + link.setAttribute('data-resources-val', origValue) + link.addEventListener('click', this.__cancelSpecsInput.bind(__proto)) + + e.target.replaceWith(link) + } + + __addOtherSpecsField () { + let targ = document.getElementById('specs-other-section') + + let sec = document.createElement('section') + + sec.innerHTML = ` + +
+ +
+
+ + + remove + +
+ +
+ ` + targ.append(sec) + } + + loadPopup () { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + }) + + popupes.then(loader => { + const a = new loader.default() + }) + + } + + async loadDialog () { + const __proto = Object.assign({__proto__: this.__proto__}, this) + const dialog = (await import('../../components/dialog-pane')).default + + this.__info = await this.__getInfo(this.opt.id) + return new dialog(this.opt).then(res => { + // open dialog pane + res.open() + this.render() + + // attach to DOM + const sec = document.querySelector(`#${res.id} > .body`) + sec.innerHTML = '' + sec.append(this.template) + + setTimeout(() => { + const section = document.querySelector('.specs-section-proposal') + this.__autoComputeTotalAmount(this.__info.quantity) + // specs + this.__info.specs.forEach((val, index) => { + let html = document.createElement('span') + html.classList.add('row', 'specs-input-section', 'specs-input-section-orig') + html.setAttribute('data-resources', this.__info.id) + html.setAttribute('style', 'margin-top: 15px;') + html.innerHTML = ` +
+ ${val.name} +
+
+ ${val.value} + +
+ + ` + + // change link + let btn = document.createElement('a') + btn.href = '#' + btn.setAttribute('onclick', 'event.preventDefault()') + btn.setAttribute('data-resources', val.id) + btn.setAttribute('data-resources-val', val.value) + btn.textContent = 'change' + btn.addEventListener('click', this.__changeEventSpecsInput.bind(__proto)) + + html.querySelector('.orig-req-menu').append(btn) + + section.append(html) + }) + + // bind other specs + const otherSpecsBtn = document.querySelector('.add-other-specs-btn:not(.event-binded)') + if (otherSpecsBtn) { + otherSpecsBtn.classList.add('event-binded') + otherSpecsBtn.addEventListener('click', this.__addOtherSpecsField.bind(__proto)) + } + + this.loadPopup() + + setTimeout(() => { + // show proposals + //ReqUtil.bindSendProposal() + //ReqUtil.bindSaveProposal() + import('./actions/create').then(actions => { + return new actions.default({ + selector: '.save-bidding-modal-btn', + id: this.__info.id, + }) + }) + + },10) + + // load actions + /*import('./actions').then(actions => { + const attachments = new actions.default().upload({ + id: this.opt.id, + selector: '#file-upload-attachment-bidding', + }) + + // get recent files + const recent = new actions.default().recent({ + id: this.opt.id, + page: 1, + token: window.localStorage.getItem('token'), + }) + + })*/ + + },700) + + }) + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + import('../../services/bidding-req-service').then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.__params.id).then(data => { + this.__info = data + }) + } + } + + render() { + this.template = document.createElement('section') + this.template.classList.add('row', 's') + this.template.style.overflowY = 'auto' + + // custom classes + if(this.opt.class) this.template.classList.add(...this.opt.class.split(' ')) + + // template + this.template.innerHTML = ` + + + + +
+
+
+ +
+
+
+

+

${this.__info.name}

+ + +

+ Unit Price : + PHP + + + + +

+ +

+ Total Amount (Unit Price X Quantity) :
+ + + + This will be automatically filled out + + +

+ +

+ Discount (for all items) : + + + + +

+ +

Quantity : ${this.__info.quantity} ${this.__info.unit}

+ +

+
+ +
+

+

+ Specifications
+ Please fill up all the fields below + +

+
+ +
+ + +
+


+

+ + Others + (optional) + + + add_circle + +
+ + These are additional specifications not included in the original bidding request. + + +

+ +
+ +
+ + +
+


+

+ + Remarks + (optional) + +
+ + Add additional content in your proposal. Please make it short and simple + + + + + +

+ +
+ +
+ +
+ + +
+
` + this.__bindListeners() + // start rendering + return this.template + } +} + + + \ No newline at end of file diff --git a/src/pages/requirement-proposal-section/actions/compare.js b/src/pages/requirement-proposal-section/actions/compare.js new file mode 100644 index 0000000..83c32bf --- /dev/null +++ b/src/pages/requirement-proposal-section/actions/compare.js @@ -0,0 +1,55 @@ +import ApiConfig from '../../../config/api' + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + this.__apiConfig = ApiConfig + return this.__bind() + } + + compare () { + // compare + let compareBtn = document.querySelector('#compare-btn:not(.event-binded)') + + if (compareBtn) { + compareBtn.classList.add('event-binded') + compareBtn.addEventListener('click' , () => { + // get all selected checkbox + let ids = [] + const isSignatoriesIncluded = document.querySelector('#compare-sign-checkbox').checked ? '&signatories=true' : '' + document.querySelectorAll(`.compare-checkbox-list:checked`).forEach((el, index) => { + const atr = el.getAttribute('data-resources') + if (!ids[atr]) ids.push(atr) + }) + window.open(`${this.__apiConfig.url}/bidding/reports/proposal_comparison.php?id=${this.opt.id}&token=6170b5207b92e5a7445ee3f7de7247c4c1f1b8ef&prop=${ids.join(',')}${isSignatoriesIncluded}`) + }) + } + } + + + load (e) { + document.querySelectorAll('.compare-checkbox-list').forEach((el, index) => { + // exclude proposals that need changes + if (!el.classList.contains('2')) { + e.target.checked ? el.checked = true : el.checked = false + } + }) + } + + + /** + *Bind + * + */ + __bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.load.bind(proto)) + }) + this.compare() + } + +} \ No newline at end of file diff --git a/src/pages/requirement-proposal-section/actions/create.js b/src/pages/requirement-proposal-section/actions/create.js new file mode 100644 index 0000000..edd5991 --- /dev/null +++ b/src/pages/requirement-proposal-section/actions/create.js @@ -0,0 +1,48 @@ + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bind() + } + + hideSpinner () { + const targ = document.querySelector('#inv-info-container > .spinner') + if (targ) targ.hide() + } + + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + } + + + loadDialogPane () { + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#inv-info-container'}).then(t => t.template.show()) + }) + + return import('../../proposal-form').then(loader => { + return new loader.default({ + target: 'body', + id: this.opt.id + }) + }).then(() => this.hideSpinner()) + + } + + /** + *Bind + * + */ + __bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDialogPane.bind(proto)) + }) + } + +} \ No newline at end of file diff --git a/src/pages/requirement-proposal-section/actions/view.js b/src/pages/requirement-proposal-section/actions/view.js new file mode 100644 index 0000000..07527b4 --- /dev/null +++ b/src/pages/requirement-proposal-section/actions/view.js @@ -0,0 +1,56 @@ + +const BiddingServ = import('../../../services/bidding-list-service') +const AccServ = import('../../../services/accounts') + +export default class { + constructor(opt){ + this.opt = opt + this.opt.token = window.localStorage.getItem('token') + this.sendingList = [] + return this.__bind() + } + + hideSpinner () { + const targ = document.querySelector('body > .spinner') + if (targ) targ.hide() + } + + + __showError () { + alert('Oops! Unable to resend this request. Please try again later.') + this.hideSpinner() + } + + + loadDialogPane (e) { + e.preventDefault() + + // spinner + import('../../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'body'}).then(t => t.template.show()) + }) + + return import('../../../components/proposal-dialog').then(loader => { + return new loader.default({ + target: 'body', + id: e.target.getAttribute('data-resources') + }) + }).then(() => { + this.hideSpinner() + }) + + } + + /** + *Bind + * + */ + __bind () { + const proto = Object.assign({ __proto__: this.__proto__ }, this) + const el = document.querySelectorAll(this.opt.selector) + el.forEach((els, index) => { + els.addEventListener('click',this.loadDialogPane.bind(proto)) + }) + } + +} \ No newline at end of file diff --git a/src/pages/requirement-proposal-section/index.js b/src/pages/requirement-proposal-section/index.js new file mode 100644 index 0000000..444f188 --- /dev/null +++ b/src/pages/requirement-proposal-section/index.js @@ -0,0 +1,230 @@ +const info = import('../../services/bidding-req-proposal-service') +import { isCBAAsst , isAdmin, isGSU, isSupplier, isStandard } from '../../utils/privilege-loader' + +export default class { + constructor (opt) { + this.opt = opt + this.__info = {} + return this.render() + } + + /** + * Get bidding information via built-in bidding services + * + * @param {int} id + */ + __getInfo (id) { + // fetch details + return new Promise((resolve, reject) => { + import('../../services/bidding-req-service').then(loader => { + const a = new loader.default().view({ id, token: localStorage.getItem('token') }).then(res => { + resolve(res[0]) + }).catch(err => reject(err)) + }) + }) + } + + /** + * Assign information to scoped data + */ + async getInfo() { + if(!this.__info.id) { + await this.__getInfo(this.opt.id).then(data => { + this.__info = data + }) + } + } + + + __loadPopup () { + + const popupes = import('../../components/popup-es') + const popupesStyle = import('../../components/popup-es/style') + + // enable popup + popupesStyle.then(css => { + const style = document.createElement('style') + style.id = 'popup-es-style' + style.innerHTML = css.default.toString() + if(!document.querySelector('#popup-es-style')) document.head.append(style) + + }) + + popupes.then(loader => new loader.default()) + + } + + __getList () { + info.then(loader => { + new loader.default().lists({ + id: this.opt.id, + token: window.localStorage.getItem('token'), + filter: 'all', + page: 1, + }).then(res => { + this.showProposals('.proposal-list-section > ul' , res) + }) + }) + } + + __bindListeners () { + import('./actions/create').then(loader => { + return new loader.default({ + id: this.opt.id, + token: window.localStorage.getItem('token'), + selector: '.proposal-reg-dialog-btn', + }) + }) + + import('./actions/compare').then(loader => { + return new loader.default({ + id: this.opt.id, + token: window.localStorage.getItem('token'), + selector: '#compare-checkbox', + }) + }) + + } + + showProposals (target, data, isEmpty = false) { + let __targ = this.template.querySelectorAll(target) + + // clear proposal list section + if(isEmpty) __targ.forEach((el, index) => { el.innerHTML ='' }) + + + data.forEach((val, index) => { + let html = document.createElement('li') + let status = '' + html.classList.add('nav-item', 'col-12') + html.setAttribute('data-resources', val.id) + html.style = 'border-bottom:1px solid #ccc;padding-top:15px;padding-bottom: 5px;' + html.id = val.id + + + if (val.status == 0) { + status = `
drafts DRAFT` + } + + if (val.status == 1 || val.status == 5) { + status = `
check Sent` + } + + + if (val.status == 2) { + status = `
warning Requesting changes` + } + + if (val.status == 3) { + status = `
star AWARDED` + // show won status + //// showWon() + // add medal icon + //const img = document.createElement('img') + //img.src = 'assets/img/trophy.png' + //img.style.width = '30px' + //document.querySelector('.req-name').append(img) + } + + if (val.status == 5) { + status = `
star Winner` + } + + html.innerHTML = ` + +
+
${val.username.substr(0,2).toUpperCase()}
+
+
+
+ +

+ ${val.username}
+ ${val.date_created} + ${status} +

+
+
+
+
+
+ ${isCBAAsst() ? `` : '' } +   #${val.id} +
+ ` + + // insert to DOM + __targ.forEach((el, index) => { + el.append(html) + }) + }) + + setTimeout(() => { + import('./actions/view').then(loader => { + return new loader.default({ + id: this.opt.id, + token: window.localStorage.getItem('token'), + selector: '.proposal-dialog-btn', + }) + }) + }, 1000) + + } + + /** + * Return template as HTMLObject to be rendered in DOM + */ + async render () { + + this.__info = await this.__getInfo(this.opt.id) + const template = document.createElement('section') + + template.classList.add('col-lg-3') + template.id = 'requirement-proposal-container' + template.innerHTML = ` +
+ +
Proposals

+ + ${isSupplier() ? + `
+
+

+ insert_drive_file
+ Submit a good propasal and stand above all other companies!

+ Submit add_circle

+
+
+
` : '' } + +
+ ${isCBAAsst() || isGSU() ? + ` +
+ +
+
+ +
+
+ + +
+ +
+
+ Include signatories? +
+

` : '' + } + + +
+
` + this.template = template + this.__getList() + this.__bindListeners() + return template + } + +} diff --git a/src/pages/suppliers/accounts/accounts.html b/src/pages/suppliers/accounts/accounts.html deleted file mode 100644 index 0ddfb1b..0000000 --- a/src/pages/suppliers/accounts/accounts.html +++ /dev/null @@ -1,59 +0,0 @@ -
-
-
- supervisor_account Accounts -
-
- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

-

- -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
UsernamePassword
jkga************** - - - -
jkga************** - - - -
jkga************** - - - -
jkga************** - - -
- -
-
\ No newline at end of file diff --git a/src/pages/suppliers/accounts/forms/register.html b/src/pages/suppliers/accounts/forms/register.html deleted file mode 100644 index 43781f1..0000000 --- a/src/pages/suppliers/accounts/forms/register.html +++ /dev/null @@ -1,48 +0,0 @@ - - - \ No newline at end of file diff --git a/src/pages/suppliers/forms/registration/registration.html b/src/pages/suppliers/forms/registration/registration.html deleted file mode 100644 index e3a5d9d..0000000 --- a/src/pages/suppliers/forms/registration/registration.html +++ /dev/null @@ -1,60 +0,0 @@ -
-
-
-
- - arrow_back Back to list -
-
-

-
-

Company Profile

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut a.

- -
- -
-
-
- -

Name (Required)

-

Alias

-

Website

-

Tagline

-

Date Established

-

Location (Required)

-

About

-

-

Industry

-

- - - - - -
- - -
- - - -
-
- - -
-
- \ No newline at end of file diff --git a/src/pages/suppliers/forms/registration/registration_update.html b/src/pages/suppliers/forms/registration/registration_update.html deleted file mode 100644 index 8536a9f..0000000 --- a/src/pages/suppliers/forms/registration/registration_update.html +++ /dev/null @@ -1,60 +0,0 @@ -
-
-
-
- - arrow_back Back -
-
-
-
-

Update Company Profile

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut a.

- -
- -
-
-
- -

Name (Required)

-

Alias

-

Website

-

Tagline

-

Date Established

-

Location (Required)

-

About

-

-

Industry

-

- -

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,

- - - - -
- - -
- - - -
-
- - -
-
- \ No newline at end of file diff --git a/src/pages/suppliers/initial.html b/src/pages/suppliers/initial.html deleted file mode 100644 index 2829447..0000000 --- a/src/pages/suppliers/initial.html +++ /dev/null @@ -1,8 +0,0 @@ -
- -
-


Bidding Management System

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-
- -
\ No newline at end of file diff --git a/src/pages/suppliers/list/list.html b/src/pages/suppliers/list/list.html deleted file mode 100644 index 535e884..0000000 --- a/src/pages/suppliers/list/list.html +++ /dev/null @@ -1,26 +0,0 @@ -
- -
-
-
- search - -
-
-
-
- All  - Blocked  - Newadd -
-
-
-
- -
-
-
- - -
- \ No newline at end of file diff --git a/src/pages/suppliers/logs/logs.html b/src/pages/suppliers/logs/logs.html deleted file mode 100644 index 38b7222..0000000 --- a/src/pages/suppliers/logs/logs.html +++ /dev/null @@ -1,20 +0,0 @@ - -
-
-
format_list_numbered Logs
-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

-
-
- -
-

[8-22-17 3:37:04] John Added new computer HPE ProLiant DL380 Gen9 Server OID7271241 | HPE

-

[8-22-17] John Added new computer HPE ProLiant DL380 | HPE

-

[8-22-17] John Added new computer HPE ProLiant DL380 Gen9 Se 71241 | HPE

-

[8-22-17] John Added new computer HPE ProLiant DL380 Genr OID7271241 | HPE

-

[8-22-17] John Added new computer HPE ProLiant DL38

-

[8-22-17] John Added new computer HPE ProLiant DL380 Gen9 Server OID7271241 | HPE

- -
-
- diff --git a/src/pages/suppliers/modal/remove.html b/src/pages/suppliers/modal/remove.html deleted file mode 100644 index 650108a..0000000 --- a/src/pages/suppliers/modal/remove.html +++ /dev/null @@ -1,14 +0,0 @@ - -
-
-

Remove

-

This will be removed permanently in the system and could no be longer recover. Are you sure you want to continue?

- - - -
-
\ No newline at end of file diff --git a/src/pages/suppliers/products/forms/category.html b/src/pages/suppliers/products/forms/category.html deleted file mode 100644 index 57db320..0000000 --- a/src/pages/suppliers/products/forms/category.html +++ /dev/null @@ -1,44 +0,0 @@ - - - \ No newline at end of file diff --git a/src/pages/suppliers/products/forms/registration.html b/src/pages/suppliers/products/forms/registration.html deleted file mode 100644 index d167ed6..0000000 --- a/src/pages/suppliers/products/forms/registration.html +++ /dev/null @@ -1,40 +0,0 @@ - -
-

Product bookmark

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis

-
- -
-
-

info Details :

- -
- -
- -
- -
-

monetization_on Price :

-
-
-
- -
- - -
-
- -
- \ No newline at end of file diff --git a/src/pages/suppliers/products/forms/specs.html b/src/pages/suppliers/products/forms/specs.html deleted file mode 100644 index 0f3e48e..0000000 --- a/src/pages/suppliers/products/forms/specs.html +++ /dev/null @@ -1,40 +0,0 @@ - -
- -

Specifications

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis

-
- -
-
-

info Product Name :

-
-
- -
-

monetization_on Price :

-
-
-
- -
-

monetization_on Specifications

- -
- -
- -
- -
- \ No newline at end of file diff --git a/src/pages/suppliers/products/products.html b/src/pages/suppliers/products/products.html deleted file mode 100644 index ae6c6c3..0000000 --- a/src/pages/suppliers/products/products.html +++ /dev/null @@ -1,138 +0,0 @@ -
-
-
shopping_basket Products
-
-
-

Computers

-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -
- -
-

Software

-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -
- - -
-
\ No newline at end of file diff --git a/src/pages/suppliers/profile/about.html b/src/pages/suppliers/profile/about.html deleted file mode 100644 index 939d8b4..0000000 --- a/src/pages/suppliers/profile/about.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
-
bookmark About
-
- -

The Southeast Asian Regional Center for Graduate Study and Research in Agriculture (SEARCA) is a non-profit organization established by the Southeast Asian Ministers of Education Organization (SEAMEO) in 1966. - - Founded in 1965, SEAMEO is a chartered international organization whose purpose is to promote cooperation in education, science and culture in the Southeast Asian region. Its highest policymaking body is the SEAMEO Council, which comprises the Ministers of Education of the 11 SEAMEO Member Countries, namely: Brunei Darussalam, Cambodia, Indonesia, Lao PDR, Malaysia, Myanmar, the Philippines, Singapore, Thailand, Timor-Leste, and Vietnam. - - SEAMEO also has Associate Member Countries, namely: Australia, Canada, France, Germany, Netherlands, New Zealand, Spain, and the United Kingdom. - - The Center derives its juridical personality from the SEAMEO Charter and possesses full capacity to contract; acquire, and dispose of, immovable and movable property; and institute legal proceedings. Moreover, SEARCA enjoys in the territory of each of its member states such privileges and immunities as are normally accorded United Nations institutions. Representatives of member states and officials of the Center shall similarly enjoy such privileges and immunities in the Philippines as are necessary for the exercise of their functions in connection with SEARCA and SEAMEO.

- -
Industry
-
-

- Agriculture - Education -

-
-
\ No newline at end of file diff --git a/src/pages/suppliers/profile/profile.html b/src/pages/suppliers/profile/profile.html deleted file mode 100644 index 0a4574f..0000000 --- a/src/pages/suppliers/profile/profile.html +++ /dev/null @@ -1,62 +0,0 @@ -
-
- - - - - -
-

-

link

-
-
-
- -
- - - - - -
- - - - - - - -
-
- - - diff --git a/src/pages/suppliers/settings/modal/block.html b/src/pages/suppliers/settings/modal/block.html deleted file mode 100644 index 17e0d71..0000000 --- a/src/pages/suppliers/settings/modal/block.html +++ /dev/null @@ -1,12 +0,0 @@ - -
-

Block/Unblock

-

You are about to block this supplier that will prevent him/her from accessing his/her account. Are you sure you want to continue?

- - - -
\ No newline at end of file diff --git a/src/pages/suppliers/settings/settings.html b/src/pages/suppliers/settings/settings.html deleted file mode 100644 index d643b7c..0000000 --- a/src/pages/suppliers/settings/settings.html +++ /dev/null @@ -1,39 +0,0 @@ - -
-
-
settings Settings
-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -
- -
- -

Profile

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -

-
- -
- - -

Block/Unblock

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation


-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -

- - - -

Remove Account

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation


-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation

- -
- -
- -
- - diff --git a/src/pages/welcome.html b/src/pages/welcome.html deleted file mode 100644 index 33ca4e1..0000000 --- a/src/pages/welcome.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
-
-

- bms welcome -
- -
-

-

Bidding Management System

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

-
-
-
\ No newline at end of file diff --git a/src/res/.pgbomit b/src/res/.pgbomit deleted file mode 100644 index e69de29..0000000 diff --git a/src/res/icon/android/drawable-hdpi-icon.png b/src/res/icon/android/drawable-hdpi-icon.png deleted file mode 100644 index 0b3e799..0000000 Binary files a/src/res/icon/android/drawable-hdpi-icon.png and /dev/null differ diff --git a/src/res/icon/android/drawable-ldpi-icon.png b/src/res/icon/android/drawable-ldpi-icon.png deleted file mode 100644 index f4b1dfb..0000000 Binary files a/src/res/icon/android/drawable-ldpi-icon.png and /dev/null differ diff --git a/src/res/icon/android/drawable-mdpi-icon.png b/src/res/icon/android/drawable-mdpi-icon.png deleted file mode 100644 index edc83f2..0000000 Binary files a/src/res/icon/android/drawable-mdpi-icon.png and /dev/null differ diff --git a/src/res/icon/android/drawable-xhdpi-icon.png b/src/res/icon/android/drawable-xhdpi-icon.png deleted file mode 100644 index e4ae9ce..0000000 Binary files a/src/res/icon/android/drawable-xhdpi-icon.png and /dev/null differ diff --git a/src/res/icon/android/drawable-xxhdpi-icon.png b/src/res/icon/android/drawable-xxhdpi-icon.png deleted file mode 100644 index e801412..0000000 Binary files a/src/res/icon/android/drawable-xxhdpi-icon.png and /dev/null differ diff --git a/src/res/icon/android/drawable-xxxhdpi-icon.png b/src/res/icon/android/drawable-xxxhdpi-icon.png deleted file mode 100644 index 0d848e6..0000000 Binary files a/src/res/icon/android/drawable-xxxhdpi-icon.png and /dev/null differ diff --git a/src/res/icon/ios/icon-40.png b/src/res/icon/ios/icon-40.png deleted file mode 100644 index ebac8cc..0000000 Binary files a/src/res/icon/ios/icon-40.png and /dev/null differ diff --git a/src/res/icon/ios/icon-40@2x.png b/src/res/icon/ios/icon-40@2x.png deleted file mode 100644 index 32caacc..0000000 Binary files a/src/res/icon/ios/icon-40@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-50.png b/src/res/icon/ios/icon-50.png deleted file mode 100644 index fe41c07..0000000 Binary files a/src/res/icon/ios/icon-50.png and /dev/null differ diff --git a/src/res/icon/ios/icon-50@2x.png b/src/res/icon/ios/icon-50@2x.png deleted file mode 100644 index 0d5f1da..0000000 Binary files a/src/res/icon/ios/icon-50@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-60.png b/src/res/icon/ios/icon-60.png deleted file mode 100644 index e7fa274..0000000 Binary files a/src/res/icon/ios/icon-60.png and /dev/null differ diff --git a/src/res/icon/ios/icon-60@2x.png b/src/res/icon/ios/icon-60@2x.png deleted file mode 100644 index ae0f0fc..0000000 Binary files a/src/res/icon/ios/icon-60@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-60@3x.png b/src/res/icon/ios/icon-60@3x.png deleted file mode 100644 index eff00b5..0000000 Binary files a/src/res/icon/ios/icon-60@3x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-72.png b/src/res/icon/ios/icon-72.png deleted file mode 100644 index 0b3e799..0000000 Binary files a/src/res/icon/ios/icon-72.png and /dev/null differ diff --git a/src/res/icon/ios/icon-72@2x.png b/src/res/icon/ios/icon-72@2x.png deleted file mode 100644 index e801412..0000000 Binary files a/src/res/icon/ios/icon-72@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-76.png b/src/res/icon/ios/icon-76.png deleted file mode 100644 index 245f37c..0000000 Binary files a/src/res/icon/ios/icon-76.png and /dev/null differ diff --git a/src/res/icon/ios/icon-76@2x.png b/src/res/icon/ios/icon-76@2x.png deleted file mode 100644 index 93ab852..0000000 Binary files a/src/res/icon/ios/icon-76@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-small.png b/src/res/icon/ios/icon-small.png deleted file mode 100644 index 586967a..0000000 Binary files a/src/res/icon/ios/icon-small.png and /dev/null differ diff --git a/src/res/icon/ios/icon-small@2x.png b/src/res/icon/ios/icon-small@2x.png deleted file mode 100644 index b7c038a..0000000 Binary files a/src/res/icon/ios/icon-small@2x.png and /dev/null differ diff --git a/src/res/icon/ios/icon-small@3x.png b/src/res/icon/ios/icon-small@3x.png deleted file mode 100644 index eb28e52..0000000 Binary files a/src/res/icon/ios/icon-small@3x.png and /dev/null differ diff --git a/src/res/icon/ios/icon.png b/src/res/icon/ios/icon.png deleted file mode 100644 index e591bc2..0000000 Binary files a/src/res/icon/ios/icon.png and /dev/null differ diff --git a/src/res/icon/ios/icon@2x.png b/src/res/icon/ios/icon@2x.png deleted file mode 100644 index aa938f7..0000000 Binary files a/src/res/icon/ios/icon@2x.png and /dev/null differ diff --git a/src/res/icon/windows/Square150x150Logo.scale-100.png b/src/res/icon/windows/Square150x150Logo.scale-100.png deleted file mode 100644 index d7b7be0..0000000 Binary files a/src/res/icon/windows/Square150x150Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square150x150Logo.scale-240.png b/src/res/icon/windows/Square150x150Logo.scale-240.png deleted file mode 100644 index 62f513b..0000000 Binary files a/src/res/icon/windows/Square150x150Logo.scale-240.png and /dev/null differ diff --git a/src/res/icon/windows/Square30x30Logo.scale-100.png b/src/res/icon/windows/Square30x30Logo.scale-100.png deleted file mode 100644 index 0c1811e..0000000 Binary files a/src/res/icon/windows/Square30x30Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square310x310Logo.scale-100.png b/src/res/icon/windows/Square310x310Logo.scale-100.png deleted file mode 100644 index b2610d4..0000000 Binary files a/src/res/icon/windows/Square310x310Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square44x44Logo.scale-100.png b/src/res/icon/windows/Square44x44Logo.scale-100.png deleted file mode 100644 index 213ad29..0000000 Binary files a/src/res/icon/windows/Square44x44Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square44x44Logo.scale-240.png b/src/res/icon/windows/Square44x44Logo.scale-240.png deleted file mode 100644 index d584e4d..0000000 Binary files a/src/res/icon/windows/Square44x44Logo.scale-240.png and /dev/null differ diff --git a/src/res/icon/windows/Square70x70Logo.scale-100.png b/src/res/icon/windows/Square70x70Logo.scale-100.png deleted file mode 100644 index 04a50d6..0000000 Binary files a/src/res/icon/windows/Square70x70Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square71x71Logo.scale-100.png b/src/res/icon/windows/Square71x71Logo.scale-100.png deleted file mode 100644 index bea6126..0000000 Binary files a/src/res/icon/windows/Square71x71Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Square71x71Logo.scale-240.png b/src/res/icon/windows/Square71x71Logo.scale-240.png deleted file mode 100644 index de331ba..0000000 Binary files a/src/res/icon/windows/Square71x71Logo.scale-240.png and /dev/null differ diff --git a/src/res/icon/windows/StoreLogo.scale-100.png b/src/res/icon/windows/StoreLogo.scale-100.png deleted file mode 100644 index 3737a87..0000000 Binary files a/src/res/icon/windows/StoreLogo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/StoreLogo.scale-240.png b/src/res/icon/windows/StoreLogo.scale-240.png deleted file mode 100644 index f14fd14..0000000 Binary files a/src/res/icon/windows/StoreLogo.scale-240.png and /dev/null differ diff --git a/src/res/icon/windows/Wide310x150Logo.scale-100.png b/src/res/icon/windows/Wide310x150Logo.scale-100.png deleted file mode 100644 index 3ed9b68..0000000 Binary files a/src/res/icon/windows/Wide310x150Logo.scale-100.png and /dev/null differ diff --git a/src/res/icon/windows/Wide310x150Logo.scale-240.png b/src/res/icon/windows/Wide310x150Logo.scale-240.png deleted file mode 100644 index 577df1b..0000000 Binary files a/src/res/icon/windows/Wide310x150Logo.scale-240.png and /dev/null differ diff --git a/src/res/icon/wp8/ApplicationIcon.png b/src/res/icon/wp8/ApplicationIcon.png deleted file mode 100644 index 58e2765..0000000 Binary files a/src/res/icon/wp8/ApplicationIcon.png and /dev/null differ diff --git a/src/res/icon/wp8/Background.png b/src/res/icon/wp8/Background.png deleted file mode 100644 index 321c929..0000000 Binary files a/src/res/icon/wp8/Background.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-hdpi-screen.png b/src/res/screen/android/drawable-land-hdpi-screen.png deleted file mode 100644 index 3b5cbe3..0000000 Binary files a/src/res/screen/android/drawable-land-hdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-ldpi-screen.png b/src/res/screen/android/drawable-land-ldpi-screen.png deleted file mode 100644 index 59e16da..0000000 Binary files a/src/res/screen/android/drawable-land-ldpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-mdpi-screen.png b/src/res/screen/android/drawable-land-mdpi-screen.png deleted file mode 100644 index 9b66b68..0000000 Binary files a/src/res/screen/android/drawable-land-mdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-xhdpi-screen.png b/src/res/screen/android/drawable-land-xhdpi-screen.png deleted file mode 100644 index 7daceb7..0000000 Binary files a/src/res/screen/android/drawable-land-xhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-xxhdpi-screen.png b/src/res/screen/android/drawable-land-xxhdpi-screen.png deleted file mode 100644 index d477acd..0000000 Binary files a/src/res/screen/android/drawable-land-xxhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-land-xxxhdpi-screen.png b/src/res/screen/android/drawable-land-xxxhdpi-screen.png deleted file mode 100644 index 08f63bd..0000000 Binary files a/src/res/screen/android/drawable-land-xxxhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-hdpi-screen.png b/src/res/screen/android/drawable-port-hdpi-screen.png deleted file mode 100644 index c447d3c..0000000 Binary files a/src/res/screen/android/drawable-port-hdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-ldpi-screen.png b/src/res/screen/android/drawable-port-ldpi-screen.png deleted file mode 100644 index 3c37ce7..0000000 Binary files a/src/res/screen/android/drawable-port-ldpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-mdpi-screen.png b/src/res/screen/android/drawable-port-mdpi-screen.png deleted file mode 100644 index bcffc5b..0000000 Binary files a/src/res/screen/android/drawable-port-mdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-xhdpi-screen.png b/src/res/screen/android/drawable-port-xhdpi-screen.png deleted file mode 100644 index f217795..0000000 Binary files a/src/res/screen/android/drawable-port-xhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-xxhdpi-screen.png b/src/res/screen/android/drawable-port-xxhdpi-screen.png deleted file mode 100644 index 0573df6..0000000 Binary files a/src/res/screen/android/drawable-port-xxhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/android/drawable-port-xxxhdpi-screen.png b/src/res/screen/android/drawable-port-xxxhdpi-screen.png deleted file mode 100644 index 9b3eced..0000000 Binary files a/src/res/screen/android/drawable-port-xxxhdpi-screen.png and /dev/null differ diff --git a/src/res/screen/ios/Default-568h@2x~iphone.png b/src/res/screen/ios/Default-568h@2x~iphone.png deleted file mode 100644 index abc368e..0000000 Binary files a/src/res/screen/ios/Default-568h@2x~iphone.png and /dev/null differ diff --git a/src/res/screen/ios/Default-667h.png b/src/res/screen/ios/Default-667h.png deleted file mode 100644 index 73f0e06..0000000 Binary files a/src/res/screen/ios/Default-667h.png and /dev/null differ diff --git a/src/res/screen/ios/Default-736h.png b/src/res/screen/ios/Default-736h.png deleted file mode 100644 index 9e4b0ec..0000000 Binary files a/src/res/screen/ios/Default-736h.png and /dev/null differ diff --git a/src/res/screen/ios/Default-Landscape-736h.png b/src/res/screen/ios/Default-Landscape-736h.png deleted file mode 100644 index 5d7c7aa..0000000 Binary files a/src/res/screen/ios/Default-Landscape-736h.png and /dev/null differ diff --git a/src/res/screen/ios/Default-Landscape@2x~ipad.png b/src/res/screen/ios/Default-Landscape@2x~ipad.png deleted file mode 100644 index 29ed379..0000000 Binary files a/src/res/screen/ios/Default-Landscape@2x~ipad.png and /dev/null differ diff --git a/src/res/screen/ios/Default-Landscape~ipad.png b/src/res/screen/ios/Default-Landscape~ipad.png deleted file mode 100644 index 652b565..0000000 Binary files a/src/res/screen/ios/Default-Landscape~ipad.png and /dev/null differ diff --git a/src/res/screen/ios/Default-Portrait@2x~ipad.png b/src/res/screen/ios/Default-Portrait@2x~ipad.png deleted file mode 100644 index 3a5a259..0000000 Binary files a/src/res/screen/ios/Default-Portrait@2x~ipad.png and /dev/null differ diff --git a/src/res/screen/ios/Default-Portrait~ipad.png b/src/res/screen/ios/Default-Portrait~ipad.png deleted file mode 100644 index 9a28e5e..0000000 Binary files a/src/res/screen/ios/Default-Portrait~ipad.png and /dev/null differ diff --git a/src/res/screen/ios/Default@2x~iphone.png b/src/res/screen/ios/Default@2x~iphone.png deleted file mode 100644 index a5972ff..0000000 Binary files a/src/res/screen/ios/Default@2x~iphone.png and /dev/null differ diff --git a/src/res/screen/ios/Default~iphone.png b/src/res/screen/ios/Default~iphone.png deleted file mode 100644 index 1392aba..0000000 Binary files a/src/res/screen/ios/Default~iphone.png and /dev/null differ diff --git a/src/res/screen/windows/SplashScreen.scale-100.png b/src/res/screen/windows/SplashScreen.scale-100.png deleted file mode 100644 index b51c2f1..0000000 Binary files a/src/res/screen/windows/SplashScreen.scale-100.png and /dev/null differ diff --git a/src/res/screen/windows/SplashScreenPhone.scale-240.png b/src/res/screen/windows/SplashScreenPhone.scale-240.png deleted file mode 100644 index 2c710b8..0000000 Binary files a/src/res/screen/windows/SplashScreenPhone.scale-240.png and /dev/null differ diff --git a/src/res/screen/wp8/screen-portrait.jpg b/src/res/screen/wp8/screen-portrait.jpg deleted file mode 100644 index 36ceab4..0000000 Binary files a/src/res/screen/wp8/screen-portrait.jpg and /dev/null differ diff --git a/src/routers/auth.js b/src/routers/auth.js new file mode 100644 index 0000000..43e6471 --- /dev/null +++ b/src/routers/auth.js @@ -0,0 +1,35 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../utils/display-styler') +const ScriptLoader = import('../utils/script-loader') + +const loadCompanyAuth = () => { + import('../pages/auth-company-section').then(auth => { + const authSec = document.querySelector('auth-section') + authSec.innerHTML = auth.default + // load MSAL + ScriptLoader.then(loader => loader.default(authSec)) + }) +} + +const loadO365Sec = () => { + // load template + import('../pages/auth-section').then(auth => { + const authSec = document.querySelector('auth-section') + authSec.innerHTML = auth.default + // load MSAL + ScriptLoader.then(loader => loader.default(authSec)) + }) +} + +DisplayStyler.then(display => { + // change visibility + display.default(['auth-section'],'block') + const ASec = document.querySelector('auth-section') + + if(ASec.getAttribute('section') != 'company') { + loadO365Sec() + } else { + loadCompanyAuth() + } +}) + diff --git a/src/routers/bidding.js b/src/routers/bidding.js new file mode 100644 index 0000000..b02cfb6 --- /dev/null +++ b/src/routers/bidding.js @@ -0,0 +1,100 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../utils/display-styler') +const ScriptLoader = import('../utils/script-loader') +const Menuselector = import('../utils/menu-selector') +const InitSection = import('../pages/initial-section') + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + const appRoute2 = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/bids/*' : () => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', 'registration-section', '#bids-report-section', '#feedback-section'], 'none') + display.default(['.list-bids-container'], 'block') + }) + console.log('bidding') + } + }).resolve() + + + appRoute2.on({ + '' : () => { }, + '/bids/all' : async () => { + let ActionsInfo = await import('../pages/bidding-info-section/actions') + Menuselector.then(loader => { new loader.default().active('bids-menu-list-all') }) + InitSection.then(res => document.querySelector('initial-section') ? document.querySelector('initial-section').replaceWith(res.default) : 0) + // get bidding + ActionsInfo.loadListSection().then(() => { + ActionsInfo.getBiddingList() + }) + }, + '/bids/drafts' : async () => { + let ActionsInfo = await import('../pages/bidding-info-section/actions') + InitSection.then(res => document.querySelector('initial-section') ? document.querySelector('initial-section').replaceWith(res.default) : 0) + Menuselector.then(loader => { new loader.default().active('bids-menu-list-drafts') }) + // get bidding + ActionsInfo.loadListSection().then(() => { + ActionsInfo.getBiddingList({filter: 'drafts', page: 1}) + }) + }, + '/bids/:id/info/': async (params) => { + let ActionsInfo = await import('../pages/bidding-info-section/actions') + // load list on page refresh or later + if (!document.querySelector('.list-bids-container')) { + Menuselector.then(loader => { new loader.default().active('bids-menu-list-all') }) + ActionsInfo.loadListSection().then(() => { + ActionsInfo.getBiddingList() + }) + } + + // spinner + import('../components/app-spinner').then(loader => { + return new loader.default().show({target: '#bidding-info-details'}).then(t => t.template.show()) + }) + + import('./bidding/info') + }, + '/bids/forms/registration/*': async (params) => { + let ActionsInfo = await import('../pages/bidding-info-section/actions') + // load list on page refresh or later + if (!document.querySelector('.list-bids-container')) { + Menuselector.then(loader => { new loader.default().active('bids-menu-list-all') }) + ActionsInfo.loadListSection().then(() => { + ActionsInfo.getBiddingList() + }) + } + + import('./bidding/registration') + }, + '/bids/requirements/:id': async (params) => { + let ActionsInfo = await import('../pages/bidding-info-section/actions') + // load list on page refresh or later + if (!document.querySelector('.list-bids-container')) { + Menuselector.then(loader => { new loader.default().active('bids-menu-list-all') }) + ActionsInfo.loadListSection().then(() => { + ActionsInfo.getBiddingList() + }) + } + import('./bidding/requirements') + }, + 'bids/reports': (params) => { + + Menuselector.then(loader => { new loader.default().active('bids-menu-list-reports') }) + setTimeout(() => { + DisplayStyler.then(display => { + display.default(['.list-bids-container', '#initial-section', '#bids-info-container'], 'none') + display.default(['#bids-report-section'], 'block') + }) + },100) + + import('./bidding/reports') + + } + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/bidding/info.js b/src/routers/bidding/info.js new file mode 100644 index 0000000..6a5af79 --- /dev/null +++ b/src/routers/bidding/info.js @@ -0,0 +1,68 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../../utils/display-styler') +const ScriptLoader = import('../../utils/script-loader') +const DropdownLoader = import('../../utils/dropdown-loader') +const Menuselector = import('../../utils/menu-selector') +const BiddingServ = import('../../services/bidding-list-service') +const CustomerReviews = import('../../components/customer-reviews') + +// change list to active +const activateList = (id) => { + document.querySelectorAll(`.list`).forEach((el, index) => { + if (el.getAttribute('data-list') == id) { + el.classList.add('active') + } else { + el.classList.remove('active') + } + }) +} + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/bids/:id/info/': async (params) => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', '#initial-section'], 'none') + display.default(['#bids-info-container'], 'block') + }) + + activateList(params.id) + + // loade Information section + return import('../../pages/bidding-info-section').then(res => { + // template + const temp = new res.template({id: params.id}) + const template = temp.render() + template.then(html => { + + // DOM + const infMenu = document.querySelector('info-section') || document.querySelector('#bids-info-container') + if(infMenu) infMenu.replaceWith(html) + + // other content + temp.setStatus() + + }).then(() => { + temp.getAttachments() + temp.getParticulars() + + + }) + + + /* + + CustomerReviews.then(res => { + const targ = document.querySelector('.reviews-section') + targ.append(new res.default()) + })*/ + + }) + } + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/bidding/registration.js b/src/routers/bidding/registration.js new file mode 100644 index 0000000..75011a5 --- /dev/null +++ b/src/routers/bidding/registration.js @@ -0,0 +1,140 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../../utils/display-styler') + +const loadForm = () => { + // fetch registration form + return import('../../pages/bidding-reg-form').then(res => { + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + targ.innerHTML = '' + targ.append(res.template) + }) +} + +const register = () => { + const serv = import('../../pages/bidding-reg-form/actions') + serv.then(loader => { + document.querySelector('.add-bidding-button').addEventListener('click', loader.register) + }) +} + +const showContainer = () => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', '#initial-section', '#bids-info-container', '#requirement-container', '#requirement-proposal-container'], 'none') + display.default(['.bids-info-container', 'registration-section'], 'block') + }) + + // spinner + import('../../components/app-spinner').then(loader => { + return new loader.default().show({target: 'registration-section'}).then(t => t.template.show()) + }) +} + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/bids/forms/registration/step/1': () => { + // load form + showContainer() + loadForm().then(() => { + register() + setTimeout(() => { + const s = document.querySelector('registration-section > .spinner') + if(s) document.querySelector('registration-section > .spinner').remove() + }, 3000) + }) + }, + '/bids/forms/registration/:id/step/1/update': (params) => { + // load form + showContainer() + loadForm().then(() => { + const serv = import('../../pages/bidding-reg-form/actions') + serv.then(loader => { + const btn = document.querySelector('.add-bidding-button') + btn.id = params.id + btn.addEventListener('click', loader.update) + }) + }) + }, + '/bids/forms/registration/:id/step/2': (params) => { + showContainer() + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + + + return import('../../pages/bidding-part-form').then(res => { + new res.default({ + action: 'create', + id: params.id, + }).then(html => { + targ.innerHTML = '' + targ.append(html) + }) + }) + }, + '/bids/forms/registration/:id/step/2/update': (params) => { + showContainer() + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + + return import('../../pages/bidding-part-form').then(res => { + new res.default({ + action: 'update', + id: params.id, + }).then(html => { + targ.innerHTML = '' + targ.append(html) + }) + }) + }, + '/bids/forms/registration/:id/step/3': (params) => { + showContainer() + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + + return import('../../pages/bidding-prod-form').then(res => { + new res.default({ + action: 'create', + id: params.id, + }).then(html => { + targ.innerHTML = '' + targ.append(html) + }) + }) + }, + '/bids/forms/registration/:id/step/3/update': (params) => { + showContainer() + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + + return import('../../pages/bidding-prod-form').then(res => { + new res.default({ + action: 'update', + id: params.id, + }).then(html => { + targ.innerHTML = '' + targ.append(html) + }) + }) + }, + '/bids/forms/registration/:id/step/4/:pid': (params) => { + showContainer() + const targ = document.querySelector('registration-section') + targ.classList.add('col', 'col-lg-10') + + return import('../../pages/bidding-prod-form/confirmation').then(res => { + new res.default({ + id: params.id, + pid: params.pid, + }).then(html => { + targ.innerHTML = '' + targ.append(html) + }) + }) + } + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/bidding/reports.js b/src/routers/bidding/reports.js new file mode 100644 index 0000000..4901ff2 --- /dev/null +++ b/src/routers/bidding/reports.js @@ -0,0 +1,31 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../../utils/display-styler') +const ScriptLoader = import('../../utils/script-loader') +const DropdownLoader = import('../../utils/dropdown-loader') +const Menuselector = import('../../utils/menu-selector') +const BiddingServ = import('../../services/bidding-list-service') + + + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '/bids/reports': async (params) => { + + // loade Information section + return import('../../pages/bidding-report-section').then(res => { + // template + const temp = new res.template() + const template = temp.render() + template.then(html => { + // DOM + const infMenu = document.querySelector('report-section') || document.querySelector('#bids-report-section') + if(infMenu) infMenu.replaceWith(html) + }) + }) + } + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/bidding/requirements.js b/src/routers/bidding/requirements.js new file mode 100644 index 0000000..b35f886 --- /dev/null +++ b/src/routers/bidding/requirements.js @@ -0,0 +1,59 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../../utils/display-styler') +const ScriptLoader = import('../../utils/script-loader') +const DropdownLoader = import('../../utils/dropdown-loader') +const Menuselector = import('../../utils/menu-selector') +const BiddingServ = import('../../services/bidding-list-service') + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/bids/requirements/:id': async (params) => { + // spinner + import('../../components/app-spinner').then(loader => { + return new loader.default().show({target: '#requirement-container'}).then(t => t.template.show()) + }) + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', '#initial-section', '#bids-info-container'], 'none') + display.default(['#requirements-container'], 'block') + }) + + // load Information section + import('../../pages/bidding-req-section').then(res => { + // template + const temp = new res.template({id: params.id}) + const template = temp.render() + template.then(html => { + // DOM + const reqSec = document.querySelector('requirement-section') || document.querySelector('#requirement-container') + if(reqSec) reqSec.replaceWith(html) + + // other content + temp.setStatus() + + }).then(() => { + temp.getAttachments() + temp.getAwardees() + }) + + + }) + + // load proposal section + return import('../../pages/requirement-proposal-section').then(res => { + // template + const temp = new res.default({id: params.id}) + temp.then(html => { + // DOM + const reqSec = document.querySelector('proposal-section') || document.querySelector('#requirement-proposal-container') + if(reqSec) reqSec.replaceWith(html) + }) + }) + } + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/feedback.js b/src/routers/feedback.js new file mode 100644 index 0000000..5fd937a --- /dev/null +++ b/src/routers/feedback.js @@ -0,0 +1,35 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../utils/display-styler') +const ScriptLoader = import('../utils/script-loader') +const Menuselector = import('../utils/menu-selector') + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + + appRoute.on({ + '' : () => { }, + '/feedback/*' : () => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', 'registration-section', '#bids-report-section', '#bids-info-container', '#bids-report-section'], 'none') + display.default(['#feedback-section'], 'block') + }) + + // loade Information section + return import('../pages/feedback-form').then(res => { + // template + const temp = new res.template() + const template = temp.render() + template.then(html => { + // DOM + const infMenu = document.querySelector('feedback-section') || document.querySelector('#feedback-section') + if(infMenu) infMenu.replaceWith(html) + }) + }) + } + }).resolve() + +}) \ No newline at end of file diff --git a/src/routers/index.js b/src/routers/index.js new file mode 100644 index 0000000..bc9f217 --- /dev/null +++ b/src/routers/index.js @@ -0,0 +1,129 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../utils/display-styler') +const ScriptLoader = import('../utils/script-loader') +const DropdownLoader = import('../utils/dropdown-loader/') +const Menuselector = import('../utils/menu-selector') + +/** + * Header + */ +const loadHeader = () => { + const MainHeader = import('../components/main-header') + const ProfileLoader = import('../utils/profile-loader') + // header + return MainHeader.then(res => { + const headerSec = document.querySelector('header') + headerSec.innerHTML = res.default + // load MSAL + ScriptLoader.then(loader => loader.default(headerSec)) + // load profile + ProfileLoader.then(loader => { loader.default() }) + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.list-bids-container'],'none') + }) + }) +} + + +/** + * Left Sidebar + */ +const loadLeftSidebar = (activeMenuID) => { + const Lsidebar = import('../components/left-sidebar') + // header + return Lsidebar.then(res => { + const lSec = document.querySelector('left-sidebar') + if (!lSec) return 0 + // sidebar's content + const aside = document.createElement('aside') + aside.classList.add('col', 'col-lg-2', 'col-md-3', 'col-xs-12', 'd-none', 'd-lg-block') + aside.innerHTML = res.default + aside.setAttribute('style', 'background:rgba(0,0,0,0.8);min-width:100px;') + lSec.replaceWith(aside) + // load MSAL + ScriptLoader.then(loader => loader.default(aside)) + }) +} + +/** + * Welcome page + */ +const loadWelcome = () => { + const HomePage = import('../pages/home-section') + + HomePage.then(res => { + const wSec = document.querySelector('.welcome-section') + // load page + wSec.innerHTML = res.default + // load scripts + ScriptLoader.then(loader => loader.default(wSec)) + // change visibility + DisplayStyler.then(display => { + display.default(['.welcome-section'],'block') + }) + }) +} + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () =>{ + // redirect to home by default + window.location.hash = '/home' + }, + '/auth' : () => { + // redirect to o365 authentication page + window.location = 'auth.html' + }, + '/home' : () => { + // detect login instance + if (!window.localStorage.getItem('role')) { + window.location.hash = '#/logout' + } + // load components + loadHeader() + loadLeftSidebar().then(() => { + Menuselector.then(loader => { new loader.default().active('home_menu') }) + }) + loadWelcome() + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + }, + '/bids/*': () => { + // load components + loadHeader() + loadLeftSidebar().then(() => { + import('./bidding') + }) + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + + + }, + '/inv/*':()=>{ + // load components + loadHeader() + loadLeftSidebar().then(() => { + Menuselector.then(loader => { new loader.default().active('inv-menu-list') }) + import('./invitation') + }) + // dropdown + DropdownLoader.then(loader => loader.default('device-dropdown')) + }, + 'feedback/*': () => { + loadHeader() + loadLeftSidebar().then(() => { + Menuselector.then(loader => { new loader.default().active('feedback-menu-list') }) + import('./feedback') + }) + }, + '/logout/' : () => { + return (localStorage.getItem('role') === 'supplier') ? (window.location = `${window.location.origin}${window.location.pathname}cauth.html`) : window.location.hash = '/auth' + + }, + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/invitation.js b/src/routers/invitation.js new file mode 100644 index 0000000..537050d --- /dev/null +++ b/src/routers/invitation.js @@ -0,0 +1,49 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../utils/display-styler') +const ScriptLoader = import('../utils/script-loader') +const Menuselector = import('../utils/menu-selector') +const InitSection = import('../pages/initial-invitation-section') + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + const appRoute2 = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/inv/*' : () => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', 'registration-section'], 'none') + display.default(['.list-bids-container'], 'block') + }) + console.log('invitation') + } + }).resolve() + + + appRoute2.on({ + '' : () => { }, + '/inv/all' : async () => { + let ActionsInfo = await import('../pages/invitation-section/actions') + Menuselector.then(loader => { new loader.default().active('inv-menu-list') }) + InitSection.then(res => document.querySelector('initial-section') ? document.querySelector('initial-section').replaceWith(res.default) : 0) + // get bidding + ActionsInfo.loadListSection().then(() => { + ActionsInfo.list() + }) + }, + '/inv/:id/info/': async (params) => { + // load list on page refresh or later + if (!document.querySelector('.list-bids-container')) { + const __ActionsInfo = await import('../pages/invitation-section/actions') + Menuselector.then(loader => { new loader.default().active('bids-menu-list-all') }) + __ActionsInfo.loadListSection().then(() => { + __ActionsInfo.list() + }) + } + import('./invitation/info') + }, + }).resolve() +}) \ No newline at end of file diff --git a/src/routers/invitation/info.js b/src/routers/invitation/info.js new file mode 100644 index 0000000..2ae5f66 --- /dev/null +++ b/src/routers/invitation/info.js @@ -0,0 +1,61 @@ +const Navigo = import('navigo') +const DisplayStyler = import('../../utils/display-styler') +const ScriptLoader = import('../../utils/script-loader') +const DropdownLoader = import('../../utils/dropdown-loader') +const Menuselector = import('../../utils/menu-selector') +const Serv = import('../../services/bidding-inv-service') +const CustomerReviews = import('../../components/customer-reviews') + +// change list to active +const activateList = (id) => { + document.querySelectorAll(`.list`).forEach((el, index) => { + if (el.getAttribute('data-list') == id) { + el.classList.add('active') + } else { + el.classList.remove('active') + } + }) +} + + +Navigo.then((Navigo) => { + // Navigo instance + const appRoute = new Navigo.default('http://localhost/bms_rethink/www/', true) + + appRoute.on({ + '' : () => { }, + '/inv/:id/info/': async (params) => { + // change visibility + DisplayStyler.then(display => { + display.default(['splash-page', '.welcome-section', '#initial-section'], 'none') + display.default(['#bids-info-container'], 'block') + }) + + activateList(params.id) + + // loade Information section + import('../../pages/invitation-info-section').then(res => { + // template + const temp = new res.template({id: params.id}) + const template = temp.render() + template.then(html => { + + // DOM + const infMenu = document.querySelector('info-section') || document.querySelector('#inv-info-container') + if(infMenu) infMenu.replaceWith(html) + + // other content + temp.setStatus() + + }).then(() => { + //temp.getAttachments() + //temp.getParticulars() + + + }) + + }) + + } + }).resolve() +}) \ No newline at end of file diff --git a/src/services/accounts.js b/src/services/accounts.js new file mode 100644 index 0000000..6db00b6 --- /dev/null +++ b/src/services/accounts.js @@ -0,0 +1,41 @@ +const Network = import('../config/api') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + cbaList(opt = {}) { + return this.__getData(`/accounts/?role=${opt.filter || 'all'}&${opt.page || 1}&token=${opt.token}`) + } + +} \ No newline at end of file diff --git a/src/services/auth-service.js b/src/services/auth-service.js new file mode 100644 index 0000000..e80bf84 --- /dev/null +++ b/src/services/auth-service.js @@ -0,0 +1,58 @@ +import sha1 from 'sha1' +const Network = import('../config/api') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + + + login (username, password) { + const __payload = { + username, + password: sha1(password) + } + return this.__postData(`/auth/company.php/`, __payload) + } + + store (credential) { + window.localStorage.setItem('credentials',JSON.stringify(credential)) + window.localStorage.setItem('token', credential.token) + } + + setCredentials () { + return window.localStorage.setItem('credentials') + } + +} + \ No newline at end of file diff --git a/src/services/bidding-attachment-service.js b/src/services/bidding-attachment-service.js new file mode 100644 index 0000000..9916e8e --- /dev/null +++ b/src/services/bidding-attachment-service.js @@ -0,0 +1,55 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + recent(opt = {}){ + return this.__getData(`/bidding/attachments/recent/?page=${opt.page || 1}&token=${opt.token}×tamp=${this.timestamp}`) + } + + create(opt) { + return this.__postData(`/bidding/attachments/?timestamp=${this.timestamp}`, opt) + } + + attach(opt) { + return this.__postData(`/bidding/attachments/recent/?timestamp=${this.timestamp}`, opt) + } + + remove(opt){ + return this.create(opt) + } + + +} \ No newline at end of file diff --git a/src/services/bidding-inv-service.js b/src/services/bidding-inv-service.js new file mode 100644 index 0000000..c9bfb68 --- /dev/null +++ b/src/services/bidding-inv-service.js @@ -0,0 +1,51 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + list(opt) { + return this.__getData(`/bidding/invitations/?status=${opt.filter}&page=${opt.page}&token=${opt.token}`) + } + + view(opt) { + return this.__getData(`/bidding/invitations/?id=${opt.id}&token=${opt.token}`) + } + + search(opt){ + return this.__getData(`/bidding/invitations/search.php?token=${opt.token}¶m=${opt.param}×tamp=${this.timestamp}&page=${opt.page}`) + } + + +} \ No newline at end of file diff --git a/src/services/bidding-list-service.js b/src/services/bidding-list-service.js new file mode 100644 index 0000000..be5bd17 --- /dev/null +++ b/src/services/bidding-list-service.js @@ -0,0 +1,118 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + + async lists(opt = {}){ + return await new Promise((resolve, reject) => { + Network.then(net => { + const url = `${net.default.url}/bidding/?status=${opt.filter || 'all'}&page=${opt.page || 1}&token=${opt.token}` + // XHR + XHR.then(loader => { + const http = new loader.default() + http.request({method:'GET', url:url}).then(res => res ? resolve(JSON.parse(res)) : '') + }) + }) + + }) + } + + view(opt = {}){ + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}/bidding/?id=${opt.id}&token=${opt.token}`).then(res => { + resolve(res.json()) + }) + }) + }) + } + + status(opt){ + return this.__postData(`/bidding/status.php/?timestamp=${this.timestamp}`, opt) + } + + create(opt) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}/bidding/?timestamp=${this.timestamp}`, + { + method: 'POST', + body: JSON.stringify(opt), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + remove(opt){ + return this.create(opt) + } + + search(opt){ + return this.__getData(`/bidding/search.php?token=${opt.token}¶m=${opt.param}×tamp=${this.timestamp}&page=${opt.page}`) + } + + signatories(opt = {}){ + return this.__postData('/bidding/signatories.php/?timestamp=${this.timestamp}', opt) + } + + send (opt) { + return this.__postData(`/bidding/collaborators/?timestamp=${this.timestamp}`, opt) + } + + reviews (opt) { + return this.__getData(`/bidding/requirements/feedback.php/?token=${opt.token}&id=${opt.id}×tamp=${this.timestamp}&bidding_request=true`) + } + + /*create(opt){ + var url=`${Net}/bidding/` + return XHR.request({method:'POST',url:url,body:JSON.stringify(opt)}) + } + update(opt){ + var url=`${Net}/bidding/` + return XHR.request({method:'POST',url:url,body:JSON.stringify(opt)}) + } + particulars(opt){ + var url=`${Net}/bidding/particulars/` + return XHR.request({method:'POST',url:url,body:JSON.stringify(opt)}) + } + requirements(opt){ + var url=`${Net}/bidding/requirements/` + return XHR.request({method:'POST',url:url,body:JSON.stringify(opt)}) + } */ +} \ No newline at end of file diff --git a/src/services/bidding-part-service.js b/src/services/bidding-part-service.js new file mode 100644 index 0000000..cc8da7f --- /dev/null +++ b/src/services/bidding-part-service.js @@ -0,0 +1,51 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url, isJSON = true) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + return isJSON ? resolve(res.json()) : resolve(res.text()) + }) + }) + }) + } + + create(opt) { + return this.__postData(`/bidding/particulars/?timestamp=${this.timestamp}`, opt) + } + + remove(opt) { + return this.__postData(`/bidding/particulars/?timestamp=${this.timestamp}`, opt) + } + + view(opt) { + return this.__getData(`/bidding/particulars/?timestamp=${this.timestamp}&token=${opt.token}&id=${opt.id}`) + } + + +} \ No newline at end of file diff --git a/src/services/bidding-prod-service.js b/src/services/bidding-prod-service.js new file mode 100644 index 0000000..2f9c364 --- /dev/null +++ b/src/services/bidding-prod-service.js @@ -0,0 +1,48 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url, isJSON = true) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + return isJSON ? resolve(res.json()) : resolve(res.text()) + }) + }) + }) + } + + create(opt) { + return this.__postData(`/bidding/requirements/?timestamp=${this.timestamp}`, opt) + } + + view(opt) { + return this.__getData(`/bidding/requirements/?timestamp=${this.timestamp}&id=${opt.id}&token=${opt.token}`) + } + + + +} \ No newline at end of file diff --git a/src/services/bidding-proposal-attachment-service.js b/src/services/bidding-proposal-attachment-service.js new file mode 100644 index 0000000..9d46917 --- /dev/null +++ b/src/services/bidding-proposal-attachment-service.js @@ -0,0 +1,45 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body, isJSON = true) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + return isJSON ? resolve(res.json()) : resolve(res.text()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + + remove(opt) { + return this.__postData(`/bidding/invitations/attachments/?timestamp=${this.timestamp}`, opt) + } + + + +} diff --git a/src/assets/js/modules/Invitation/Services/Proposal.js b/src/services/bidding-proposal-service.js similarity index 50% rename from src/assets/js/modules/Invitation/Services/Proposal.js rename to src/services/bidding-proposal-service.js index af81ee4..3df8cd3 100644 --- a/src/assets/js/modules/Invitation/Services/Proposal.js +++ b/src/services/bidding-proposal-service.js @@ -1,28 +1,53 @@ -import Network from '../../../config/network/network.config.js' - -const NetConf = new Network() -const XHR=new window.bms.exports.XHR() +const Network = import('../config/api') +const XHR = import('../utils/xhr') export default class{ constructor(){ this.timestamp = new Date().getTime() } - lists(options={}){ - let opt=options||{} - opt.page=opt.page||1 - opt.filter=opt.filter||'all' - - const url=`${NetConf.get()}/bidding/proposals/?id=${opt.id}&status=${opt.filter}&${opt.page}&token=${opt.token}×tamp=${this.timestamp}` - - //require exports.js - const XHR=new window.bms.exports.XHR() - return XHR.request({method:'GET',url:url}) + __postData(url, body, isJSON = true) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + return isJSON ? resolve(res.json()) : resolve(res.text()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + create(opt) { + return this.__postData(`/bidding/proposals/?timestamp=${this.timestamp}`, opt) + } + view(opt) { + return this.__getData(`/bidding/proposals/?id=${opt.id}&token=${opt.token}×tamp=${this.timestamp}`) } - view(opt = {}){ - return XHR.request({method:'GET',url:`${NetConf.get()}/bidding/proposals/?id=${opt.id}&token=${opt.token}×tamp=${this.timestamp}`}) + + remove(opt) { + return this.__postData(`/bidding/proposals/?timestamp=${this.timestamp}`, opt, false) } + + /* status(opt = {}){ var url=`${NetConf.get()}/bidding/status.php?timestamp=${this.timestamp}` @@ -47,6 +72,7 @@ export default class{ reference(opt = {}){ const url=`${NetConf.get()}/bidding/proposals/reference.php/?timestamp=${this.timestamp}` return XHR.request({method:'POST',url:url,body:JSON.stringify(opt)}) - } - + }*/ + + } \ No newline at end of file diff --git a/src/services/bidding-reports-service.js b/src/services/bidding-reports-service.js new file mode 100644 index 0000000..1d143cb --- /dev/null +++ b/src/services/bidding-reports-service.js @@ -0,0 +1,41 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class { + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + summary(opt){ + return this.__getData(`/bidding/reports/summary.php?from=${opt.from}&to=${opt.to}&token=${opt.token}`) + } +} \ No newline at end of file diff --git a/src/services/bidding-req-attachment-service.js b/src/services/bidding-req-attachment-service.js new file mode 100644 index 0000000..8f520cd --- /dev/null +++ b/src/services/bidding-req-attachment-service.js @@ -0,0 +1,54 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + create(opt) { + return this.__postData(`/bidding/requirements/attachments/?timestamp=${this.timestamp}`, opt) + } + + remove(opt){ + return this.create(opt) + } + + recent(opt) { + return this.__getData(`/bidding/requirements/attachments/recent/?page=${opt.page || 1 }&token=${opt.token}×tamp=${this.timestamp}`) + } + + attach(opt) { + return this.__postData('/bidding/requirements/attachments/recent/', opt) + } + +} \ No newline at end of file diff --git a/src/services/bidding-req-proposal-service.js b/src/services/bidding-req-proposal-service.js new file mode 100644 index 0000000..fdbd092 --- /dev/null +++ b/src/services/bidding-req-proposal-service.js @@ -0,0 +1,53 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + lists(opt) { + return this.__getData(`/bidding/proposals/?id=${opt.id}&status=${opt.filter}&page=${opt.page}&token=${opt.token}×tamp=${this.timestamp}`) + } + + view(opt) { + return this.__getData(`/bidding/proposals/?id=${opt.id}&token=${opt.token}×tamp=${this.timestamp}`) + } + + send(opt) { + return this.__postData(`/bidding/proposals/?timestamp=${this.timestamp}`, opt) + } + + award(opt) { + return this.send(opt) + } +} \ No newline at end of file diff --git a/src/services/bidding-req-service.js b/src/services/bidding-req-service.js new file mode 100644 index 0000000..b447288 --- /dev/null +++ b/src/services/bidding-req-service.js @@ -0,0 +1,70 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + view(opt) { + return this.__getData(`/bidding/requirements/?id=${opt.id}&token=${opt.token}×tamp=${this.timestamp}`) + } + + remove(opt) { + return this.__postData(`/bidding/requirements/?timestamp=${this.timestamp}`, opt) + } + + deadline(opt) { + return this.__postData(`/bidding/requirements/deadline.php/?timestamp=${this.timestamp}`, opt) + } + + invite(opt) { + return this.__postData(`/bidding/requirements/recepients/?timestamp=${this.timestamp}`, opt) + } + + winner(opt) { + return this.invite(opt) + } + + award(opt) { + return this.invite(opt) + } + + sendFeedbackToAwardee(opt){ + return this.__postData(`/bidding/requirements/feedback.php/?timestamp=${this.timestamp}`, opt) + } + + getFeedbackAwardee(opt) { + return this.__getData(`/bidding/requirements/feedback.php/?token=${opt.token}&id=${opt.id}×tamp=${this.timestamp}`) + } + +} \ No newline at end of file diff --git a/src/services/feedback-services.js b/src/services/feedback-services.js new file mode 100644 index 0000000..b5d7db4 --- /dev/null +++ b/src/services/feedback-services.js @@ -0,0 +1,41 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class { + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + feedback(opt){ + return this.__postData(`/feedback/`, opt) + } +} \ No newline at end of file diff --git a/src/services/supplier-service.js b/src/services/supplier-service.js new file mode 100644 index 0000000..6f2d608 --- /dev/null +++ b/src/services/supplier-service.js @@ -0,0 +1,45 @@ +const Network = import('../config/api') +const XHR = import('../utils/xhr') + +export default class{ + constructor(){ + this.timestamp = new Date().getTime() + } + + __postData(url, body) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'POST', + body: JSON.stringify(body), + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + __getData(url) { + return new Promise((resolve, reject) => { + Network.then(net => { + fetch(`${net.default.url}${url}`, + { + method: 'GET', + }) + .then(res => { + resolve(res.json()) + }) + }) + }) + } + + list(opt) { + return this.__getData(`/suppliers/?status=${opt.filter}&page=${opt.page}`) + } + + search(opt) { + return this.__getData(`/suppliers/?&page=${opt.page}¶m=${opt.param}&search=true`) + } +} \ No newline at end of file diff --git a/src/spec.html b/src/spec.html deleted file mode 100644 index 71f00de..0000000 --- a/src/spec.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Jasmine Spec Runner - - - - - - - - - - - - - - - - - - - - diff --git a/src/spec/helper.js b/src/spec/helper.js deleted file mode 100644 index 929f776..0000000 --- a/src/spec/helper.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -afterEach(function() { - document.getElementById('stage').innerHTML = ''; -}); - -var helper = { - trigger: function(obj, name) { - var e = document.createEvent('Event'); - e.initEvent(name, true, true); - obj.dispatchEvent(e); - }, - getComputedStyle: function(querySelector, property) { - var element = document.querySelector(querySelector); - return window.getComputedStyle(element).getPropertyValue(property); - } -}; diff --git a/src/spec/index.js b/src/spec/index.js deleted file mode 100644 index 20f8be5..0000000 --- a/src/spec/index.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -describe('app', function() { - describe('initialize', function() { - it('should bind deviceready', function() { - runs(function() { - spyOn(app, 'onDeviceReady'); - app.initialize(); - helper.trigger(window.document, 'deviceready'); - }); - - waitsFor(function() { - return (app.onDeviceReady.calls.length > 0); - }, 'onDeviceReady should be called once', 500); - - runs(function() { - expect(app.onDeviceReady).toHaveBeenCalled(); - }); - }); - }); - - describe('onDeviceReady', function() { - it('should report that it fired', function() { - spyOn(app, 'receivedEvent'); - app.onDeviceReady(); - expect(app.receivedEvent).toHaveBeenCalledWith('deviceready'); - }); - }); - - describe('receivedEvent', function() { - beforeEach(function() { - var el = document.getElementById('stage'); - el.innerHTML = ['
', - '

Listening

', - '

Received

', - '
'].join('\n'); - }); - - it('should hide the listening element', function() { - app.receivedEvent('deviceready'); - var displayStyle = helper.getComputedStyle('#deviceready .listening', 'display'); - expect(displayStyle).toEqual('none'); - }); - - it('should show the received element', function() { - app.receivedEvent('deviceready'); - var displayStyle = helper.getComputedStyle('#deviceready .received', 'display'); - expect(displayStyle).toEqual('block'); - }); - }); -}); diff --git a/src/spec/lib/jasmine-1.2.0/MIT.LICENSE b/src/spec/lib/jasmine-1.2.0/MIT.LICENSE deleted file mode 100644 index 7c435ba..0000000 --- a/src/spec/lib/jasmine-1.2.0/MIT.LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008-2011 Pivotal Labs - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/spec/lib/jasmine-1.2.0/jasmine-html.js b/src/spec/lib/jasmine-1.2.0/jasmine-html.js deleted file mode 100644 index a0b0639..0000000 --- a/src/spec/lib/jasmine-1.2.0/jasmine-html.js +++ /dev/null @@ -1,616 +0,0 @@ -jasmine.HtmlReporterHelpers = {}; - -jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { - el.appendChild(child); - } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { - var results = child.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - - return status; -}; - -jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { - var parentDiv = this.dom.summary; - var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; - var parent = child[parentSuite]; - - if (parent) { - if (typeof this.views.suites[parent.id] == 'undefined') { - this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); - } - parentDiv = this.views.suites[parent.id].element; - } - - parentDiv.appendChild(childElement); -}; - - -jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { - for(var fn in jasmine.HtmlReporterHelpers) { - ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; - } -}; - -jasmine.HtmlReporter = function(_doc) { - var self = this; - var doc = _doc || window.document; - - var reporterView; - - var dom = {}; - - // Jasmine Reporter Public Interface - self.logRunningSpecs = false; - - self.reportRunnerStarting = function(runner) { - var specs = runner.specs() || []; - - if (specs.length == 0) { - return; - } - - createReporterDom(runner.env.versionString()); - doc.body.appendChild(dom.reporter); - - reporterView = new jasmine.HtmlReporter.ReporterView(dom); - reporterView.addSpecs(specs, self.specFilter); - }; - - self.reportRunnerResults = function(runner) { - reporterView && reporterView.complete(); - }; - - self.reportSuiteResults = function(suite) { - reporterView.suiteComplete(suite); - }; - - self.reportSpecStarting = function(spec) { - if (self.logRunningSpecs) { - self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } - }; - - self.reportSpecResults = function(spec) { - reporterView.specComplete(spec); - }; - - self.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } - }; - - self.specFilter = function(spec) { - if (!focusedSpecName()) { - return true; - } - - return spec.getFullName().indexOf(focusedSpecName()) === 0; - }; - - return self; - - function focusedSpecName() { - var specName; - - (function memoizeFocusedSpec() { - if (specName) { - return; - } - - var paramMap = []; - var params = doc.location.search.substring(1).split('&'); - - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - specName = paramMap.spec; - })(); - - return specName; - } - - function createReporterDom(version) { - dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, - dom.banner = self.createDom('div', { className: 'banner' }, - self.createDom('span', { className: 'title' }, "Jasmine "), - self.createDom('span', { className: 'version' }, version)), - - dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), - dom.alert = self.createDom('div', {className: 'alert'}), - dom.results = self.createDom('div', {className: 'results'}, - dom.summary = self.createDom('div', { className: 'summary' }), - dom.details = self.createDom('div', { id: 'details' })) - ); - } -}; -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) { - this.startedAt = new Date(); - this.runningSpecCount = 0; - this.completeSpecCount = 0; - this.passedCount = 0; - this.failedCount = 0; - this.skippedCount = 0; - - this.createResultsMenu = function() { - this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, - this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), - ' | ', - this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); - - this.summaryMenuItem.onclick = function() { - dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); - }; - - this.detailsMenuItem.onclick = function() { - showDetails(); - }; - }; - - this.addSpecs = function(specs, specFilter) { - this.totalSpecCount = specs.length; - - this.views = { - specs: {}, - suites: {} - }; - - for (var i = 0; i < specs.length; i++) { - var spec = specs[i]; - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); - if (specFilter(spec)) { - this.runningSpecCount++; - } - } - }; - - this.specComplete = function(spec) { - this.completeSpecCount++; - - if (isUndefined(this.views.specs[spec.id])) { - this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); - } - - var specView = this.views.specs[spec.id]; - - switch (specView.status()) { - case 'passed': - this.passedCount++; - break; - - case 'failed': - this.failedCount++; - break; - - case 'skipped': - this.skippedCount++; - break; - } - - specView.refresh(); - this.refresh(); - }; - - this.suiteComplete = function(suite) { - var suiteView = this.views.suites[suite.id]; - if (isUndefined(suiteView)) { - return; - } - suiteView.refresh(); - }; - - this.refresh = function() { - - if (isUndefined(this.resultsMenu)) { - this.createResultsMenu(); - } - - // currently running UI - if (isUndefined(this.runningAlert)) { - this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); - dom.alert.appendChild(this.runningAlert); - } - this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); - - // skipped specs UI - if (isUndefined(this.skippedAlert)) { - this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); - } - - this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.skippedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.skippedAlert); - } - - // passing specs UI - if (isUndefined(this.passedAlert)) { - this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); - } - this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); - - // failing specs UI - if (isUndefined(this.failedAlert)) { - this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); - } - this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); - - if (this.failedCount === 1 && isDefined(dom.alert)) { - dom.alert.appendChild(this.failedAlert); - dom.alert.appendChild(this.resultsMenu); - } - - // summary info - this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); - this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; - }; - - this.complete = function() { - dom.alert.removeChild(this.runningAlert); - - this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; - - if (this.failedCount === 0) { - dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); - } else { - showDetails(); - } - - dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); - }; - - return this; - - function showDetails() { - if (dom.reporter.className.search(/showDetails/) === -1) { - dom.reporter.className += " showDetails"; - } - } - - function isUndefined(obj) { - return typeof obj === 'undefined'; - } - - function isDefined(obj) { - return !isUndefined(obj); - } - - function specPluralizedFor(count) { - var str = count + " spec"; - if (count > 1) { - str += "s" - } - return str; - } - -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); - - -jasmine.HtmlReporter.SpecView = function(spec, dom, views) { - this.spec = spec; - this.dom = dom; - this.views = views; - - this.symbol = this.createDom('li', { className: 'pending' }); - this.dom.symbolSummary.appendChild(this.symbol); - - this.summary = this.createDom('div', { className: 'specSummary' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.description) - ); - - this.detail = this.createDom('div', { className: 'specDetail' }, - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(this.spec.getFullName()), - title: this.spec.getFullName() - }, this.spec.getFullName()) - ); -}; - -jasmine.HtmlReporter.SpecView.prototype.status = function() { - return this.getSpecStatus(this.spec); -}; - -jasmine.HtmlReporter.SpecView.prototype.refresh = function() { - this.symbol.className = this.status(); - - switch (this.status()) { - case 'skipped': - break; - - case 'passed': - this.appendSummaryToSuiteDiv(); - break; - - case 'failed': - this.appendSummaryToSuiteDiv(); - this.appendFailureDetail(); - break; - } -}; - -jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { - this.summary.className += ' ' + this.status(); - this.appendToSummary(this.spec, this.summary); -}; - -jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { - this.detail.className += ' ' + this.status(); - - var resultItems = this.spec.results().getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - this.detail.appendChild(messagesDiv); - this.dom.details.appendChild(this.detail); - } -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { - this.suite = suite; - this.dom = dom; - this.views = views; - - this.element = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) - ); - - this.appendToSummary(this.suite, this.element); -}; - -jasmine.HtmlReporter.SuiteView.prototype.status = function() { - return this.getSpecStatus(this.suite); -}; - -jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { - this.element.className += " " + this.status(); -}; - -jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); - -/* @deprecated Use jasmine.HtmlReporter instead - */ -jasmine.TrivialReporter = function(doc) { - this.document = doc || document; - this.suiteDivs = {}; - this.logRunningSpecs = false; -}; - -jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) { - var el = document.createElement(type); - - for (var i = 2; i < arguments.length; i++) { - var child = arguments[i]; - - if (typeof child === 'string') { - el.appendChild(document.createTextNode(child)); - } else { - if (child) { el.appendChild(child); } - } - } - - for (var attr in attrs) { - if (attr == "className") { - el[attr] = attrs[attr]; - } else { - el.setAttribute(attr, attrs[attr]); - } - } - - return el; -}; - -jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { - var showPassed, showSkipped; - - this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, - this.createDom('div', { className: 'banner' }, - this.createDom('div', { className: 'logo' }, - this.createDom('span', { className: 'title' }, "Jasmine"), - this.createDom('span', { className: 'version' }, runner.env.versionString())), - this.createDom('div', { className: 'options' }, - "Show ", - showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), - showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), - this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") - ) - ), - - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running..."), - this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) - ); - - this.document.body.appendChild(this.outerDiv); - - var suites = runner.suites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - var suiteDiv = this.createDom('div', { className: 'suite' }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); - this.suiteDivs[suite.id] = suiteDiv; - var parentDiv = this.outerDiv; - if (suite.parentSuite) { - parentDiv = this.suiteDivs[suite.parentSuite.id]; - } - parentDiv.appendChild(suiteDiv); - } - - this.startedAt = new Date(); - - var self = this; - showPassed.onclick = function(evt) { - if (showPassed.checked) { - self.outerDiv.className += ' show-passed'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); - } - }; - - showSkipped.onclick = function(evt) { - if (showSkipped.checked) { - self.outerDiv.className += ' show-skipped'; - } else { - self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); - } - }; -}; - -jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { - var results = runner.results(); - var className = (results.failedCount > 0) ? "runner failed" : "runner passed"; - this.runnerDiv.setAttribute("class", className); - //do it twice for IE - this.runnerDiv.setAttribute("className", className); - var specs = runner.specs(); - var specCount = 0; - for (var i = 0; i < specs.length; i++) { - if (this.specFilter(specs[i])) { - specCount++; - } - } - var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); - message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; - this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); - - this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); -}; - -jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { - var results = suite.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.totalCount === 0) { // todo: change this to check results.skipped - status = 'skipped'; - } - this.suiteDivs[suite.id].className += " " + status; -}; - -jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) { - if (this.logRunningSpecs) { - this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); - } -}; - -jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { - var results = spec.results(); - var status = results.passed() ? 'passed' : 'failed'; - if (results.skipped) { - status = 'skipped'; - } - var specDiv = this.createDom('div', { className: 'spec ' + status }, - this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { - className: 'description', - href: '?spec=' + encodeURIComponent(spec.getFullName()), - title: spec.getFullName() - }, spec.description)); - - - var resultItems = results.getItems(); - var messagesDiv = this.createDom('div', { className: 'messages' }); - for (var i = 0; i < resultItems.length; i++) { - var result = resultItems[i]; - - if (result.type == 'log') { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); - } else if (result.type == 'expect' && result.passed && !result.passed()) { - messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - - if (result.trace.stack) { - messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); - } - } - } - - if (messagesDiv.childNodes.length > 0) { - specDiv.appendChild(messagesDiv); - } - - this.suiteDivs[spec.suite.id].appendChild(specDiv); -}; - -jasmine.TrivialReporter.prototype.log = function() { - var console = jasmine.getGlobal().console; - if (console && console.log) { - if (console.log.apply) { - console.log.apply(console, arguments); - } else { - console.log(arguments); // ie fix: console.log.apply doesn't exist on ie - } - } -}; - -jasmine.TrivialReporter.prototype.getLocation = function() { - return this.document.location; -}; - -jasmine.TrivialReporter.prototype.specFilter = function(spec) { - var paramMap = {}; - var params = this.getLocation().search.substring(1).split('&'); - for (var i = 0; i < params.length; i++) { - var p = params[i].split('='); - paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); - } - - if (!paramMap.spec) { - return true; - } - return spec.getFullName().indexOf(paramMap.spec) === 0; -}; diff --git a/src/spec/lib/jasmine-1.2.0/jasmine.css b/src/spec/lib/jasmine-1.2.0/jasmine.css deleted file mode 100644 index 826e575..0000000 --- a/src/spec/lib/jasmine-1.2.0/jasmine.css +++ /dev/null @@ -1,81 +0,0 @@ -body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } - -#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -#HTMLReporter a { text-decoration: none; } -#HTMLReporter a:hover { text-decoration: underline; } -#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } -#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } -#HTMLReporter #jasmine_content { position: fixed; right: 100%; } -#HTMLReporter .version { color: #aaaaaa; } -#HTMLReporter .banner { margin-top: 14px; } -#HTMLReporter .duration { color: #aaaaaa; float: right; } -#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } -#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } -#HTMLReporter .symbolSummary li.passed { font-size: 14px; } -#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } -#HTMLReporter .symbolSummary li.failed { line-height: 9px; } -#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } -#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } -#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } -#HTMLReporter .symbolSummary li.pending { line-height: 11px; } -#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } -#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -#HTMLReporter .runningAlert { background-color: #666666; } -#HTMLReporter .skippedAlert { background-color: #aaaaaa; } -#HTMLReporter .skippedAlert:first-child { background-color: #333333; } -#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } -#HTMLReporter .passingAlert { background-color: #a6b779; } -#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } -#HTMLReporter .failingAlert { background-color: #cf867e; } -#HTMLReporter .failingAlert:first-child { background-color: #b03911; } -#HTMLReporter .results { margin-top: 14px; } -#HTMLReporter #details { display: none; } -#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } -#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter.showDetails .summary { display: none; } -#HTMLReporter.showDetails #details { display: block; } -#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -#HTMLReporter .summary { margin-top: 14px; } -#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } -#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } -#HTMLReporter .summary .specSummary.failed a { color: #b03911; } -#HTMLReporter .description + .suite { margin-top: 0; } -#HTMLReporter .suite { margin-top: 14px; } -#HTMLReporter .suite a { color: #333333; } -#HTMLReporter #details .specDetail { margin-bottom: 28px; } -#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } -#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } -#HTMLReporter .resultMessage span.result { display: block; } -#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } - -#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } -#TrivialReporter a:visited, #TrivialReporter a { color: #303; } -#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } -#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } -#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } -#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } -#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } -#TrivialReporter .runner.running { background-color: yellow; } -#TrivialReporter .options { text-align: right; font-size: .8em; } -#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } -#TrivialReporter .suite .suite { margin: 5px; } -#TrivialReporter .suite.passed { background-color: #dfd; } -#TrivialReporter .suite.failed { background-color: #fdd; } -#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } -#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } -#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } -#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } -#TrivialReporter .spec.skipped { background-color: #bbb; } -#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } -#TrivialReporter .passed { background-color: #cfc; display: none; } -#TrivialReporter .failed { background-color: #fbb; } -#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } -#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } -#TrivialReporter .resultMessage .mismatch { color: black; } -#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } -#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } -#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } -#TrivialReporter #jasmine_content { position: fixed; right: 100%; } -#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/src/spec/lib/jasmine-1.2.0/jasmine.js b/src/spec/lib/jasmine-1.2.0/jasmine.js deleted file mode 100644 index 03bf89a..0000000 --- a/src/spec/lib/jasmine-1.2.0/jasmine.js +++ /dev/null @@ -1,2529 +0,0 @@ -var isCommonJS = typeof window == "undefined"; - -/** - * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. - * - * @namespace - */ -var jasmine = {}; -if (isCommonJS) exports.jasmine = jasmine; -/** - * @private - */ -jasmine.unimplementedMethod_ = function() { - throw new Error("unimplemented method"); -}; - -/** - * Use jasmine.undefined instead of undefined, since undefined is just - * a plain old variable and may be redefined by somebody else. - * - * @private - */ -jasmine.undefined = jasmine.___undefined___; - -/** - * Show diagnostic messages in the console if set to true - * - */ -jasmine.VERBOSE = false; - -/** - * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed. - * - */ -jasmine.DEFAULT_UPDATE_INTERVAL = 250; - -/** - * Default timeout interval in milliseconds for waitsFor() blocks. - */ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; - -jasmine.getGlobal = function() { - function getGlobal() { - return this; - } - - return getGlobal(); -}; - -/** - * Allows for bound functions to be compared. Internal use only. - * - * @ignore - * @private - * @param base {Object} bound 'this' for the function - * @param name {Function} function to find - */ -jasmine.bindOriginal_ = function(base, name) { - var original = base[name]; - if (original.apply) { - return function() { - return original.apply(base, arguments); - }; - } else { - // IE support - return jasmine.getGlobal()[name]; - } -}; - -jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); -jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); -jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); -jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); - -jasmine.MessageResult = function(values) { - this.type = 'log'; - this.values = values; - this.trace = new Error(); // todo: test better -}; - -jasmine.MessageResult.prototype.toString = function() { - var text = ""; - for (var i = 0; i < this.values.length; i++) { - if (i > 0) text += " "; - if (jasmine.isString_(this.values[i])) { - text += this.values[i]; - } else { - text += jasmine.pp(this.values[i]); - } - } - return text; -}; - -jasmine.ExpectationResult = function(params) { - this.type = 'expect'; - this.matcherName = params.matcherName; - this.passed_ = params.passed; - this.expected = params.expected; - this.actual = params.actual; - this.message = this.passed_ ? 'Passed.' : params.message; - - var trace = (params.trace || new Error(this.message)); - this.trace = this.passed_ ? '' : trace; -}; - -jasmine.ExpectationResult.prototype.toString = function () { - return this.message; -}; - -jasmine.ExpectationResult.prototype.passed = function () { - return this.passed_; -}; - -/** - * Getter for the Jasmine environment. Ensures one gets created - */ -jasmine.getEnv = function() { - var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); - return env; -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isArray_ = function(value) { - return jasmine.isA_("Array", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isString_ = function(value) { - return jasmine.isA_("String", value); -}; - -/** - * @ignore - * @private - * @param value - * @returns {Boolean} - */ -jasmine.isNumber_ = function(value) { - return jasmine.isA_("Number", value); -}; - -/** - * @ignore - * @private - * @param {String} typeName - * @param value - * @returns {Boolean} - */ -jasmine.isA_ = function(typeName, value) { - return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; -}; - -/** - * Pretty printer for expecations. Takes any object and turns it into a human-readable string. - * - * @param value {Object} an object to be outputted - * @returns {String} - */ -jasmine.pp = function(value) { - var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); - stringPrettyPrinter.format(value); - return stringPrettyPrinter.string; -}; - -/** - * Returns true if the object is a DOM Node. - * - * @param {Object} obj object to check - * @returns {Boolean} - */ -jasmine.isDomNode = function(obj) { - return obj.nodeType > 0; -}; - -/** - * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. - * - * @example - * // don't care about which function is passed in, as long as it's a function - * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); - * - * @param {Class} clazz - * @returns matchable object of the type clazz - */ -jasmine.any = function(clazz) { - return new jasmine.Matchers.Any(clazz); -}; - -/** - * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the - * attributes on the object. - * - * @example - * // don't care about any other attributes than foo. - * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); - * - * @param sample {Object} sample - * @returns matchable object for the sample - */ -jasmine.objectContaining = function (sample) { - return new jasmine.Matchers.ObjectContaining(sample); -}; - -/** - * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. - * - * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine - * expectation syntax. Spies can be checked if they were called or not and what the calling params were. - * - * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). - * - * Spies are torn down at the end of every spec. - * - * Note: Do not call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. - * - * @example - * // a stub - * var myStub = jasmine.createSpy('myStub'); // can be used anywhere - * - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // actual foo.not will not be called, execution stops - * spyOn(foo, 'not'); - - // foo.not spied upon, execution will continue to implementation - * spyOn(foo, 'not').andCallThrough(); - * - * // fake example - * var foo = { - * not: function(bool) { return !bool; } - * } - * - * // foo.not(val) will return val - * spyOn(foo, 'not').andCallFake(function(value) {return value;}); - * - * // mock example - * foo.not(7 == 7); - * expect(foo.not).toHaveBeenCalled(); - * expect(foo.not).toHaveBeenCalledWith(true); - * - * @constructor - * @see spyOn, jasmine.createSpy, jasmine.createSpyObj - * @param {String} name - */ -jasmine.Spy = function(name) { - /** - * The name of the spy, if provided. - */ - this.identity = name || 'unknown'; - /** - * Is this Object a spy? - */ - this.isSpy = true; - /** - * The actual function this spy stubs. - */ - this.plan = function() { - }; - /** - * Tracking of the most recent call to the spy. - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy.mostRecentCall.args = [1, 2]; - */ - this.mostRecentCall = {}; - - /** - * Holds arguments for each call to the spy, indexed by call count - * @example - * var mySpy = jasmine.createSpy('foo'); - * mySpy(1, 2); - * mySpy(7, 8); - * mySpy.mostRecentCall.args = [7, 8]; - * mySpy.argsForCall[0] = [1, 2]; - * mySpy.argsForCall[1] = [7, 8]; - */ - this.argsForCall = []; - this.calls = []; -}; - -/** - * Tells a spy to call through to the actual implemenatation. - * - * @example - * var foo = { - * bar: function() { // do some stuff } - * } - * - * // defining a spy on an existing property: foo.bar - * spyOn(foo, 'bar').andCallThrough(); - */ -jasmine.Spy.prototype.andCallThrough = function() { - this.plan = this.originalValue; - return this; -}; - -/** - * For setting the return value of a spy. - * - * @example - * // defining a spy from scratch: foo() returns 'baz' - * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); - * - * // defining a spy on an existing property: foo.bar() returns 'baz' - * spyOn(foo, 'bar').andReturn('baz'); - * - * @param {Object} value - */ -jasmine.Spy.prototype.andReturn = function(value) { - this.plan = function() { - return value; - }; - return this; -}; - -/** - * For throwing an exception when a spy is called. - * - * @example - * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' - * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); - * - * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' - * spyOn(foo, 'bar').andThrow('baz'); - * - * @param {String} exceptionMsg - */ -jasmine.Spy.prototype.andThrow = function(exceptionMsg) { - this.plan = function() { - throw exceptionMsg; - }; - return this; -}; - -/** - * Calls an alternate implementation when a spy is called. - * - * @example - * var baz = function() { - * // do some stuff, return something - * } - * // defining a spy from scratch: foo() calls the function baz - * var foo = jasmine.createSpy('spy on foo').andCall(baz); - * - * // defining a spy on an existing property: foo.bar() calls an anonymnous function - * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); - * - * @param {Function} fakeFunc - */ -jasmine.Spy.prototype.andCallFake = function(fakeFunc) { - this.plan = fakeFunc; - return this; -}; - -/** - * Resets all of a spy's the tracking variables so that it can be used again. - * - * @example - * spyOn(foo, 'bar'); - * - * foo.bar(); - * - * expect(foo.bar.callCount).toEqual(1); - * - * foo.bar.reset(); - * - * expect(foo.bar.callCount).toEqual(0); - */ -jasmine.Spy.prototype.reset = function() { - this.wasCalled = false; - this.callCount = 0; - this.argsForCall = []; - this.calls = []; - this.mostRecentCall = {}; -}; - -jasmine.createSpy = function(name) { - - var spyObj = function() { - spyObj.wasCalled = true; - spyObj.callCount++; - var args = jasmine.util.argsToArray(arguments); - spyObj.mostRecentCall.object = this; - spyObj.mostRecentCall.args = args; - spyObj.argsForCall.push(args); - spyObj.calls.push({object: this, args: args}); - return spyObj.plan.apply(this, arguments); - }; - - var spy = new jasmine.Spy(name); - - for (var prop in spy) { - spyObj[prop] = spy[prop]; - } - - spyObj.reset(); - - return spyObj; -}; - -/** - * Determines whether an object is a spy. - * - * @param {jasmine.Spy|Object} putativeSpy - * @returns {Boolean} - */ -jasmine.isSpy = function(putativeSpy) { - return putativeSpy && putativeSpy.isSpy; -}; - -/** - * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something - * large in one call. - * - * @param {String} baseName name of spy class - * @param {Array} methodNames array of names of methods to make spies - */ -jasmine.createSpyObj = function(baseName, methodNames) { - if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { - throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); - } - var obj = {}; - for (var i = 0; i < methodNames.length; i++) { - obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); - } - return obj; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the current spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.log = function() { - var spec = jasmine.getEnv().currentSpec; - spec.log.apply(spec, arguments); -}; - -/** - * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. - * - * @example - * // spy example - * var foo = { - * not: function(bool) { return !bool; } - * } - * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops - * - * @see jasmine.createSpy - * @param obj - * @param methodName - * @returns a Jasmine spy that can be chained with all spy methods - */ -var spyOn = function(obj, methodName) { - return jasmine.getEnv().currentSpec.spyOn(obj, methodName); -}; -if (isCommonJS) exports.spyOn = spyOn; - -/** - * Creates a Jasmine spec that will be added to the current suite. - * - * // TODO: pending tests - * - * @example - * it('should be true', function() { - * expect(true).toEqual(true); - * }); - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var it = function(desc, func) { - return jasmine.getEnv().it(desc, func); -}; -if (isCommonJS) exports.it = it; - -/** - * Creates a disabled Jasmine spec. - * - * A convenience method that allows existing specs to be disabled temporarily during development. - * - * @param {String} desc description of this specification - * @param {Function} func defines the preconditions and expectations of the spec - */ -var xit = function(desc, func) { - return jasmine.getEnv().xit(desc, func); -}; -if (isCommonJS) exports.xit = xit; - -/** - * Starts a chain for a Jasmine expectation. - * - * It is passed an Object that is the actual value and should chain to one of the many - * jasmine.Matchers functions. - * - * @param {Object} actual Actual value to test against and expected value - */ -var expect = function(actual) { - return jasmine.getEnv().currentSpec.expect(actual); -}; -if (isCommonJS) exports.expect = expect; - -/** - * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. - * - * @param {Function} func Function that defines part of a jasmine spec. - */ -var runs = function(func) { - jasmine.getEnv().currentSpec.runs(func); -}; -if (isCommonJS) exports.runs = runs; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -var waits = function(timeout) { - jasmine.getEnv().currentSpec.waits(timeout); -}; -if (isCommonJS) exports.waits = waits; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); -}; -if (isCommonJS) exports.waitsFor = waitsFor; - -/** - * A function that is called before each spec in a suite. - * - * Used for spec setup, including validating assumptions. - * - * @param {Function} beforeEachFunction - */ -var beforeEach = function(beforeEachFunction) { - jasmine.getEnv().beforeEach(beforeEachFunction); -}; -if (isCommonJS) exports.beforeEach = beforeEach; - -/** - * A function that is called after each spec in a suite. - * - * Used for restoring any state that is hijacked during spec execution. - * - * @param {Function} afterEachFunction - */ -var afterEach = function(afterEachFunction) { - jasmine.getEnv().afterEach(afterEachFunction); -}; -if (isCommonJS) exports.afterEach = afterEach; - -/** - * Defines a suite of specifications. - * - * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared - * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization - * of setup in some tests. - * - * @example - * // TODO: a simple suite - * - * // TODO: a simple suite with a nested describe block - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var describe = function(description, specDefinitions) { - return jasmine.getEnv().describe(description, specDefinitions); -}; -if (isCommonJS) exports.describe = describe; - -/** - * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. - * - * @param {String} description A string, usually the class under test. - * @param {Function} specDefinitions function that defines several specs. - */ -var xdescribe = function(description, specDefinitions) { - return jasmine.getEnv().xdescribe(description, specDefinitions); -}; -if (isCommonJS) exports.xdescribe = xdescribe; - - -// Provide the XMLHttpRequest class for IE 5.x-6.x: -jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { - function tryIt(f) { - try { - return f(); - } catch(e) { - } - return null; - } - - var xhr = tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.6.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP.3.0"); - }) || - tryIt(function() { - return new ActiveXObject("Msxml2.XMLHTTP"); - }) || - tryIt(function() { - return new ActiveXObject("Microsoft.XMLHTTP"); - }); - - if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); - - return xhr; -} : XMLHttpRequest; -/** - * @namespace - */ -jasmine.util = {}; - -/** - * Declare that a child class inherit it's prototype from the parent class. - * - * @private - * @param {Function} childClass - * @param {Function} parentClass - */ -jasmine.util.inherit = function(childClass, parentClass) { - /** - * @private - */ - var subclass = function() { - }; - subclass.prototype = parentClass.prototype; - childClass.prototype = new subclass(); -}; - -jasmine.util.formatException = function(e) { - var lineNumber; - if (e.line) { - lineNumber = e.line; - } - else if (e.lineNumber) { - lineNumber = e.lineNumber; - } - - var file; - - if (e.sourceURL) { - file = e.sourceURL; - } - else if (e.fileName) { - file = e.fileName; - } - - var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); - - if (file && lineNumber) { - message += ' in ' + file + ' (line ' + lineNumber + ')'; - } - - return message; -}; - -jasmine.util.htmlEscape = function(str) { - if (!str) return str; - return str.replace(/&/g, '&') - .replace(//g, '>'); -}; - -jasmine.util.argsToArray = function(args) { - var arrayOfArgs = []; - for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); - return arrayOfArgs; -}; - -jasmine.util.extend = function(destination, source) { - for (var property in source) destination[property] = source[property]; - return destination; -}; - -/** - * Environment for Jasmine - * - * @constructor - */ -jasmine.Env = function() { - this.currentSpec = null; - this.currentSuite = null; - this.currentRunner_ = new jasmine.Runner(this); - - this.reporter = new jasmine.MultiReporter(); - - this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; - this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; - this.lastUpdate = 0; - this.specFilter = function() { - return true; - }; - - this.nextSpecId_ = 0; - this.nextSuiteId_ = 0; - this.equalityTesters_ = []; - - // wrap matchers - this.matchersClass = function() { - jasmine.Matchers.apply(this, arguments); - }; - jasmine.util.inherit(this.matchersClass, jasmine.Matchers); - - jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); -}; - - -jasmine.Env.prototype.setTimeout = jasmine.setTimeout; -jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; -jasmine.Env.prototype.setInterval = jasmine.setInterval; -jasmine.Env.prototype.clearInterval = jasmine.clearInterval; - -/** - * @returns an object containing jasmine version build info, if set. - */ -jasmine.Env.prototype.version = function () { - if (jasmine.version_) { - return jasmine.version_; - } else { - throw new Error('Version not set'); - } -}; - -/** - * @returns string containing jasmine version build info, if set. - */ -jasmine.Env.prototype.versionString = function() { - if (!jasmine.version_) { - return "version unknown"; - } - - var version = this.version(); - var versionString = version.major + "." + version.minor + "." + version.build; - if (version.release_candidate) { - versionString += ".rc" + version.release_candidate; - } - versionString += " revision " + version.revision; - return versionString; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSpecId = function () { - return this.nextSpecId_++; -}; - -/** - * @returns a sequential integer starting at 0 - */ -jasmine.Env.prototype.nextSuiteId = function () { - return this.nextSuiteId_++; -}; - -/** - * Register a reporter to receive status updates from Jasmine. - * @param {jasmine.Reporter} reporter An object which will receive status updates. - */ -jasmine.Env.prototype.addReporter = function(reporter) { - this.reporter.addReporter(reporter); -}; - -jasmine.Env.prototype.execute = function() { - this.currentRunner_.execute(); -}; - -jasmine.Env.prototype.describe = function(description, specDefinitions) { - var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); - - var parentSuite = this.currentSuite; - if (parentSuite) { - parentSuite.add(suite); - } else { - this.currentRunner_.add(suite); - } - - this.currentSuite = suite; - - var declarationError = null; - try { - specDefinitions.call(suite); - } catch(e) { - declarationError = e; - } - - if (declarationError) { - this.it("encountered a declaration exception", function() { - throw declarationError; - }); - } - - this.currentSuite = parentSuite; - - return suite; -}; - -jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { - if (this.currentSuite) { - this.currentSuite.beforeEach(beforeEachFunction); - } else { - this.currentRunner_.beforeEach(beforeEachFunction); - } -}; - -jasmine.Env.prototype.currentRunner = function () { - return this.currentRunner_; -}; - -jasmine.Env.prototype.afterEach = function(afterEachFunction) { - if (this.currentSuite) { - this.currentSuite.afterEach(afterEachFunction); - } else { - this.currentRunner_.afterEach(afterEachFunction); - } - -}; - -jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { - return { - execute: function() { - } - }; -}; - -jasmine.Env.prototype.it = function(description, func) { - var spec = new jasmine.Spec(this, this.currentSuite, description); - this.currentSuite.add(spec); - this.currentSpec = spec; - - if (func) { - spec.runs(func); - } - - return spec; -}; - -jasmine.Env.prototype.xit = function(desc, func) { - return { - id: this.nextSpecId(), - runs: function() { - } - }; -}; - -jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { - if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { - return true; - } - - a.__Jasmine_been_here_before__ = b; - b.__Jasmine_been_here_before__ = a; - - var hasKey = function(obj, keyName) { - return obj !== null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in b) { - if (!hasKey(a, property) && hasKey(b, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - } - for (property in a) { - if (!hasKey(b, property) && hasKey(a, property)) { - mismatchKeys.push("expected missing key '" + property + "', but present in actual."); - } - } - for (property in b) { - if (property == '__Jasmine_been_here_before__') continue; - if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); - } - } - - if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { - mismatchValues.push("arrays were not the same length"); - } - - delete a.__Jasmine_been_here_before__; - delete b.__Jasmine_been_here_before__; - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - for (var i = 0; i < this.equalityTesters_.length; i++) { - var equalityTester = this.equalityTesters_[i]; - var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); - if (result !== jasmine.undefined) return result; - } - - if (a === b) return true; - - if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { - return (a == jasmine.undefined && b == jasmine.undefined); - } - - if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { - return a === b; - } - - if (a instanceof Date && b instanceof Date) { - return a.getTime() == b.getTime(); - } - - if (a.jasmineMatches) { - return a.jasmineMatches(b); - } - - if (b.jasmineMatches) { - return b.jasmineMatches(a); - } - - if (a instanceof jasmine.Matchers.ObjectContaining) { - return a.matches(b); - } - - if (b instanceof jasmine.Matchers.ObjectContaining) { - return b.matches(a); - } - - if (jasmine.isString_(a) && jasmine.isString_(b)) { - return (a == b); - } - - if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { - return (a == b); - } - - if (typeof a === "object" && typeof b === "object") { - return this.compareObjects_(a, b, mismatchKeys, mismatchValues); - } - - //Straight check - return (a === b); -}; - -jasmine.Env.prototype.contains_ = function(haystack, needle) { - if (jasmine.isArray_(haystack)) { - for (var i = 0; i < haystack.length; i++) { - if (this.equals_(haystack[i], needle)) return true; - } - return false; - } - return haystack.indexOf(needle) >= 0; -}; - -jasmine.Env.prototype.addEqualityTester = function(equalityTester) { - this.equalityTesters_.push(equalityTester); -}; -/** No-op base class for Jasmine reporters. - * - * @constructor - */ -jasmine.Reporter = function() { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportRunnerResults = function(runner) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecStarting = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.reportSpecResults = function(spec) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.Reporter.prototype.log = function(str) { -}; - -/** - * Blocks are functions with executable code that make up a spec. - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {jasmine.Spec} spec - */ -jasmine.Block = function(env, func, spec) { - this.env = env; - this.func = func; - this.spec = spec; -}; - -jasmine.Block.prototype.execute = function(onComplete) { - try { - this.func.apply(this.spec); - } catch (e) { - this.spec.fail(e); - } - onComplete(); -}; -/** JavaScript API reporter. - * - * @constructor - */ -jasmine.JsApiReporter = function() { - this.started = false; - this.finished = false; - this.suites_ = []; - this.results_ = {}; -}; - -jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { - this.started = true; - var suites = runner.topLevelSuites(); - for (var i = 0; i < suites.length; i++) { - var suite = suites[i]; - this.suites_.push(this.summarize_(suite)); - } -}; - -jasmine.JsApiReporter.prototype.suites = function() { - return this.suites_; -}; - -jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { - var isSuite = suiteOrSpec instanceof jasmine.Suite; - var summary = { - id: suiteOrSpec.id, - name: suiteOrSpec.description, - type: isSuite ? 'suite' : 'spec', - children: [] - }; - - if (isSuite) { - var children = suiteOrSpec.children(); - for (var i = 0; i < children.length; i++) { - summary.children.push(this.summarize_(children[i])); - } - } - return summary; -}; - -jasmine.JsApiReporter.prototype.results = function() { - return this.results_; -}; - -jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { - return this.results_[specId]; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { - this.finished = true; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { - this.results_[spec.id] = { - messages: spec.results().getItems(), - result: spec.results().failedCount > 0 ? "failed" : "passed" - }; -}; - -//noinspection JSUnusedLocalSymbols -jasmine.JsApiReporter.prototype.log = function(str) { -}; - -jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ - var results = {}; - for (var i = 0; i < specIds.length; i++) { - var specId = specIds[i]; - results[specId] = this.summarizeResult_(this.results_[specId]); - } - return results; -}; - -jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ - var summaryMessages = []; - var messagesLength = result.messages.length; - for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { - var resultMessage = result.messages[messageIndex]; - summaryMessages.push({ - text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, - passed: resultMessage.passed ? resultMessage.passed() : true, - type: resultMessage.type, - message: resultMessage.message, - trace: { - stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined - } - }); - } - - return { - result : result.result, - messages : summaryMessages - }; -}; - -/** - * @constructor - * @param {jasmine.Env} env - * @param actual - * @param {jasmine.Spec} spec - */ -jasmine.Matchers = function(env, actual, spec, opt_isNot) { - this.env = env; - this.actual = actual; - this.spec = spec; - this.isNot = opt_isNot || false; - this.reportWasCalled_ = false; -}; - -// todo: @deprecated as of Jasmine 0.11, remove soon [xw] -jasmine.Matchers.pp = function(str) { - throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); -}; - -// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] -jasmine.Matchers.prototype.report = function(result, failing_message, details) { - throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); -}; - -jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { - for (var methodName in prototype) { - if (methodName == 'report') continue; - var orig = prototype[methodName]; - matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); - } -}; - -jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { - return function() { - var matcherArgs = jasmine.util.argsToArray(arguments); - var result = matcherFunction.apply(this, arguments); - - if (this.isNot) { - result = !result; - } - - if (this.reportWasCalled_) return result; - - var message; - if (!result) { - if (this.message) { - message = this.message.apply(this, arguments); - if (jasmine.isArray_(message)) { - message = message[this.isNot ? 1 : 0]; - } - } else { - var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; - if (matcherArgs.length > 0) { - for (var i = 0; i < matcherArgs.length; i++) { - if (i > 0) message += ","; - message += " " + jasmine.pp(matcherArgs[i]); - } - } - message += "."; - } - } - var expectationResult = new jasmine.ExpectationResult({ - matcherName: matcherName, - passed: result, - expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], - actual: this.actual, - message: message - }); - this.spec.addMatcherResult(expectationResult); - return jasmine.undefined; - }; -}; - - - - -/** - * toBe: compares the actual to the expected using === - * @param expected - */ -jasmine.Matchers.prototype.toBe = function(expected) { - return this.actual === expected; -}; - -/** - * toNotBe: compares the actual to the expected using !== - * @param expected - * @deprecated as of 1.0. Use not.toBe() instead. - */ -jasmine.Matchers.prototype.toNotBe = function(expected) { - return this.actual !== expected; -}; - -/** - * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. - * - * @param expected - */ -jasmine.Matchers.prototype.toEqual = function(expected) { - return this.env.equals_(this.actual, expected); -}; - -/** - * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual - * @param expected - * @deprecated as of 1.0. Use not.toEqual() instead. - */ -jasmine.Matchers.prototype.toNotEqual = function(expected) { - return !this.env.equals_(this.actual, expected); -}; - -/** - * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes - * a pattern or a String. - * - * @param expected - */ -jasmine.Matchers.prototype.toMatch = function(expected) { - return new RegExp(expected).test(this.actual); -}; - -/** - * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch - * @param expected - * @deprecated as of 1.0. Use not.toMatch() instead. - */ -jasmine.Matchers.prototype.toNotMatch = function(expected) { - return !(new RegExp(expected).test(this.actual)); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeDefined = function() { - return (this.actual !== jasmine.undefined); -}; - -/** - * Matcher that compares the actual to jasmine.undefined. - */ -jasmine.Matchers.prototype.toBeUndefined = function() { - return (this.actual === jasmine.undefined); -}; - -/** - * Matcher that compares the actual to null. - */ -jasmine.Matchers.prototype.toBeNull = function() { - return (this.actual === null); -}; - -/** - * Matcher that boolean not-nots the actual. - */ -jasmine.Matchers.prototype.toBeTruthy = function() { - return !!this.actual; -}; - - -/** - * Matcher that boolean nots the actual. - */ -jasmine.Matchers.prototype.toBeFalsy = function() { - return !this.actual; -}; - - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called. - */ -jasmine.Matchers.prototype.toHaveBeenCalled = function() { - if (arguments.length > 0) { - throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to have been called.", - "Expected spy " + this.actual.identity + " not to have been called." - ]; - }; - - return this.actual.wasCalled; -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ -jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was not called. - * - * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead - */ -jasmine.Matchers.prototype.wasNotCalled = function() { - if (arguments.length > 0) { - throw new Error('wasNotCalled does not take arguments'); - } - - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy " + this.actual.identity + " to not have been called.", - "Expected spy " + this.actual.identity + " to have been called." - ]; - }; - - return !this.actual.wasCalled; -}; - -/** - * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. - * - * @example - * - */ -jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - this.message = function() { - if (this.actual.callCount === 0) { - // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw] - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.", - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was." - ]; - } else { - return [ - "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall), - "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall) - ]; - } - }; - - return this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; - -/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ -jasmine.Matchers.prototype.wasNotCalledWith = function() { - var expectedArgs = jasmine.util.argsToArray(arguments); - if (!jasmine.isSpy(this.actual)) { - throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); - } - - this.message = function() { - return [ - "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", - "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" - ]; - }; - - return !this.env.contains_(this.actual.argsForCall, expectedArgs); -}; - -/** - * Matcher that checks that the expected item is an element in the actual Array. - * - * @param {Object} expected - */ -jasmine.Matchers.prototype.toContain = function(expected) { - return this.env.contains_(this.actual, expected); -}; - -/** - * Matcher that checks that the expected item is NOT an element in the actual Array. - * - * @param {Object} expected - * @deprecated as of 1.0. Use not.toContain() instead. - */ -jasmine.Matchers.prototype.toNotContain = function(expected) { - return !this.env.contains_(this.actual, expected); -}; - -jasmine.Matchers.prototype.toBeLessThan = function(expected) { - return this.actual < expected; -}; - -jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { - return this.actual > expected; -}; - -/** - * Matcher that checks that the expected item is equal to the actual item - * up to a given level of decimal precision (default 2). - * - * @param {Number} expected - * @param {Number} precision - */ -jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { - if (!(precision === 0)) { - precision = precision || 2; - } - var multiplier = Math.pow(10, precision); - var actual = Math.round(this.actual * multiplier); - expected = Math.round(expected * multiplier); - return expected == actual; -}; - -/** - * Matcher that checks that the expected exception was thrown by the actual. - * - * @param {String} expected - */ -jasmine.Matchers.prototype.toThrow = function(expected) { - var result = false; - var exception; - if (typeof this.actual != 'function') { - throw new Error('Actual is not a function'); - } - try { - this.actual(); - } catch (e) { - exception = e; - } - if (exception) { - result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); - } - - var not = this.isNot ? "not " : ""; - - this.message = function() { - if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { - return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); - } else { - return "Expected function to throw an exception."; - } - }; - - return result; -}; - -jasmine.Matchers.Any = function(expectedClass) { - this.expectedClass = expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { - if (this.expectedClass == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedClass == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedClass == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedClass == Object) { - return typeof other == 'object'; - } - - return other instanceof this.expectedClass; -}; - -jasmine.Matchers.Any.prototype.jasmineToString = function() { - return ''; -}; - -jasmine.Matchers.ObjectContaining = function (sample) { - this.sample = sample; -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - var env = jasmine.getEnv(); - - var hasKey = function(obj, keyName) { - return obj != null && obj[keyName] !== jasmine.undefined; - }; - - for (var property in this.sample) { - if (!hasKey(other, property) && hasKey(this.sample, property)) { - mismatchKeys.push("expected has key '" + property + "', but missing from actual."); - } - else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { - mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); - } - } - - return (mismatchKeys.length === 0 && mismatchValues.length === 0); -}; - -jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { - return ""; -}; -// Mock setTimeout, clearTimeout -// Contributed by Pivotal Computer Systems, www.pivotalsf.com - -jasmine.FakeTimer = function() { - this.reset(); - - var self = this; - self.setTimeout = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); - return self.timeoutsMade; - }; - - self.setInterval = function(funcToCall, millis) { - self.timeoutsMade++; - self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); - return self.timeoutsMade; - }; - - self.clearTimeout = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - - self.clearInterval = function(timeoutKey) { - self.scheduledFunctions[timeoutKey] = jasmine.undefined; - }; - -}; - -jasmine.FakeTimer.prototype.reset = function() { - this.timeoutsMade = 0; - this.scheduledFunctions = {}; - this.nowMillis = 0; -}; - -jasmine.FakeTimer.prototype.tick = function(millis) { - var oldMillis = this.nowMillis; - var newMillis = oldMillis + millis; - this.runFunctionsWithinRange(oldMillis, newMillis); - this.nowMillis = newMillis; -}; - -jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { - var scheduledFunc; - var funcsToRun = []; - for (var timeoutKey in this.scheduledFunctions) { - scheduledFunc = this.scheduledFunctions[timeoutKey]; - if (scheduledFunc != jasmine.undefined && - scheduledFunc.runAtMillis >= oldMillis && - scheduledFunc.runAtMillis <= nowMillis) { - funcsToRun.push(scheduledFunc); - this.scheduledFunctions[timeoutKey] = jasmine.undefined; - } - } - - if (funcsToRun.length > 0) { - funcsToRun.sort(function(a, b) { - return a.runAtMillis - b.runAtMillis; - }); - for (var i = 0; i < funcsToRun.length; ++i) { - try { - var funcToRun = funcsToRun[i]; - this.nowMillis = funcToRun.runAtMillis; - funcToRun.funcToCall(); - if (funcToRun.recurring) { - this.scheduleFunction(funcToRun.timeoutKey, - funcToRun.funcToCall, - funcToRun.millis, - true); - } - } catch(e) { - } - } - this.runFunctionsWithinRange(oldMillis, nowMillis); - } -}; - -jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { - this.scheduledFunctions[timeoutKey] = { - runAtMillis: this.nowMillis + millis, - funcToCall: funcToCall, - recurring: recurring, - timeoutKey: timeoutKey, - millis: millis - }; -}; - -/** - * @namespace - */ -jasmine.Clock = { - defaultFakeTimer: new jasmine.FakeTimer(), - - reset: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.reset(); - }, - - tick: function(millis) { - jasmine.Clock.assertInstalled(); - jasmine.Clock.defaultFakeTimer.tick(millis); - }, - - runFunctionsWithinRange: function(oldMillis, nowMillis) { - jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); - }, - - scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { - jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); - }, - - useMock: function() { - if (!jasmine.Clock.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Clock.uninstallMock); - - jasmine.Clock.installMock(); - } - }, - - installMock: function() { - jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; - }, - - uninstallMock: function() { - jasmine.Clock.assertInstalled(); - jasmine.Clock.installed = jasmine.Clock.real; - }, - - real: { - setTimeout: jasmine.getGlobal().setTimeout, - clearTimeout: jasmine.getGlobal().clearTimeout, - setInterval: jasmine.getGlobal().setInterval, - clearInterval: jasmine.getGlobal().clearInterval - }, - - assertInstalled: function() { - if (!jasmine.Clock.isInstalled()) { - throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); - } - }, - - isInstalled: function() { - return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; - }, - - installed: null -}; -jasmine.Clock.installed = jasmine.Clock.real; - -//else for IE support -jasmine.getGlobal().setTimeout = function(funcToCall, millis) { - if (jasmine.Clock.installed.setTimeout.apply) { - return jasmine.Clock.installed.setTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.setTimeout(funcToCall, millis); - } -}; - -jasmine.getGlobal().setInterval = function(funcToCall, millis) { - if (jasmine.Clock.installed.setInterval.apply) { - return jasmine.Clock.installed.setInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.setInterval(funcToCall, millis); - } -}; - -jasmine.getGlobal().clearTimeout = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearTimeout.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearTimeout(timeoutKey); - } -}; - -jasmine.getGlobal().clearInterval = function(timeoutKey) { - if (jasmine.Clock.installed.clearTimeout.apply) { - return jasmine.Clock.installed.clearInterval.apply(this, arguments); - } else { - return jasmine.Clock.installed.clearInterval(timeoutKey); - } -}; - -/** - * @constructor - */ -jasmine.MultiReporter = function() { - this.subReporters_ = []; -}; -jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); - -jasmine.MultiReporter.prototype.addReporter = function(reporter) { - this.subReporters_.push(reporter); -}; - -(function() { - var functionNames = [ - "reportRunnerStarting", - "reportRunnerResults", - "reportSuiteResults", - "reportSpecStarting", - "reportSpecResults", - "log" - ]; - for (var i = 0; i < functionNames.length; i++) { - var functionName = functionNames[i]; - jasmine.MultiReporter.prototype[functionName] = (function(functionName) { - return function() { - for (var j = 0; j < this.subReporters_.length; j++) { - var subReporter = this.subReporters_[j]; - if (subReporter[functionName]) { - subReporter[functionName].apply(subReporter, arguments); - } - } - }; - })(functionName); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Adds a log message. - * @param values Array of message parts which will be concatenated later. - */ -jasmine.NestedResults.prototype.log = function(values) { - this.items_.push(new jasmine.MessageResult(values)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'log') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed()) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; -/** - * Base class for pretty printing for expectation results. - */ -jasmine.PrettyPrinter = function() { - this.ppNestLevel_ = 0; -}; - -/** - * Formats a value in a nice, human-readable string. - * - * @param value - */ -jasmine.PrettyPrinter.prototype.format = function(value) { - if (this.ppNestLevel_ > 40) { - throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); - } - - this.ppNestLevel_++; - try { - if (value === jasmine.undefined) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === jasmine.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString()); - } else if (typeof value === 'string') { - this.emitString(value); - } else if (jasmine.isSpy(value)) { - this.emitScalar("spy on " + value.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (typeof value.nodeType === 'number') { - this.emitScalar('HTMLNode'); - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { - this.emitScalar(''); - } else if (jasmine.isArray_(value) || typeof value == 'object') { - value.__Jasmine_been_here_before__ = true; - if (jasmine.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - delete value.__Jasmine_been_here_before__; - } else { - this.emitScalar(value.toString()); - } - } finally { - this.ppNestLevel_--; - } -}; - -jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { - for (var property in obj) { - if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && - obj.__lookupGetter__(property) !== null) : false); - } -}; - -jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; - -jasmine.StringPrettyPrinter = function() { - jasmine.PrettyPrinter.call(this); - - this.string = ''; -}; -jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); - -jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); -}; - -jasmine.StringPrettyPrinter.prototype.emitString = function(value) { - this.append("'" + value + "'"); -}; - -jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { - this.append('[ '); - for (var i = 0; i < array.length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - this.append(' ]'); -}; - -jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { - var self = this; - this.append('{ '); - var first = true; - - this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.append(property); - self.append(' : '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } - }); - - this.append(' }'); -}; - -jasmine.StringPrettyPrinter.prototype.append = function(value) { - this.string += value; -}; -jasmine.Queue = function(env) { - this.env = env; - this.blocks = []; - this.running = false; - this.index = 0; - this.offset = 0; - this.abort = false; -}; - -jasmine.Queue.prototype.addBefore = function(block) { - this.blocks.unshift(block); -}; - -jasmine.Queue.prototype.add = function(block) { - this.blocks.push(block); -}; - -jasmine.Queue.prototype.insertNext = function(block) { - this.blocks.splice((this.index + this.offset + 1), 0, block); - this.offset++; -}; - -jasmine.Queue.prototype.start = function(onComplete) { - this.running = true; - this.onComplete = onComplete; - this.next_(); -}; - -jasmine.Queue.prototype.isRunning = function() { - return this.running; -}; - -jasmine.Queue.LOOP_DONT_RECURSE = true; - -jasmine.Queue.prototype.next_ = function() { - var self = this; - var goAgain = true; - - while (goAgain) { - goAgain = false; - - if (self.index < self.blocks.length && !this.abort) { - var calledSynchronously = true; - var completedSynchronously = false; - - var onComplete = function () { - if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { - completedSynchronously = true; - return; - } - - if (self.blocks[self.index].abort) { - self.abort = true; - } - - self.offset = 0; - self.index++; - - var now = new Date().getTime(); - if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { - self.env.lastUpdate = now; - self.env.setTimeout(function() { - self.next_(); - }, 0); - } else { - if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { - goAgain = true; - } else { - self.next_(); - } - } - }; - self.blocks[self.index].execute(onComplete); - - calledSynchronously = false; - if (completedSynchronously) { - onComplete(); - } - - } else { - self.running = false; - if (self.onComplete) { - self.onComplete(); - } - } - } -}; - -jasmine.Queue.prototype.results = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.blocks.length; i++) { - if (this.blocks[i].results) { - results.addResult(this.blocks[i].results()); - } - } - return results; -}; - - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - var self = this; - self.env = env; - self.queue = new jasmine.Queue(env); - self.before_ = []; - self.after_ = []; - self.suites_ = []; -}; - -jasmine.Runner.prototype.execute = function() { - var self = this; - if (self.env.reporter.reportRunnerStarting) { - self.env.reporter.reportRunnerStarting(this); - } - self.queue.start(function () { - self.finishCallback(); - }); -}; - -jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.splice(0,0,beforeEachFunction); -}; - -jasmine.Runner.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.splice(0,0,afterEachFunction); -}; - - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.addSuite = function(suite) { - this.suites_.push(suite); -}; - -jasmine.Runner.prototype.add = function(block) { - if (block instanceof jasmine.Suite) { - this.addSuite(block); - } - this.queue.add(block); -}; - -jasmine.Runner.prototype.specs = function () { - var suites = this.suites(); - var specs = []; - for (var i = 0; i < suites.length; i++) { - specs = specs.concat(suites[i].specs()); - } - return specs; -}; - -jasmine.Runner.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Runner.prototype.topLevelSuites = function() { - var topLevelSuites = []; - for (var i = 0; i < this.suites_.length; i++) { - if (!this.suites_[i].parentSuite) { - topLevelSuites.push(this.suites_[i]); - } - } - return topLevelSuites; -}; - -jasmine.Runner.prototype.results = function() { - return this.queue.results(); -}; -/** - * Internal representation of a Jasmine specification, or test. - * - * @constructor - * @param {jasmine.Env} env - * @param {jasmine.Suite} suite - * @param {String} description - */ -jasmine.Spec = function(env, suite, description) { - if (!env) { - throw new Error('jasmine.Env() required'); - } - if (!suite) { - throw new Error('jasmine.Suite() required'); - } - var spec = this; - spec.id = env.nextSpecId ? env.nextSpecId() : null; - spec.env = env; - spec.suite = suite; - spec.description = description; - spec.queue = new jasmine.Queue(env); - - spec.afterCallbacks = []; - spec.spies_ = []; - - spec.results_ = new jasmine.NestedResults(); - spec.results_.description = description; - spec.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - - -jasmine.Spec.prototype.results = function() { - return this.results_; -}; - -/** - * All parameters are pretty-printed and concatenated together, then written to the spec's output. - * - * Be careful not to leave calls to jasmine.log in production code. - */ -jasmine.Spec.prototype.log = function() { - return this.results_.log(arguments); -}; - -jasmine.Spec.prototype.runs = function (func) { - var block = new jasmine.Block(this.env, func, this); - this.addToQueue(block); - return this; -}; - -jasmine.Spec.prototype.addToQueue = function (block) { - if (this.queue.isRunning()) { - this.queue.insertNext(block); - } else { - this.queue.add(block); - } -}; - -/** - * @param {jasmine.ExpectationResult} result - */ -jasmine.Spec.prototype.addMatcherResult = function(result) { - this.results_.addResult(result); -}; - -jasmine.Spec.prototype.expect = function(actual) { - var positive = new (this.getMatchersClass_())(this.env, actual, this); - positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); - return positive; -}; - -/** - * Waits a fixed time period before moving to the next block. - * - * @deprecated Use waitsFor() instead - * @param {Number} timeout milliseconds to wait - */ -jasmine.Spec.prototype.waits = function(timeout) { - var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); - this.addToQueue(waitsFunc); - return this; -}; - -/** - * Waits for the latchFunction to return true before proceeding to the next block. - * - * @param {Function} latchFunction - * @param {String} optional_timeoutMessage - * @param {Number} optional_timeout - */ -jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { - var latchFunction_ = null; - var optional_timeoutMessage_ = null; - var optional_timeout_ = null; - - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i]; - switch (typeof arg) { - case 'function': - latchFunction_ = arg; - break; - case 'string': - optional_timeoutMessage_ = arg; - break; - case 'number': - optional_timeout_ = arg; - break; - } - } - - var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); - this.addToQueue(waitsForFunc); - return this; -}; - -jasmine.Spec.prototype.fail = function (e) { - var expectationResult = new jasmine.ExpectationResult({ - passed: false, - message: e ? jasmine.util.formatException(e) : 'Exception', - trace: { stack: e.stack } - }); - this.results_.addResult(expectationResult); -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || this.env.matchersClass; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function(onComplete) { - this.removeAllSpies(); - this.finishCallback(); - if (onComplete) { - onComplete(); - } -}; - -jasmine.Spec.prototype.after = function(doAfter) { - if (this.queue.isRunning()) { - this.queue.add(new jasmine.Block(this.env, doAfter, this)); - } else { - this.afterCallbacks.unshift(doAfter); - } -}; - -jasmine.Spec.prototype.execute = function(onComplete) { - var spec = this; - if (!spec.env.specFilter(spec)) { - spec.results_.skipped = true; - spec.finish(onComplete); - return; - } - - this.env.reporter.reportSpecStarting(this); - - spec.env.currentSpec = spec; - - spec.addBeforesAndAftersToQueue(); - - spec.queue.start(function () { - spec.finish(onComplete); - }); -}; - -jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { - var runner = this.env.currentRunner(); - var i; - - for (var suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); - } - } - for (i = 0; i < runner.before_.length; i++) { - this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); - } - for (i = 0; i < this.afterCallbacks.length; i++) { - this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); - } - for (suite = this.suite; suite; suite = suite.parentSuite) { - for (i = 0; i < suite.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); - } - } - for (i = 0; i < runner.after_.length; i++) { - this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == jasmine.undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - throw new Error(methodName + ' has already been spied upon'); - } - - var spyObj = jasmine.createSpy(methodName); - - this.spies_.push(spyObj); - spyObj.baseObj = obj; - spyObj.methodName = methodName; - spyObj.originalValue = obj[methodName]; - - obj[methodName] = spyObj; - - return spyObj; -}; - -jasmine.Spec.prototype.removeAllSpies = function() { - for (var i = 0; i < this.spies_.length; i++) { - var spy = this.spies_[i]; - spy.baseObj[spy.methodName] = spy.originalValue; - } - this.spies_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { - var self = this; - self.id = env.nextSuiteId ? env.nextSuiteId() : null; - self.description = description; - self.queue = new jasmine.Queue(env); - self.parentSuite = parentSuite; - self.env = env; - self.before_ = []; - self.after_ = []; - self.children_ = []; - self.suites_ = []; - self.specs_ = []; -}; - -jasmine.Suite.prototype.getFullName = function() { - var fullName = this.description; - for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; - } - return fullName; -}; - -jasmine.Suite.prototype.finish = function(onComplete) { - this.env.reporter.reportSuiteResults(this); - this.finished = true; - if (typeof(onComplete) == 'function') { - onComplete(); - } -}; - -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.before_.unshift(beforeEachFunction); -}; - -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.after_.unshift(afterEachFunction); -}; - -jasmine.Suite.prototype.results = function() { - return this.queue.results(); -}; - -jasmine.Suite.prototype.add = function(suiteOrSpec) { - this.children_.push(suiteOrSpec); - if (suiteOrSpec instanceof jasmine.Suite) { - this.suites_.push(suiteOrSpec); - this.env.currentRunner().addSuite(suiteOrSpec); - } else { - this.specs_.push(suiteOrSpec); - } - this.queue.add(suiteOrSpec); -}; - -jasmine.Suite.prototype.specs = function() { - return this.specs_; -}; - -jasmine.Suite.prototype.suites = function() { - return this.suites_; -}; - -jasmine.Suite.prototype.children = function() { - return this.children_; -}; - -jasmine.Suite.prototype.execute = function(onComplete) { - var self = this; - this.queue.start(function () { - self.finish(onComplete); - }); -}; -jasmine.WaitsBlock = function(env, timeout, spec) { - this.timeout = timeout; - jasmine.Block.call(this, env, null, spec); -}; - -jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); - -jasmine.WaitsBlock.prototype.execute = function (onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); - } - this.env.setTimeout(function () { - onComplete(); - }, this.timeout); -}; -/** - * A block which waits for some condition to become true, with timeout. - * - * @constructor - * @extends jasmine.Block - * @param {jasmine.Env} env The Jasmine environment. - * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. - * @param {Function} latchFunction A function which returns true when the desired condition has been met. - * @param {String} message The message to display if the desired condition hasn't been met within the given time period. - * @param {jasmine.Spec} spec The Jasmine spec. - */ -jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { - this.timeout = timeout || env.defaultTimeoutInterval; - this.latchFunction = latchFunction; - this.message = message; - this.totalTimeSpentWaitingForLatch = 0; - jasmine.Block.call(this, env, null, spec); -}; -jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); - -jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; - -jasmine.WaitsForBlock.prototype.execute = function(onComplete) { - if (jasmine.VERBOSE) { - this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); - } - var latchFunctionResult; - try { - latchFunctionResult = this.latchFunction.apply(this.spec); - } catch (e) { - this.spec.fail(e); - onComplete(); - return; - } - - if (latchFunctionResult) { - onComplete(); - } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { - var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); - this.spec.fail({ - name: 'timeout', - message: message - }); - - this.abort = true; - onComplete(); - } else { - this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; - var self = this; - this.env.setTimeout(function() { - self.execute(onComplete); - }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); - } -}; - -jasmine.version_= { - "major": 1, - "minor": 2, - "build": 0, - "revision": 1337005947 -}; diff --git a/src/utils/display-styler.js b/src/utils/display-styler.js new file mode 100644 index 0000000..4599127 --- /dev/null +++ b/src/utils/display-styler.js @@ -0,0 +1,25 @@ + +/** + * Toggle element by adding .show or .hide to an HTML element + * + * @function window.bms.default.changeDisplay + * @param {array} selector - className or ID of elements + * @param {string} [display = block] - block || none + * @example + * // window.bms.default.changeDisplay(['.sideBar'], 'none') + */ + +export default (selector = [], display="block") => { + selector.forEach((val, index) => { + let el = document.querySelector(val) + if(el) { + if (display == 'block') { + el.classList.add('show') + el.classList.remove('hide') + } else { + el.classList.add('hide') + el.classList.remove('show') + } + } + }) +} \ No newline at end of file diff --git a/src/utils/dropdown-loader/index.js b/src/utils/dropdown-loader/index.js new file mode 100644 index 0000000..960730d --- /dev/null +++ b/src/utils/dropdown-loader/index.js @@ -0,0 +1,54 @@ +import style from './style' +/** + * Enable toggle on given class + * + * @function window.bms.default.dropdown + * @param {string} className + */ + +export default (className) => { + // create style + if (!document.getElementById('dropdownStyle')) { + const styl = document.createElement('style') + styl.id = 'dropdownStyle' + styl.innerHTML = style.toString() + document.body.append(styl) + } + + const targ = document.querySelectorAll(`.${className}:not(.data-bind-dropdown)`) + // window + window.bms = window.bms || {} + window.bms.default = window.bms.default || {} + window.bms.default.modal = window.bms.default.modal || {} + + // read elements + targ.forEach((el, index) => { + // mark as binded + el.classList.add('data-bind-dropdown') + + el.addEventListener('click', (e) => { + e.preventDefault() + // target ID + const targEl = el.getAttribute('data-device-dropdown') + + // get ID + // Holds the ID to be sent to the server + window.bms.default.modal.resources = el.getAttribute('data-resources') + window.bms.default.modal.element = el + + // dropdown section + let target = document.getElementById(targEl) + let a = new Promise((resolve, reject) => { + //close all open dropdpwn + document.querySelectorAll('.dropdown-section').forEach((el2, index2) => { + if (el2.classList.contains('open') && el2!=target) el2.classList.remove('open') + resolve() + }) + }).then(() => { + target.classList.toggle('open') + // prevent adding new listeners + el.classList.add('data-bind-dropdown') + }) + }) + }) +} diff --git a/src/utils/dropdown-loader/style.styl b/src/utils/dropdown-loader/style.styl new file mode 100644 index 0000000..731d25b --- /dev/null +++ b/src/utils/dropdown-loader/style.styl @@ -0,0 +1,18 @@ +.dropdown-section + position: absolute + width: 150px + height: 200px + background: #fff + z-index: 3 + right:0px + display: none + visibility:hidden + opacity: 0 + transition: all 0.3s ease-in-out + box-shadow: 0 0 7px rgba(0,0,0,.4) + + &.open + transition: opacity 0.3s ease-in-out; + visibility: visible + display: block + opacity: 1 \ No newline at end of file diff --git a/src/utils/lazy-loader.js b/src/utils/lazy-loader.js new file mode 100644 index 0000000..f698f07 --- /dev/null +++ b/src/utils/lazy-loader.js @@ -0,0 +1,44 @@ + +//inject