diff --git a/akvo/rest/serializers/iati_export.py b/akvo/rest/serializers/iati_export.py index 7b19eff9e9..20ee5bdcbb 100644 --- a/akvo/rest/serializers/iati_export.py +++ b/akvo/rest/serializers/iati_export.py @@ -4,14 +4,17 @@ # See more details in the license.txt file located at the root folder of the Akvo RSR module. # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >. - +from akvo.rest.serializers.rsr_serializer import BaseRSRSerializer from akvo.rsr.models import IatiActivityExport, IatiExport -from .rsr_serializer import BaseRSRSerializer +from rest_framework import serializers class IatiExportSerializer(BaseRSRSerializer): + user_name = serializers.Field(source='user.get_full_name') + status_label = serializers.Field(source='show_status') + class Meta: model = IatiExport diff --git a/akvo/rsr/static/scripts-src/my-iati.js b/akvo/rsr/static/scripts-src/my-iati.js index 6c3bdd66fa..aede84615a 100644 --- a/akvo/rsr/static/scripts-src/my-iati.js +++ b/akvo/rsr/static/scripts-src/my-iati.js @@ -7,6 +7,7 @@ var csrftoken, endpoints, + months, i18n; /* CSRF TOKEN (this should really be added in base.html, we use it everywhere) */ @@ -27,7 +28,7 @@ function getCookie(name) { csrftoken = getCookie('csrftoken'); /* Capitalize the first character of a string */ -function capitalizeFirstLetter(string) { +function cap(string) { return string.charAt(0).toUpperCase() + string.slice(1); } @@ -45,17 +46,30 @@ function apiCall(method, url, data, successCallback, retries) { var success = function(newResponse) { var oldResults = response.results; response.results = oldResults.concat(newResponse.results); - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } }; apiCall(method, response.next, data, success); } else { - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } } } else { - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } } } else { - var message = i18nResults.general_error + ': '; + // var message = i18nResults.general_error + ': '; + var message = 'general error: '; for (var key in response) { if (response.hasOwnProperty(key)) { message += response[key] + '. '; @@ -115,6 +129,47 @@ function loadAsync(url, callback, retryCount, retryLimit) { xmlHttp.send(); } +function getDateDescription(month) { + switch (month) { + case 0: + return months.january; + case 1: + return months.february; + case 2: + return months.march; + case 3: + return months.april; + case 4: + return months.may; + case 5: + return months.june; + case 6: + return months.july; + case 7: + return months.august; + case 8: + return months.september; + case 9: + return months.october; + case 10: + return months.november; + case 11: + return months.december; + } +} + +function displayDate(dateString) { + // Display a dateString like "25 Jan 2016" + if (dateString !== undefined && dateString !== null) { + var date = new Date(dateString.split(".")[0].replace("/", /-/g)); + var day = date.getUTCDate(); + var month = getDateDescription(date.getUTCMonth()); + var year = date.getUTCFullYear(); + return day + " " + month + " " + year; + } + return i18n.unknown_date; +} + function processResponse(label, response) { var label_content, checks, all_checks_passed, span, checks_response; @@ -217,20 +272,102 @@ function loadComponents() { } }); + var ExportRow = React.createClass({displayName: 'ExportRow', + openPublicFile: function() { + window.open(i18n.last_exports_url, '_blank'); + }, + + openFile: function() { + window.open(endpoints.base_url + '/media/' + this.props.exp.iati_file, '_blank'); + }, + + setPublic: function() { + this.props.setPublic(this.props.exp.id); + }, + + renderActions: function() { + if (this.props.publicFile) { + return ( + React.DOM.button( {className:"btn btn-success btn-sm", onClick:this.openPublicFile}, + React.DOM.i( {className:"fa fa-globe"} ), " ", cap(i18n.view_public_file) + ) + ); + } else if (this.props.exp.iati_file) { + return ( + React.DOM.div(null, + React.DOM.button( {className:"btn btn-default btn-sm", onClick:this.openFile}, + React.DOM.i( {className:"fa fa-code"} ), " ", cap(i18n.view_file) + ), + React.DOM.button( {className:"btn btn-default btn-sm", onClick:this.setPublic}, + React.DOM.i( {className:"fa fa-globe"} ), " ", cap(i18n.set_public) + ) + ) + ); + } else { + return ( + React.DOM.button( {className:"btn btn-default btn-sm disabled"}, + React.DOM.i( {className:"fa fa-globe"} ), " ", cap(i18n.no_iati_file) + ) + ); + } + }, + + renderRowClass: function() { + if (this.props.publicFile) { + return 'publicFile'; + } else if (this.props.exp.status === 2) { + return 'inProgress'; + } else if (this.props.exp.status === 4 || this.props.exp.iati_file === '') { + return 'cancelled'; + } else { + return ''; + } + }, + + render: function() { + return ( + React.DOM.tr( {className:this.renderRowClass()}, + React.DOM.td(null, this.props.exp.status_label), + React.DOM.td(null, this.props.exp.projects.length), + React.DOM.td(null, this.props.exp.user_name), + React.DOM.td(null, displayDate(this.props.exp.created_at)), + React.DOM.td(null, 'v' + this.props.exp.version), + React.DOM.td( {className:"text-right"}, this.renderActions()) + ) + ); + } + }); + var ExportsTable = React.createClass({displayName: 'ExportsTable', render: function() { + var thisTable = this; + + var exports = this.props.exports.results.map(function(exp) { + var publicFile = thisTable.props.publicFile === exp.id; + + return React.createElement(ExportRow, { + key: exp.id, + exp: exp, + publicFile: publicFile, + setPublic: thisTable.props.setPublic + }); + }); + + return ( - React.DOM.table( {className:"table table-striped table-responsive myProjectList"}, + React.DOM.table( {className:"table table-striped table-responsive myProjectList topMargin"}, React.DOM.thead(null, React.DOM.tr(null, - React.DOM.th(null, "Last export"), - React.DOM.th(null, "User"), - React.DOM.th(null, "Created at"), - React.DOM.th(null, "IATI version"), - React.DOM.th(null, "status"), - React.DOM.th(null, "IATI file"), - React.DOM.th(null, "Number of projects") + React.DOM.th(null, cap(i18n.status)), + React.DOM.th(null, i18n.number_of_projects), + React.DOM.th(null, cap(i18n.created_by)), + React.DOM.th(null, cap(i18n.created_at)), + React.DOM.th(null, i18n.iati_version), + React.DOM.th( {className:"text-right"}, cap(i18n.actions)) ) + ), + React.DOM.tbody(null, + exports ) ) ); @@ -242,7 +379,8 @@ function loadComponents() { return { exports: null, initializing: true, - refreshing: false + refreshing: false, + actionInProgress: false }; }, @@ -277,16 +415,122 @@ function loadComponents() { } }, + publicFile: function() { + if (this.state.exports === null) { + return null; + } else { + for (var i = 0; i < this.state.exports.results.length; i++) { + var exp = this.state.exports.results[i]; + if (exp.iati_file !== '' && exp.is_public) { + return exp.id; + } + } + return null; + } + }, + + findExport: function(exportId) { + for (var i = 0; i < this.state.exports.results.length; i++) { + var exp = this.state.exports.results[i]; + if (exp.id === exportId) { + return exp; + } + } + return null; + }, + + setPublic: function(exportId) { + // Basically what we do is to set this export to public first, and then set all + // newer exports to private. This automatically makes this export the public export. + var thisApp = this, + exportUrl = endpoints.base_url + endpoints.iati_export, + thisExport = this.findExport(exportId), + newerExports = [], + newerExportsUpdated = 0, + publicData = JSON.stringify({'is_public': true}), + privateData = JSON.stringify({'is_public': false}); + + function allExportsUpdated() { + thisApp.loadExports(false); + thisApp.setState({actionInProgress: false}); + } + + function exportUpdated(response) { + newerExportsUpdated++; + if (newerExportsUpdated === newerExports.length) { + allExportsUpdated(); + } + } + + // Set current IATI export to public + this.setState({actionInProgress: true}); + apiCall('PATCH', exportUrl.replace('{iati_export}', exportId), publicData); + + // Find the newer IATI exports + for (var i = 0; i < this.state.exports.results.length; i++) { + var newerExp = this.state.exports.results[i]; + if (newerExp.id !== exportId && newerExp.created_at > thisExport.created_at) { + newerExports.push(newerExp); + } + } + + // Update the newer IATI exports + if (newerExports.length > 0) { + for (var j = 0; j < newerExports.length; j++) { + var exp = newerExports[j]; + apiCall('PATCH', exportUrl.replace('{iati_export}', exp.id), privateData, exportUpdated); + } + } else { + allExportsUpdated(); + } + }, + render: function() { + var initOrTable, + refreshing, + exportCount, + exportCountString, + lastExportDescription; + + refreshing = this.state.refreshing ? React.DOM.span( {className:"small"}, React.DOM.i( {className:"fa fa-spin fa-spinner"} ),' ' + cap(i18n.refreshing) + ' ' + i18n.iati_exports + '...') : React.DOM.span(null ); + exportCount = !this.state.initializing ? this.state.exports.count : null; + exportCountString = (exportCount !== null && exportCount > 0) ? ' ' + this.state.exports.count + ' ' : ' '; + + if (this.state.initializing) { + // Only show a message that data is being loading when initializing + initOrTable = React.DOM.span( {className:"small"}, React.DOM.i( {className:"fa fa-spin fa-spinner"}),' ' + cap(i18n.loading) + ' ' + i18n.last + ' ' + i18n.iati_exports + '...'); + } else if (exportCount > 0) { + // Show a table of exiting imports (max 10) when the data has been loaded and exports exist + initOrTable = React.createElement(ExportsTable, { + exports: this.state.exports, + refreshing: this.state.refreshing, + publicFile: this.publicFile(), + setPublic: this.setPublic + }); + } else { + // Do not show the 'Last exports' part when no exports exist yet + return ( + React.DOM.div(null, + React.DOM.h4( {className:"topMargin"}, cap(i18n.new) + ' ' + i18n.iati_export) + ) + ); + } + + lastExportDescription = React.DOM.div( {className:"lastExportDescription"}, + React.DOM.span(null, cap(i18n.last_exports_1)), + React.DOM.a( {href:i18n.last_exports_url, target:"_blank"}, i18n.last_exports_url), + React.DOM.span(null, '. ' + cap(i18n.last_exports_2) + ' ' + cap(i18n.last_exports_3)), + React.DOM.a( {href:"http://iatiregistry.org", target:"_blank"}, i18n.iati_registry), + React.DOM.span(null, i18n.last_exports_4) + ); + return ( React.DOM.div(null, - React.DOM.h4( {className:"topMargin"}, capitalizeFirstLetter(i18n.last_ten) + ' ' + i18n.iati_exports), - React.createElement(ExportsTable, { - exports: this.state.exports, - initializing: this.state.initializing, - refreshing: this.state.refreshing - }), - React.DOM.h4( {className:"topMargin"}, capitalizeFirstLetter(i18n.new) + ' ' + i18n.iati_export) + React.DOM.h4( {className:"topMargin"}, cap(i18n.last) + exportCountString + i18n.iati_exports), + lastExportDescription, + refreshing, + initOrTable, + React.DOM.h4( {className:"topMargin"}, cap(i18n.new) + ' ' + i18n.iati_export) ) ); } @@ -329,8 +573,9 @@ function loadAndRenderReact() { } document.addEventListener('DOMContentLoaded', function() { - i18n = JSON.parse(document.getElementById("translations").innerHTML); endpoints = JSON.parse(document.getElementById("endpoints").innerHTML); + months = JSON.parse(document.getElementById("months").innerHTML); + i18n = JSON.parse(document.getElementById("translations").innerHTML); if (document.getElementById('myIATIContainer')) { if (typeof React !== 'undefined' && typeof ReactDOM !== 'undefined') { diff --git a/akvo/rsr/static/scripts-src/my-iati.jsx b/akvo/rsr/static/scripts-src/my-iati.jsx index d3d4947483..6ec817c0ac 100644 --- a/akvo/rsr/static/scripts-src/my-iati.jsx +++ b/akvo/rsr/static/scripts-src/my-iati.jsx @@ -7,6 +7,7 @@ var csrftoken, endpoints, + months, i18n; /* CSRF TOKEN (this should really be added in base.html, we use it everywhere) */ @@ -27,7 +28,7 @@ function getCookie(name) { csrftoken = getCookie('csrftoken'); /* Capitalize the first character of a string */ -function capitalizeFirstLetter(string) { +function cap(string) { return string.charAt(0).toUpperCase() + string.slice(1); } @@ -45,17 +46,30 @@ function apiCall(method, url, data, successCallback, retries) { var success = function(newResponse) { var oldResults = response.results; response.results = oldResults.concat(newResponse.results); - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } }; apiCall(method, response.next, data, success); } else { - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } } } else { - return successCallback(response); + if (successCallback !== undefined) { + return successCallback(response); + } else { + return false; + } } } else { - var message = i18nResults.general_error + ': '; + // var message = i18nResults.general_error + ': '; + var message = 'general error: '; for (var key in response) { if (response.hasOwnProperty(key)) { message += response[key] + '. '; @@ -115,6 +129,47 @@ function loadAsync(url, callback, retryCount, retryLimit) { xmlHttp.send(); } +function getDateDescription(month) { + switch (month) { + case 0: + return months.january; + case 1: + return months.february; + case 2: + return months.march; + case 3: + return months.april; + case 4: + return months.may; + case 5: + return months.june; + case 6: + return months.july; + case 7: + return months.august; + case 8: + return months.september; + case 9: + return months.october; + case 10: + return months.november; + case 11: + return months.december; + } +} + +function displayDate(dateString) { + // Display a dateString like "25 Jan 2016" + if (dateString !== undefined && dateString !== null) { + var date = new Date(dateString.split(".")[0].replace("/", /-/g)); + var day = date.getUTCDate(); + var month = getDateDescription(date.getUTCMonth()); + var year = date.getUTCFullYear(); + return day + " " + month + " " + year; + } + return i18n.unknown_date; +} + function processResponse(label, response) { var label_content, checks, all_checks_passed, span, checks_response; @@ -217,21 +272,103 @@ function loadComponents() { } }); + var ExportRow = React.createClass({ + openPublicFile: function() { + window.open(i18n.last_exports_url, '_blank'); + }, + + openFile: function() { + window.open(endpoints.base_url + '/media/' + this.props.exp.iati_file, '_blank'); + }, + + setPublic: function() { + this.props.setPublic(this.props.exp.id); + }, + + renderActions: function() { + if (this.props.publicFile) { + return ( + + ); + } else if (this.props.exp.iati_file) { + return ( +
+ + +
+ ); + } else { + return ( + + ); + } + }, + + renderRowClass: function() { + if (this.props.publicFile) { + return 'publicFile'; + } else if (this.props.exp.status === 2) { + return 'inProgress'; + } else if (this.props.exp.status === 4 || this.props.exp.iati_file === '') { + return 'cancelled'; + } else { + return ''; + } + }, + + render: function() { + return ( + + {this.props.exp.status_label} + {this.props.exp.projects.length} + {this.props.exp.user_name} + {displayDate(this.props.exp.created_at)} + {'v' + this.props.exp.version} + {this.renderActions()} + + ); + } + }); + var ExportsTable = React.createClass({ render: function() { + var thisTable = this; + + var exports = this.props.exports.results.map(function(exp) { + var publicFile = thisTable.props.publicFile === exp.id; + + return React.createElement(ExportRow, { + key: exp.id, + exp: exp, + publicFile: publicFile, + setPublic: thisTable.props.setPublic + }); + }); + + return ( - +
- - - - - - - + + + + + + + + {exports} +
Last exportUserCreated atIATI versionstatusIATI fileNumber of projects{cap(i18n.status)}{i18n.number_of_projects}{cap(i18n.created_by)}{cap(i18n.created_at)}{i18n.iati_version}{cap(i18n.actions)}
); } @@ -242,7 +379,8 @@ function loadComponents() { return { exports: null, initializing: true, - refreshing: false + refreshing: false, + actionInProgress: false }; }, @@ -277,16 +415,122 @@ function loadComponents() { } }, + publicFile: function() { + if (this.state.exports === null) { + return null; + } else { + for (var i = 0; i < this.state.exports.results.length; i++) { + var exp = this.state.exports.results[i]; + if (exp.iati_file !== '' && exp.is_public) { + return exp.id; + } + } + return null; + } + }, + + findExport: function(exportId) { + for (var i = 0; i < this.state.exports.results.length; i++) { + var exp = this.state.exports.results[i]; + if (exp.id === exportId) { + return exp; + } + } + return null; + }, + + setPublic: function(exportId) { + // Basically what we do is to set this export to public first, and then set all + // newer exports to private. This automatically makes this export the public export. + var thisApp = this, + exportUrl = endpoints.base_url + endpoints.iati_export, + thisExport = this.findExport(exportId), + newerExports = [], + newerExportsUpdated = 0, + publicData = JSON.stringify({'is_public': true}), + privateData = JSON.stringify({'is_public': false}); + + function allExportsUpdated() { + thisApp.loadExports(false); + thisApp.setState({actionInProgress: false}); + } + + function exportUpdated(response) { + newerExportsUpdated++; + if (newerExportsUpdated === newerExports.length) { + allExportsUpdated(); + } + } + + // Set current IATI export to public + this.setState({actionInProgress: true}); + apiCall('PATCH', exportUrl.replace('{iati_export}', exportId), publicData); + + // Find the newer IATI exports + for (var i = 0; i < this.state.exports.results.length; i++) { + var newerExp = this.state.exports.results[i]; + if (newerExp.id !== exportId && newerExp.created_at > thisExport.created_at) { + newerExports.push(newerExp); + } + } + + // Update the newer IATI exports + if (newerExports.length > 0) { + for (var j = 0; j < newerExports.length; j++) { + var exp = newerExports[j]; + apiCall('PATCH', exportUrl.replace('{iati_export}', exp.id), privateData, exportUpdated); + } + } else { + allExportsUpdated(); + } + }, + render: function() { + var initOrTable, + refreshing, + exportCount, + exportCountString, + lastExportDescription; + + refreshing = this.state.refreshing ? {' ' + cap(i18n.refreshing) + ' ' + i18n.iati_exports + '...'} : ; + exportCount = !this.state.initializing ? this.state.exports.count : null; + exportCountString = (exportCount !== null && exportCount > 0) ? ' ' + this.state.exports.count + ' ' : ' '; + + if (this.state.initializing) { + // Only show a message that data is being loading when initializing + initOrTable = {' ' + cap(i18n.loading) + ' ' + i18n.last + ' ' + i18n.iati_exports + '...'}; + } else if (exportCount > 0) { + // Show a table of exiting imports (max 10) when the data has been loaded and exports exist + initOrTable = React.createElement(ExportsTable, { + exports: this.state.exports, + refreshing: this.state.refreshing, + publicFile: this.publicFile(), + setPublic: this.setPublic + }); + } else { + // Do not show the 'Last exports' part when no exports exist yet + return ( +
+

{cap(i18n.new) + ' ' + i18n.iati_export}

+
+ ); + } + + lastExportDescription =
+ {cap(i18n.last_exports_1)} + {i18n.last_exports_url} + {'. ' + cap(i18n.last_exports_2) + ' ' + cap(i18n.last_exports_3)} + {i18n.iati_registry} + {i18n.last_exports_4} +
; + return (
-

{capitalizeFirstLetter(i18n.last_ten) + ' ' + i18n.iati_exports}

- {React.createElement(ExportsTable, { - exports: this.state.exports, - initializing: this.state.initializing, - refreshing: this.state.refreshing - })} -

{capitalizeFirstLetter(i18n.new) + ' ' + i18n.iati_export}

+

{cap(i18n.last) + exportCountString + i18n.iati_exports}

+ {lastExportDescription} + {refreshing} + {initOrTable} +

{cap(i18n.new) + ' ' + i18n.iati_export}

); } @@ -329,8 +573,9 @@ function loadAndRenderReact() { } document.addEventListener('DOMContentLoaded', function() { - i18n = JSON.parse(document.getElementById("translations").innerHTML); endpoints = JSON.parse(document.getElementById("endpoints").innerHTML); + months = JSON.parse(document.getElementById("months").innerHTML); + i18n = JSON.parse(document.getElementById("translations").innerHTML); if (document.getElementById('myIATIContainer')) { if (typeof React !== 'undefined' && typeof ReactDOM !== 'undefined') { diff --git a/akvo/rsr/static/styles-src/main.css b/akvo/rsr/static/styles-src/main.css index 5f94194a27..b91a848704 100755 --- a/akvo/rsr/static/styles-src/main.css +++ b/akvo/rsr/static/styles-src/main.css @@ -631,16 +631,13 @@ nav.navbar-fixed-top { #createProject:hover { background: #2c2a74; } -.indicator-period-list.parentProject .relatedInfo, -.indicator-period-list.childProject .relatedInfo { +.indicator-period-list.parentProject .relatedInfo, .indicator-period-list.childProject .relatedInfo { color: #2c2a74; } -.indicator-period-list.parentProject .relatedInfoProjectTitle, -.indicator-period-list.childProject .relatedInfoProjectTitle { +.indicator-period-list.parentProject .relatedInfoProjectTitle, .indicator-period-list.childProject .relatedInfoProjectTitle { display: none; margin-top: 15px; } - .indicator-period-list.parentProject .relatedInfoProjectTitle + .indicator-periods-title, - .indicator-period-list.childProject .relatedInfoProjectTitle + .indicator-periods-title { + .indicator-period-list.parentProject .relatedInfoProjectTitle + .indicator-periods-title, .indicator-period-list.childProject .relatedInfoProjectTitle + .indicator-periods-title { margin-top: 15px; } .indicator-period-list thead tr { @@ -1333,7 +1330,7 @@ h4.detailedInfo { .main-list.organisations ul li:hover img { filter: 0; -webkit-filter: grayscale(0%); - filter: url(); + filter: url(url(); filter: grayscale(0%); } /* PROJECT */ @@ -1520,7 +1517,7 @@ div.projectTopRow { div.projectTopRow .projectSideInfo ul li.projectPartners a.logoImg img:hover { filter: 0; -webkit-filter: grayscale(0%); - filter: url(); + filter: url(url(); filter: grayscale(0%); } div.projectTopRow .projectSideInfo ul li.projectPartners .partType { overflow: hidden; @@ -1634,7 +1631,7 @@ div.textBlock { .updateComponent { padding-top: 10px; padding-bottom: 15px; - background: #fff6f1; } + background: #fff6f2; } .updateComponent h4 { margin-bottom: 10px; } .updateComponent .updateLinkTitle { @@ -1679,8 +1676,7 @@ div.textBlock { .projectFinancial .budgetItem { margin-top: 12px; width: 60%; } - .projectFinancial .budgetItem:nth-child(1), - .projectFinancial .budgetItem:nth-child(2) { + .projectFinancial .budgetItem:nth-child(1), .projectFinancial .budgetItem:nth-child(2) { margin-top: 0; } /* Project hierarchy */ @@ -1762,8 +1758,7 @@ ul.typeahead-selector { padding-left: 6px; padding-top: 4px; padding-bottom: 4px; } - ul.typeahead-selector li:hover, - ul.typeahead-selector li.hover { + ul.typeahead-selector li:hover, ul.typeahead-selector li.hover { font-weight: bold; cursor: pointer; } ul.typeahead-selector li a { @@ -2162,7 +2157,7 @@ body.translationBarActive div.skiptranslate ~ #map { display: block; overflow: hidden; box-sizing: border-box; - transition: all 0.5s linear; + transition: all .5s linear; padding: 5px 15px; margin: 0 auto; -moz-border-radius: 5px; @@ -2424,8 +2419,7 @@ body.translationBarActive div.skiptranslate ~ #map { padding: 0.5em; margin: 0 1em; color: #00aaff; } - .tab-menu a:hover, - .tab-menu a.selected { + .tab-menu a:hover, .tab-menu a.selected { border-bottom: 3px solid #646363; color: #646363; } .tab-menu a:active { @@ -2536,8 +2530,7 @@ article { .periodValues .period-actual > span:first-child span, .periodValues .period-target-comment > span:first-child span { display: inline; } - .baseline .baseline-value span:nth-child(2n), - .baseline .baseline-value .actualValueSpan, + .baseline .baseline-value span:nth-child(2n), .baseline .baseline-value .actualValueSpan, .baseline .baseline-year span:nth-child(2n), .baseline .baseline-year .actualValueSpan, .baseline .period-target span:nth-child(2n), @@ -2554,11 +2547,11 @@ article { .periodValues .period-target .actualValueSpan, .periodValues .period-actual span:nth-child(2n), .periodValues .period-actual .actualValueSpan, - .periodValues .period-target-comment span:nth-child(2n), .periodValues .period-target-comment .actualValueSpan { + .periodValues .period-target-comment span:nth-child(2n), + .periodValues .period-target-comment .actualValueSpan { display: block; font-family: "Montserrat", "Helvetica Neue", Helvetica, Arial, sans-serif; } - .baseline .baseline-value span:nth-child(2n) span, - .baseline .baseline-value .actualValueSpan span, + .baseline .baseline-value span:nth-child(2n) span, .baseline .baseline-value .actualValueSpan span, .baseline .baseline-year span:nth-child(2n) span, .baseline .baseline-year .actualValueSpan span, .baseline .period-target span:nth-child(2n) span, @@ -2575,10 +2568,10 @@ article { .periodValues .period-target .actualValueSpan span, .periodValues .period-actual span:nth-child(2n) span, .periodValues .period-actual .actualValueSpan span, - .periodValues .period-target-comment span:nth-child(2n) span, .periodValues .period-target-comment .actualValueSpan span { + .periodValues .period-target-comment span:nth-child(2n) span, + .periodValues .period-target-comment .actualValueSpan span { display: inline; } - .baseline .baseline-value span:nth-child(2n) span:nth-child(1), - .baseline .baseline-value .actualValueSpan span:nth-child(1), + .baseline .baseline-value span:nth-child(2n) span:nth-child(1), .baseline .baseline-value .actualValueSpan span:nth-child(1), .baseline .baseline-year span:nth-child(2n) span:nth-child(1), .baseline .baseline-year .actualValueSpan span:nth-child(1), .baseline .period-target span:nth-child(2n) span:nth-child(1), @@ -2595,7 +2588,8 @@ article { .periodValues .period-target .actualValueSpan span:nth-child(1), .periodValues .period-actual span:nth-child(2n) span:nth-child(1), .periodValues .period-actual .actualValueSpan span:nth-child(1), - .periodValues .period-target-comment span:nth-child(2n) span:nth-child(1), .periodValues .period-target-comment .actualValueSpan span:nth-child(1) { + .periodValues .period-target-comment span:nth-child(2n) span:nth-child(1), + .periodValues .period-target-comment .actualValueSpan span:nth-child(1) { margin-left: 10px; } .baseline .period-target-comment span:nth-child(2n), .periodValues .period-target-comment span:nth-child(2n) { @@ -2696,8 +2690,7 @@ article { border: 1px solid rgba(153, 153, 153, 0.8); left: 50px; top: -2px; } - .baseline .baseline-year .result-tooltip .tooltip-arrow:after, - .baseline .baseline-year .result-tooltip .tooltip-arrow:before, + .baseline .baseline-year .result-tooltip .tooltip-arrow:after, .baseline .baseline-year .result-tooltip .tooltip-arrow:before, .baseline .period-actual .result-tooltip .tooltip-arrow:after, .baseline .period-actual .result-tooltip .tooltip-arrow:before, .periodValues .baseline-year .result-tooltip .tooltip-arrow:after, @@ -3032,10 +3025,7 @@ article { margin-right: 1.4em; } .indicator-container .indicator-group .table td { vertical-align: middle; } - .indicator-container .indicator-group .table td.period-td, - .indicator-container .indicator-group .table td.actual-td, - .indicator-container .indicator-group .table td.target-td, - .indicator-container .indicator-group .table td.actions-td { + .indicator-container .indicator-group .table td.period-td, .indicator-container .indicator-group .table td.actual-td, .indicator-container .indicator-group .table td.target-td, .indicator-container .indicator-group .table td.actions-td { white-space: nowrap; } .indicator-container .indicator-group .table td.target-td { font-weight: 600; } @@ -3661,8 +3651,7 @@ label.fileUpload input[type="file"] { opacity: 0.5; } .update-dialog-container td .update-entry-container .edit-slider .change-indicator.negative { background-color: red; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal { height: 5px; border: 0; background-color: rgba(255, 255, 255, 0); @@ -3673,15 +3662,13 @@ label.fileUpload input[type="file"] { -webkit-border-radius: 4px; border-radius: 4px; box-shadow: none; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-origin, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-origin { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-origin, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-origin { background-color: rgba(255, 255, 255, 0); -moz-border-radius: 4px; -o-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle { position: relative; width: 28px; height: 28px; @@ -3692,8 +3679,7 @@ label.fileUpload input[type="file"] { box-shadow: none; top: -7px; color: white; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:after, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:after { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:after, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:after { display: block; content: '|||'; color: white; @@ -3706,17 +3692,13 @@ label.fileUpload input[type="file"] { background-color: transparent; top: 0; left: -1px; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:before, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:before { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:before, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:before { display: none; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:hover, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:hover { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:hover, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:hover { background-color: #009ceb; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:active, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:active { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle:active, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle:active { background-color: #de8929; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-label, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-label { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-label, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-label { position: absolute; top: -32px; background-color: #007ab8; @@ -3724,8 +3706,7 @@ label.fileUpload input[type="file"] { width: 44px; left: -8px; text-align: center; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-label:after, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-label:after { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-label:after, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-label:after { content: ''; height: 0px; width: 0px; @@ -3735,8 +3716,7 @@ label.fileUpload input[type="file"] { border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 5px solid #007ab8; } - .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-change-label, - .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-change-label { + .update-dialog-container td .update-entry-container .edit-slider .noUi-base .noUi-handle .handle-change-label, .update-dialog-container td .update-entry-container .edit-slider.noUi-horizontal .noUi-handle .handle-change-label { position: absolute; width: 42px; text-align: center; @@ -3936,8 +3916,7 @@ div#my-reports { border: 1px solid rgba(153, 153, 153, 0.8); right: 8px; top: -2px; } - .period-td .result-tooltip .tooltip-arrow:after, - .period-td .result-tooltip .tooltip-arrow:before { + .period-td .result-tooltip .tooltip-arrow:after, .period-td .result-tooltip .tooltip-arrow:before { bottom: 100%; left: 50%; border: solid transparent; @@ -3957,6 +3936,26 @@ div#my-reports { border-width: 8px; margin-left: -8px; } +div#myIATIContainer tr.publicFile { + background: rgba(92, 184, 92, 0.1) !important; + color: #5cb85c !important; } + +div#myIATIContainer tr.inProgress { + background: rgba(222, 137, 41, 0.1) !important; + color: #de8929 !important; } + +div#myIATIContainer tr.cancelled { + background: rgba(238, 49, 36, 0.1) !important; + color: #ee3124 !important; } + +div#myIATIContainer button.btn-default { + margin: -5px 0 0 25px; + background: #202024; + color: white; + border: none; } + div#myIATIContainer button.btn-default:hover { + background: #2c2a74; } + .iatiCheck span.noCheck { color: #202024; } diff --git a/akvo/rsr/static/styles-src/main.scss b/akvo/rsr/static/styles-src/main.scss index 0bf147c17a..278c7c0750 100755 --- a/akvo/rsr/static/styles-src/main.scss +++ b/akvo/rsr/static/styles-src/main.scss @@ -4613,6 +4613,33 @@ div#my-reports { } } +// My IATI +div#myIATIContainer { + tr { + &.publicFile { + background: rgba($progressSuccess, 0.1) !important; + color: $progressSuccess !important; + } + &.inProgress { + background: rgba($flowOrange, 0.1) !important; + color: $flowOrange !important; + } + &.cancelled { + background: rgba($akvoTvRed, 0.1) !important; + color: $akvoTvRed !important; + } + } + button.btn-default { + margin: -5px 0 0 25px; + background: $akvoBlack; + color: white; + border: none; + &:hover { + background: $rsrBlue; + } + } +} + .iatiCheck { span.noCheck { color: $akvoBlack; diff --git a/akvo/templates/myrsr/my_iati.html b/akvo/templates/myrsr/my_iati.html index 67da5b0c06..d8634c83b4 100644 --- a/akvo/templates/myrsr/my_iati.html +++ b/akvo/templates/myrsr/my_iati.html @@ -43,16 +43,6 @@

{% trans 'Select an organisation' %}

{# Container for showing a table with existing IATI exports and creating a new IATI export (React) #}
-
- {% blocktrans with org_id=selected_org.id host=request.META.HTTP_HOST %} - The latest IATI file for your organisation is always available at this link: - http://{{ host }}/organisation/{{ org_id }}/iati/. - If you already have existing IATI exports, you can select which of the IATI exports should be available at this link. - This makes it possible to register only this link in the - IATI registry and automatically update your IATI file in the IATI - registry by updating your IATI file in RSR. - {% endblocktrans %} -
{# React implementation for creating a new IATI export #} @@ -119,11 +109,31 @@

{ "iati_export": "{% trans 'IATI export' %}", "iati_exports": "{% trans 'IATI exports' %}", + "last_exports_1": "{% trans 'the public IATI file for your organisation is always available at this link: ' %}", + "last_exports_2": "{% trans 'if you already have existing IATI exports, you can select which of the IATI exports should be available at this link.' %}", + "last_exports_3": "{% trans 'this makes it possible to register only this link in the ' %}", + "last_exports_4": "{% trans ' and automatically update your IATI file in the IATI registry by updating your IATI file in RSR.' %}", + "last_exports_url": "http://{{ request.META.HTTP_HOST }}/organisation/{{ selected_org.id }}/iati/", + "iati_registry": "{% trans 'IATI registry' %}", + "loading": "{% trans 'loading' %}", + "refreshing": "{% trans 'refreshing' %}", + "last": "{% trans 'last' %}", + "view_public_file": "{% trans 'view public file' %}", + "view_file": "{% trans 'view file' %}", + "set_public": "{% trans 'set as public file' %}", + "created_by": "{% trans 'created by' %}", + "created_at": "{% trans 'created at' %}", + "iati_version": "{% trans 'IATI version' %}", + "status": "{% trans 'status' %}", + "number_of_projects": "{% trans '# of projects' %}", + "actions": "{% trans 'actions' %}", + "no_iati_file": "{% trans 'no IATI file' %}", "new": "{% trans 'new' %}", - "last_ten": "{% trans 'last 10' %}", + "no_exports": "{% trans 'your organisation has no IATI exports yet, create a new IATI export below' %}", "perform_checks": "{% trans 'Perform checks' %}", "performing_checks": "{% trans 'Performing checks' %}", - "warning": "{% trans 'Warning' %}" + "warning": "{% trans 'Warning' %}", + "unknown_date": "{% trans 'Unknown date' %}" } @@ -131,7 +141,26 @@

+ + {# Months #} +