aboutsummaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/cobrands/fixmystreet/fixmystreet.js28
-rw-r--r--web/cobrands/fixmystreet/staff.js80
-rw-r--r--web/cobrands/sass/_base.scss158
-rw-r--r--web/cobrands/sass/_layout.scss13
-rw-r--r--web/cobrands/sass/_report_list_pins.scss2
-rw-r--r--web/js/duplicates.js206
-rw-r--r--web/js/map-OpenLayers.js8
7 files changed, 391 insertions, 104 deletions
diff --git a/web/cobrands/fixmystreet/fixmystreet.js b/web/cobrands/fixmystreet/fixmystreet.js
index 5efc0d878..d6d03eac3 100644
--- a/web/cobrands/fixmystreet/fixmystreet.js
+++ b/web/cobrands/fixmystreet/fixmystreet.js
@@ -974,7 +974,7 @@ $.extend(fixmystreet.set_up, {
e.preventDefault();
var form = $('<form/>').attr({ method:'post', action:"/alert/subscribe" });
form.append($('<input name="alert" value="Subscribe me to an email alert" type="hidden" />'));
- $('#alerts input[type=text], #alerts input[type=hidden], #alerts input[type=radio]:checked').each(function() {
+ $(this).closest('.js-alert-list').find('input[type=text], input[type=hidden], input[type=radio]:checked').each(function() {
var $v = $(this);
$('<input/>').attr({ name:$v.attr('name'), value:$v.val(), type:'hidden' }).appendTo(form);
});
@@ -1049,6 +1049,32 @@ $.extend(fixmystreet.set_up, {
var set_map_state = true;
fixmystreet.back_to_reports_list(e, report_list_url, map_state, set_map_state);
});
+ },
+
+ expandable_list_items: function(){
+ $(document).on('click', '.js-toggle-expansion', function(e) {
+ e.preventDefault(); // eg: prevent button from submitting parent form
+ var $toggle = $(this);
+ var $parent = $toggle.closest('.js-expandable');
+ $parent.toggleClass('expanded');
+ $toggle.text($parent.hasClass('expanded') ? $toggle.data('less') : $toggle.data('more'));
+ });
+
+ $(document).on('click', '.js-expandable', function(e) {
+ var $parent = $(this);
+ // Ignore parents that are already expanded.
+ if ( ! $parent.hasClass('expanded') ) {
+ // Ignore clicks on action buttons (clicks on the
+ // .js-toggle-expansion button will be handled by
+ // the more specific handler above).
+ if ( ! $(e.target).is('.item-list__item--expandable__actions *') ) {
+ e.preventDefault();
+ $parent.addClass('expanded');
+ var $toggle = $parent.find('.js-toggle-expansion');
+ $toggle.text($toggle.data('less'));
+ }
+ }
+ });
}
});
diff --git a/web/cobrands/fixmystreet/staff.js b/web/cobrands/fixmystreet/staff.js
index fadf18356..a7e0c8896 100644
--- a/web/cobrands/fixmystreet/staff.js
+++ b/web/cobrands/fixmystreet/staff.js
@@ -1,84 +1,4 @@
fixmystreet.staff_set_up = {
- manage_duplicates: function() {
- // Deal with changes to report state by inspector/other staff, specifically
- // displaying nearby reports if it's changed to 'duplicate'.
- function refresh_duplicate_list() {
- var report_id = $("#report_inspect_form .js-report-id").text();
- var args = {
- filter_category: $("#report_inspect_form select#category").val(),
- latitude: $('input[name="latitude"]').val(),
- longitude: $('input[name="longitude"]').val()
- };
- $("#js-duplicate-reports ul").html('<li class="item-list__item">Loading...</li>');
- var nearby_url = '/report/'+report_id+'/nearby.json';
- $.getJSON(nearby_url, args, function(data) {
- var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val();
- var $reports = $(data.reports_list)
- .not("[data-report-id="+report_id+"]")
- .slice(0, 5);
- $reports.filter("[data-report-id="+duplicate_of+"]").addClass("item-list--reports__item--selected");
-
- (function() {
- var timeout;
- $reports.on('mouseenter', function(){
- clearTimeout(timeout);
- fixmystreet.maps.markers_highlight(parseInt($(this).data('reportId'), 10));
- }).on('mouseleave', function(){
- timeout = setTimeout(fixmystreet.maps.markers_highlight, 50);
- });
- })();
-
- $("#js-duplicate-reports ul").empty().prepend($reports);
-
- $reports.find("a").click(function() {
- var report_id = $(this).closest("li").data('reportId');
- $("#report_inspect_form [name=duplicate_of]").val(report_id);
- $("#js-duplicate-reports ul li").removeClass("item-list--reports__item--selected");
- $(this).closest("li").addClass("item-list--reports__item--selected");
- return false;
- });
-
- show_nearby_pins(data, report_id);
- });
- }
-
- function show_nearby_pins(data, report_id) {
- var markers = fixmystreet.maps.markers_list( data.pins, true );
- // We're replacing all the features in the markers layer with the
- // possible duplicates, but the list of pins from the server doesn't
- // include the current report. So we need to extract the feature for
- // the current report and include it in the list of features we're
- // showing on the layer.
- var report_marker = fixmystreet.maps.get_marker_by_id(parseInt(report_id, 10));
- if (report_marker) {
- markers.unshift(report_marker);
- }
- fixmystreet.markers.removeAllFeatures();
- fixmystreet.markers.addFeatures( markers );
- }
-
- function state_change() {
- // The duplicate report list only makes sense when state is 'duplicate'
- if ($(this).val() !== "duplicate") {
- $("#js-duplicate-reports").addClass("hidden");
- return;
- } else {
- $("#js-duplicate-reports").removeClass("hidden");
- }
- // If this report is already marked as a duplicate of another, then
- // there's no need to refresh the list of duplicate reports
- var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val();
- if (!!duplicate_of) {
- return;
- }
-
- refresh_duplicate_list();
- }
-
- $("#report_inspect_form").on("change.state", "select#state", state_change);
- $("#js-change-duplicate-report").click(refresh_duplicate_list);
- },
-
action_scheduled_raise_defect: function() {
$("#report_inspect_form").find('[name=state]').on('change', function() {
if ($(this).val() !== "action scheduled") {
diff --git a/web/cobrands/sass/_base.scss b/web/cobrands/sass/_base.scss
index 276db90ae..c3cf3cb1b 100644
--- a/web/cobrands/sass/_base.scss
+++ b/web/cobrands/sass/_base.scss
@@ -530,6 +530,53 @@ ul.error {
@include border-radius(0.25em);
}
+.duplicate-report-suggestions {
+ position: relative;
+
+ .item-list__item--expandable {
+ border-top: 1px solid #ddd;
+ }
+
+ .item-list {
+ margin: 0 -1em 1em -1em;
+ border-bottom: 1px solid #ddd;
+ }
+}
+
+.duplicate-report-suggestions__close {
+ position: absolute;
+ right: 0;
+ top: 0;
+ display: block;
+ width: 32px;
+ height: 0;
+ padding-top: 32px;
+ overflow: hidden;
+ border: none;
+ background: transparent;
+ opacity: 0.5;
+ line-height: 2em; // Make sure line box is taller than text, so text is pushed below hidden overflow.
+
+ &:hover,
+ &:focus {
+ opacity: 0.7;
+ }
+
+ // Doing some gymnastics so we can reuse the existing .btn--close icon.
+ &:before {
+ content: "";
+ display: block;
+ width: 16px;
+ height: 16px;
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ background-repeat: no-repeat;
+ background-size: 112px 16px;
+ @include svg-background-image('/cobrands/fixmystreet/images/button-icons');
+ background-position: -32px 0;
+ }
+}
/*** LAYOUT ***/
@@ -1194,12 +1241,10 @@ input.final-submit {
// that appears in Inspector form, when closing a report as a duplicate.
.item-list--inspect-duplicates {
border-bottom: none;
+ background-color: rgba(255, 255, 255, 0.5);
- .item-list__item {
- background-color: rgba(255, 255, 255, 0.5);
- }
-
- .item-list--reports__item--selected {
+ .item-list__item--selected,
+ .js & .item-list__item--selected.expanded:hover {
border: 0.25em solid $primary;
background-color: #fff;
}
@@ -1256,6 +1301,92 @@ input.final-submit {
}
}
+.item-list__item--expandable {
+ @include clearfix();
+ margin: 0;
+ padding: 1em;
+
+ .js & {
+ cursor: pointer;
+
+ &:hover,
+ &.hovered {
+ background-color: $itemlist_item_background_hover;
+ }
+
+ &.item-list__item--selected,
+ &.item-list__item--selected:hover,
+ &.item-list__item--selected.hovered {
+ cursor: default;
+ background-color: #fff;
+ }
+ }
+
+ h3 {
+ margin: 0;
+ }
+
+ .img {
+ float: $right;
+ width: 90px;
+ height: auto;
+ margin: flip(0 0 0.5em 1em, 0 1em 0.5em 0);
+ }
+
+ small {
+ color: #666;
+ display: block;
+ margin-top: 0.5em;
+ }
+
+ p {
+ line-height: 1.4em;
+ margin-top: 0.5em;
+ }
+
+ &.expanded {
+ .js & {
+ cursor: default;
+
+ &:hover {
+ background-color: $itemlist_item_background;
+ }
+ }
+ }
+}
+
+.item-list__item--expandable__actions {
+ @include flex-container();
+
+ & > * {
+ @include flex(1); // Force equal width children
+ }
+}
+
+.item-list__item--expandable__hide-until-expanded {
+ display: none;
+
+ .expanded & {
+ display: block;
+ }
+}
+
+.item-list__item--expandable__hide-when-expanded {
+ .expanded & {
+ display: none;
+ }
+}
+
+.item-list__item .get-updates {
+ margin: 0 -1em -1em -1em;
+ padding: 1em;
+ background-color: $itemlist_item_background_hover;
+
+ p, label {
+ margin: 0 0 0.5em 0;
+ }
+}
+
.problem-header .update-img,
.item-list .update-img {
float: $right;
@@ -1768,6 +1899,23 @@ img.pin {
height: 10em; // eg: at the top of individual report pages
}
+#map_sidebar {
+ // Chrome/Safari count padding-bottom as part of the scrollable content in
+ // an overflow:scroll element (technically in contravention of the spec),
+ // whereas Firefox/IE render the padding outside the scrollable area.
+ // In desktop mode, we need padding at the bottom of the sidebar, to stop
+ // .shadow-wrap from obscuring content at the bottom of the sidebar. So we
+ // use an :after pseudo-element instead of padding.
+ // In mobile mode, the extra space here still helps differentiate the page
+ // content on report/reporting pages, from the nav immediately below.
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
+ &:after {
+ content: "";
+ display: block;
+ height: 4em;
+ }
+}
+
// When you're in the reporting flow on mobile, we hide the site-header
// and make the map full screen to reduce distractions. JavaScript also
// tweaks the text content of some of the map-related elements, to make
diff --git a/web/cobrands/sass/_layout.scss b/web/cobrands/sass/_layout.scss
index 8735da4f5..7d2b99c9b 100644
--- a/web/cobrands/sass/_layout.scss
+++ b/web/cobrands/sass/_layout.scss
@@ -268,19 +268,6 @@ body.mappage.admin {
width: $mappage-sidebar-width--medium + $mappage-actions-width--medium;
}
}
-
- // Chrome/Safari count padding-bottom as part of the scrollable content in
- // an overflow:scroll element (technically in contravention of the spec),
- // whereas Firefox/IE render the padding outside the scrollable area.
- // We need padding at the bottom of the sidebar, to stop .shadow-wrap from
- // obscuring content at the bottom of the sidebar. So we use an :after
- // pseudo-element instead of padding.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=748518
- &:after {
- content: "";
- display: block;
- height: 4em;
- }
}
// This goes inside #map_sidebar, and establishes a flex container allowing
diff --git a/web/cobrands/sass/_report_list_pins.scss b/web/cobrands/sass/_report_list_pins.scss
index 55ef1cf56..f6fcb46f9 100644
--- a/web/cobrands/sass/_report_list_pins.scss
+++ b/web/cobrands/sass/_report_list_pins.scss
@@ -27,7 +27,7 @@ $pin_prefix: '/i/' !default;
background-image: url('#{$pin_prefix}pin-orange-small.png');
}
}
-.item-list--reports__item--selected {
+.item-list__item--selected {
background: $base_bg;
a, a:hover, a:focus {
diff --git a/web/js/duplicates.js b/web/js/duplicates.js
new file mode 100644
index 000000000..4ed54846c
--- /dev/null
+++ b/web/js/duplicates.js
@@ -0,0 +1,206 @@
+$(function() {
+
+ // Store a reference to the "duplicate" report pins so we can
+ // quickly remove them when we’re finished showing duplicates.
+ var current_duplicate_markers;
+
+ // Report ID will be available on report inspect page,
+ // but undefined on new report page.
+ var report_id = $("#report_inspect_form .js-report-id").text() || undefined;
+
+ function refresh_duplicate_list() {
+ // This function will return a jQuery Promise, so callbacks can be
+ // hooked onto it, once the ajax request as completed.
+ var dfd = $.Deferred();
+
+ var nearby_url;
+ var url_params = {
+ filter_category: $('select[name="category"]').val(),
+ latitude: $('input[name="latitude"]').val(),
+ longitude: $('input[name="longitude"]').val()
+ };
+
+ if ( report_id ) {
+ nearby_url = '/report/' + report_id + '/nearby.json';
+ url_params.distance = 1000; // Inspectors might want to see reports fairly far away (1000 metres)
+ url_params.pin_size = 'small'; // How it's always been
+ } else {
+ nearby_url = '/around/nearby';
+ url_params.distance = 250; // Only want to bother public with very nearby reports (250 metres)
+ url_params.pin_size = 'normal';
+ }
+
+ $.ajax({
+ url: nearby_url,
+ data: url_params,
+ dataType: 'json'
+ }).done(function(response) {
+ if ( response.pins.length ){
+ render_duplicate_list(response);
+ render_duplicate_pins(response);
+ } else {
+ remove_duplicate_pins();
+ remove_duplicate_list();
+ }
+ dfd.resolve();
+ }).fail(function(){
+ remove_duplicate_pins();
+ remove_duplicate_list();
+ dfd.reject();
+ });
+
+ return dfd.promise();
+ }
+
+ function render_duplicate_list(api_response) {
+ var $reports = $( api_response.reports_list );
+
+ var duplicate_of = $('#report_inspect_form [name="duplicate_of"]').val();
+ if ( duplicate_of ) {
+ $reports.filter('[data-report-id="' + duplicate_of + '"]')
+ .addClass("item-list__item--selected");
+ }
+
+ $("#js-duplicate-reports ul").empty().prepend( $reports );
+ fixmystreet.set_up.fancybox_images();
+
+ $('#js-duplicate-reports').hide().removeClass('hidden').slideDown();
+ if ( $('#problem_form').length ) {
+ $('.js-hide-if-invalid-category').slideUp();
+ }
+
+ // Highlight map pin when hovering associated list item.
+ var timeout;
+ $reports.on('mouseenter', function(){
+ var id = parseInt( $(this).data('reportId'), 10 );
+ clearTimeout( timeout );
+ fixmystreet.maps.markers_highlight( id );
+ }).on('mouseleave', function(){
+ timeout = setTimeout( fixmystreet.maps.markers_highlight, 50 );
+ });
+
+ // Add a "select this report" button, when on the report inspect form.
+ if ( $('#report_inspect_form').length ) {
+ $reports.each(function(){
+ var $button = $('<button>').addClass('btn btn--small btn--primary');
+ $button.text(translation_strings.this_report);
+ $button.on('click', function(e) {
+ e.preventDefault(); // Prevent button from submitting parent form
+ var report_id = $(this).closest('li').data('reportId');
+ $('#report_inspect_form [name="duplicate_of"]').val(report_id);
+ $(this).closest('li')
+ .addClass('item-list__item--selected')
+ .siblings('.item-list__item--selected')
+ .removeClass('item-list__item--selected');
+ });
+ $(this).find('.item-list__item--expandable__actions').append($button);
+ });
+ }
+
+ // Add a "track this report" button when on the regular reporting form.
+ if ( $('#problem_form').length ) {
+ $reports.each(function() {
+ var $li = $(this);
+ var id = parseInt( $li.data('reportId'), 10 );
+ var alert_url = '/alert/subscribe?id=' + encodeURIComponent(id);
+ var $button = $('<a>').addClass('btn btn--small btn--primary');
+ $button.text(translation_strings.this_is_the_problem);
+ $button.attr('href', alert_url);
+ $button.on('click', function(e){
+ e.preventDefault();
+ var $div = $('.js-template-get-updates > div').clone();
+ $div.find('input[name="id"]').val(id);
+ $div.find('input[disabled]').prop('disabled', false);
+ $div.hide().appendTo($li).slideDown(250, function(){
+ $div.find('input[type="email"]').focus();
+ });
+ $li.find('.item-list__item--expandable__actions').slideUp(250);
+ $li.removeClass('js-expandable');
+ $li.addClass('item-list__item--selected');
+ });
+ $li.find('.item-list__item--expandable__actions').append($button);
+ });
+ }
+ }
+
+ function render_duplicate_pins(api_response) {
+ var markers = fixmystreet.maps.markers_list( api_response.pins, true );
+ fixmystreet.markers.removeFeatures( current_duplicate_markers );
+ fixmystreet.markers.addFeatures( markers );
+ current_duplicate_markers = markers;
+ }
+
+ function remove_duplicate_list(cb) {
+ var animations = [];
+
+ animations.push( $.Deferred() );
+ $('#js-duplicate-reports').slideUp(function(){
+ $(this).addClass('hidden');
+ $(this).find('ul').empty();
+ animations[0].resolve();
+ });
+ if ( $('#problem_form').length ) {
+ animations.push( $.Deferred() );
+ $('.js-hide-if-invalid-category').slideDown(function(){
+ animations[1].resolve();
+ });
+ }
+
+ $.when.apply(this, animations).then(cb);
+ }
+
+ function remove_duplicate_pins() {
+ fixmystreet.markers.removeFeatures( current_duplicate_markers );
+ }
+
+ function inspect_form_state_change() {
+ // The duplicate report list only makes sense when state is 'duplicate'
+ if ($(this).val() !== "duplicate") {
+ $("#js-duplicate-reports").addClass("hidden");
+ return;
+ } else {
+ $("#js-duplicate-reports").removeClass("hidden");
+ }
+ // If this report is already marked as a duplicate of another, then
+ // there's no need to refresh the list of duplicate reports
+ var duplicate_of = $("#report_inspect_form [name=duplicate_of]").val();
+ if (!!duplicate_of) {
+ return;
+ }
+ refresh_duplicate_list();
+ }
+
+ var category_changing = false;
+ function problem_form_category_change() {
+ // Annoyingly this event seems to fire a few times in quick succession,
+ // so set a flag to avoid multiple overlapping refreshes.
+ if (category_changing) { return; }
+ category_changing = true;
+
+ refresh_duplicate_list().always(function(){
+ // Wait an extra second until we allow another reload.
+ setTimeout(function(){
+ category_changing = false;
+ }, 1000);
+ });
+ }
+
+ // Want to show potential duplicates when a regular user starts a new
+ // report, or changes the category/location of a partial report.
+ $("#problem_form").on("change.category", "select#form_category", problem_form_category_change);
+
+ // Want to show duplicates when an inspector sets a report’s state to "duplicate".
+ $("#report_inspect_form").on("change.state", "select#state", inspect_form_state_change);
+
+ // Also want to give inspectors a way to select a *new* duplicate report.
+ $("#js-change-duplicate-report").click(refresh_duplicate_list);
+
+ $('.js-hide-duplicate-suggestions').on('click', function(e){
+ e.preventDefault();
+ remove_duplicate_pins();
+ remove_duplicate_list(function(){
+ $('#form_title').focus();
+ });
+ });
+
+});
diff --git a/web/js/map-OpenLayers.js b/web/js/map-OpenLayers.js
index fdfeed314..984f4b098 100644
--- a/web/js/map-OpenLayers.js
+++ b/web/js/map-OpenLayers.js
@@ -381,15 +381,15 @@ $.extend(fixmystreet.utils, {
function sidebar_highlight(problem_id) {
if (typeof problem_id !== 'undefined') {
- var $a = $('.item-list--reports a[href$="/' + problem_id + '"]');
- $a.parent().addClass('hovered');
+ var $li = $('[data-report-id="' + problem_id + '"]');
+ $li.addClass('hovered');
} else {
- $('.item-list--reports .hovered').removeClass('hovered');
+ $('.item-list .hovered').removeClass('hovered');
}
}
function marker_click(problem_id, evt) {
- var $a = $('.item-list--reports a[href$="/' + problem_id + '"]');
+ var $a = $('.item-list a[href$="/' + problem_id + '"]');
if (!$a[0]) {
return;
}