diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ea3c2059 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.iml +*.zip + +node_modules +jspm_packages + +.sass-cache +build + + diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 00000000..344f76ad --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,86 @@ +'use strict'; + +const BUILD_DIR = 'build'; + +const gulp = require('gulp'); + +gulp.task('clean', function (cb) { + var del = require('del'); + + del(BUILD_DIR, cb); +}); + +gulp.task('build-deploy', function (cb) { + var runSequence = require('run-sequence'); + runSequence('clean', ['app', 'pack', 'deps', 'sass'], 'deploy'); +}); + +gulp.task('build-prod', function (cb) { + var runSequence = require('run-sequence'); + runSequence('clean', ['app', 'pack', 'deps', 'sass'], 'zip'); +}); + +gulp.task('zip', function () { + var zip = require('gulp-zip'); + + return gulp.src(['./build/**']) + .pipe(zip('where-is-my-mechanism.zip')) + .pipe(gulp.dest('.')); +}); + +gulp.task('pack', function (cb) { + var Builder = require('systemjs-builder'); + var builder = new Builder({}); + + builder.loadConfig('./src/config.js') + .then(function () { + builder.config({ + paths: { + 'json': './src/*.js', + 'app': './src/*.js' + }, + meta: { + 'manifest.webapp!json': { build: false}, + 'manifest.webapp': { build: false} + } + }); + + return builder.build('app', BUILD_DIR + '/wimm.js', { minify: true, mangle: false, sourceMaps: true }) + .then(function () { + cb(); + }) + .catch(function (error) { + console.log(error); + }); + }); +}); + +gulp.task('app', function () { + return gulp.src([ + 'src/**/*.{html,png}', + 'src/config.js', + 'src/manifest.webapp' + ]) + .pipe(gulp.dest(BUILD_DIR)); +}); + +gulp.task('deps', function () { + return gulp.src(['jspm_packages/*']) + .pipe(gulp.dest([BUILD_DIR, 'jspm_packages'].join('/'))); +}); + +gulp.task('sass', function () { + var rubySass = require('gulp-ruby-sass'); + return rubySass(['./src/app.sass'], {loadPath: './src', style: 'expanded', compass: true}) + .on('error', function (err) { + console.error('Error!', err.message); + }) + .pipe(gulp.dest( + [BUILD_DIR, 'css'].join('/') + )); +}); + +gulp.task('deploy', function () { + return gulp.src(['./build/**']) + .pipe(gulp.dest('/Users/markadm/Projects/dhis/DHIS2_HOME/apps/where-is-my-mechanism')); +}); diff --git a/package.json b/package.json new file mode 100644 index 00000000..d3e2d677 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "where-is-my-mechanism", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Mark Polak", + "license": "BSD", + "jspm": { + "directories": {}, + "configFile": "src/config.js", + "dependencies": { + "angular": "npm:angular@^1.4.0", + "jquery": "github:components/jquery@^2.1.4", + "systemjs/plugin-json.git": "github:systemjs/plugin-json@^0.1.0" + }, + "devDependencies": { + "babel": "npm:babel-core@^5.1.13", + "babel-runtime": "npm:babel-runtime@^5.1.13", + "core-js": "npm:core-js@^0.9.4" + } + }, + "devDependencies": { + "del": "^1.2.0", + "gulp": "^3.8.11", + "gulp-dev": "0.0.2", + "gulp-ruby-sass": "^1.0.5", + "gulp-zip": "^3.0.2", + "systemjs-builder": "^0.10.6" + }, + "dependencies": { + "babel": "^5.4.7" + } +} diff --git a/src/app.js b/src/app.js new file mode 100644 index 00000000..af35e12c --- /dev/null +++ b/src/app.js @@ -0,0 +1,169 @@ +'use strict'; +import 'jquery'; +import angular from 'angular'; + +import manifest from 'manifest.webapp!json'; + +class WhereIsMyMechController { + constructor($http, $scope, $location) { + this.headerMap = { + Date: 'Date', + OperatingUnit: 'OU', + FiscalYear: 'FY', + PlanningReportingCycle: 'Reporting Cycle', + HQMechanismID: 'HQ ID', + LegacyMechanismID: 'Legacy ID', + ImplementingMechanismName: 'IM', + FundingAgency: 'Funding Agency', + PrimePartner: 'Prime Partner', + PrimePartnerID: 'Partner ID', + StartDate: 'Start Date', + EndDate: 'End Date', + Active: 'Active' + }; + + this.myMechanismSearchString = ''; + this.api = $http; + this.datimInfo = {}; + this.location = $location; + + $scope.$watch(() => this.myMechanismSearchString, (newVal, oldVal) => { + if (newVal !== oldVal) { + this.search(); + } + }); + + //Set the predefined search if we have one and run the search against the server + if ($location.$$search.query) { + this.myMechanismSearchString = $location.$$search.query; + this.search(); + } + } + + setSearchString(value) { + this.myMechanismSearchString = value.replace(/\"/g, '').trim(); + } + + search() { + //Reset found flags + this.foundInDatim = false; + this.foundInFactsInfo = false; + this.datimInfo = {}; + + this.location.search('query', this.myMechanismSearchString); + + this.api.get('//sync.datim.org:1777/?search=' + this.myMechanismSearchString) + .then(this.buildResultList.bind(this)) + .then(this.setResultList.bind(this)) + .then((resultList) => { + if (resultList.length > 0) { + this.foundInFactsInfo = true; + return resultList[0]; + } + return []; + }) + .then(this.loadDATIMMechanism.bind(this)); + } + + buildResultList(response) { + let lines = response.data; + let headers = lines.shift(); + + return { + headers: headers, + lines: lines + .map(line => { + line[line.length - 1] = parseInt(line[line.length - 1], 10); + return line; + }) + }; + } + + setResultList(resultList) { + if (!this.headers) { + this.headers = resultList.headers; + } + this.resultList = resultList.lines; + + return this.resultList; + } + + loadDATIMMechanism(firstRow) { + if (firstRow) { + this.datimMechanismCodeToSearch = firstRow[4]; + } else { + this.datimMechanismCodeToSearch = this.myMechanismSearchString; + } + + this.api.get('manifest.webapp') + .then(response => { + return response.data; + }) + .then(manifest => { + return manifest.activities.dhis.href; + }) + .then(apiUrl => { + return this.api.get([apiUrl, 'api', 'categoryOptions.json?filter=code:eq:' + this.datimMechanismCodeToSearch + '&fields=:all'].join('/')); + }) + .then(response => response.data.categoryOptions[0]) + .then(categoryOption => { + if (categoryOption) { + this.foundInDatim = true; + this.datimInfo.mechanism = categoryOption; + } + + if (categoryOption && categoryOption.categoryOptionGroups.length > 0) { + this.datimInfo.agency = categoryOption.categoryOptionGroups.filter(categoryOptionGroup => /^Agency_.+/.test(categoryOptionGroup.code)).reduce((current) => current); + this.datimInfo.partner = categoryOption.categoryOptionGroups.filter(categoryOptionGroup => /^Partner_.+/.test(categoryOptionGroup.code)).reduce((current) => current); + } + }); + + } +} + +function initMenu() { + //Setup dhis2 global for the menu + window.dhis2 = window.dhis2 || {}; + dhis2.settings = dhis2.settings || {}; + dhis2.settings.baseUrl = manifest.activities.dhis.href.replace(window.location.origin, '').replace(/^\//, ''); + + System.config({ + paths: { + 'commons:*': window.location.origin + '/' + dhis2.settings.baseUrl + '/dhis-web-commons/javascripts/dhis2/*.js' + } + }); + let menuFiles = [ + 'dhis2.translate', + 'dhis2.menu', + 'dhis2.menu.ui' + ]; + + let stylesheets = [ + '/dhis-web-commons/font-awesome/css/font-awesome.min.css', + '/dhis-web-commons/css/menu.css' + ]; + let headElement = angular.element(document.querySelector('head')); + stylesheets.forEach(stylesheetUrl => { + let styleSheetElement = angular.element(''); + angular.element(document.querySelector('head')) + .append(styleSheetElement); + }); + + return new Promise(function (resolve, reject) { + let result = Promise.resolve([]); + menuFiles.forEach(menuFile => { + result = result.then(function () { + return System.import('commons:' + menuFile); + }); + }); + + resolve(result); + }); +} + +angular.module('whereIsMyMech', ['d2HeaderBar']) + .controller('WhereIsMyMechController', WhereIsMyMechController); + +initMenu() + .then(() => angular.bootstrap(document.querySelector('html'), ['whereIsMyMech'])) + .catch(() => window.console.log('Failed to bootstrap the app')); diff --git a/src/app.sass b/src/app.sass new file mode 100644 index 00000000..4202a51b --- /dev/null +++ b/src/app.sass @@ -0,0 +1,110 @@ +@import 'normalize' +@import 'compass' +@import 'susy' + +.ng-hide + display: none + +.app + padding-top: 75px + width: 90% + margin: 0 auto + + +input[type=search] + font-size: 2rem + -webkit-appearance: none + -moz-appearance: none + border-radius: 2rem + border: 1px solid #CCC + color: #333 + padding: .25rem 1rem + width: 100% + outline: none + + &:focus + border-color: orange + +.found-statuses + @include span(1 of 1) + +.datim-info, +.factsinfo-info + @include container() + + h2 + font-size: 1.2rem + color: #CCC + + th, td + position: relative + padding: .2rem 0.4rem + white-space: nowrap + + .longtext-field + position: relative + max-width: 15rem + overflow: hidden + + &:after + content: '' + display: block + position: absolute + top: 0 + right: 0 + width: 2rem + bottom: 0 + + .odd :after + @include background-image(linear-gradient(left, rgba(254,254,254,0) 0%,rgba(254,254,254,1) 100%)) + + .even :after + @include background-image(linear-gradient(left, rgba(244,244,244,0) 0%,rgba(244,244,244,1) 100%)) + +.factsinfo-data-table + overflow-x: auto + + +.factsinfo-info + tr + border-bottom: 1px solid #CCC + +.datim-info-blocks + @include container() + + div + @include span(1 of 3) + + div:last-child + @include span(1 of 3 last) + + th, td + padding: .25rem + + +table + width: 100% + +th + text-align: left + font-weight: normal + color: #CCC + font-size: .8rem + +.even + background-color: #F5F5F5 + +.odd + background-color: #FEFEFE + +.fa-check + color: darkgreen + +.fa-times + color: darkred + +h2, h3 + margin: 0 + +h2 + margin-top: 1rem diff --git a/src/config.js b/src/config.js new file mode 100644 index 00000000..95249868 --- /dev/null +++ b/src/config.js @@ -0,0 +1,36 @@ +System.config({ + "baseURL": ".", + "transpiler": "babel", + "babelOptions": { + "optional": [ + "runtime" + ] + }, + "paths": { + "*": "*.js", + "github:*": "jspm_packages/github/*.js", + "npm:*": "jspm_packages/npm/*.js" + } +}); + +System.config({ + "map": { + "angular": "npm:angular@1.4.0", + "babel": "npm:babel-core@5.4.7", + "babel-runtime": "npm:babel-runtime@5.4.7", + "core-js": "npm:core-js@0.9.13", + "jquery": "github:components/jquery@2.1.4", + "github:jspm/nodelibs-process@0.1.1": { + "process": "npm:process@0.10.1" + }, + "npm:angular@1.4.0": { + "process": "github:jspm/nodelibs-process@0.1.1" + }, + "npm:core-js@0.9.13": { + "fs": "github:jspm/nodelibs-fs@0.1.2", + "process": "github:jspm/nodelibs-process@0.1.1", + "systemjs-json": "github:systemjs/plugin-json@0.1.0" + } + } +}); + diff --git a/src/config.rb b/src/config.rb new file mode 100644 index 00000000..3e4c3cec --- /dev/null +++ b/src/config.rb @@ -0,0 +1,4 @@ +require "compass-normalize" +require "compass" +require "breakpoint" +require "susy" diff --git a/src/index.html b/src/index.html new file mode 100644 index 00000000..9510d081 --- /dev/null +++ b/src/index.html @@ -0,0 +1,89 @@ + + + + + + Where is my mechanism + + + + + + + + + +
+ +
+

+ Trying to find mechanism with mechanism code: +

+ Found in DATIM + Found in FACTS Info +
+ +
+

DATIM info

+
+
+

Mechanism

+ + + + + + +
Name
Code
Country
Start
End
+
+
+

Agency

+ + + + +
Name
Code
Created
+
+
+

Partner

+ + + + +
Name
Code
Created
+
+
+
+
+

FACTS Info to DATIM synchronisation logs

+
+ + + + + + + + + + + +
+ + +
+
+
+
+ + + + + + diff --git a/src/json.js b/src/json.js new file mode 100644 index 00000000..64507bde --- /dev/null +++ b/src/json.js @@ -0,0 +1,6 @@ +/* + JSON plugin + */ +exports.translate = function(load) { + return 'module.exports = ' + load.source; +} diff --git a/src/mag.png b/src/mag.png new file mode 100644 index 00000000..0e5fdfd7 Binary files /dev/null and b/src/mag.png differ diff --git a/src/manifest.webapp b/src/manifest.webapp new file mode 100644 index 00000000..71777d32 --- /dev/null +++ b/src/manifest.webapp @@ -0,0 +1 @@ +{"version":"0.0.1","name":"Where is my mechanism?","description":"DATIM application to find a mechanism","icons":{"48":"./mag.png"},"developer":{"url":"","name":"Mark Polak, Jim Grace"},"launch_path":"index.html","default_locale":"en","activities":{"dhis":{"href":"*"}}} \ No newline at end of file