aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md3
-rw-r--r--perllib/FixMyStreet/App/Controller/Around.pm15
-rw-r--r--perllib/FixMyStreet/App/Controller/Report.pm30
-rw-r--r--perllib/FixMyStreet/Cobrand/Borsetshire.pm2
-rw-r--r--perllib/FixMyStreet/DB/ResultSet/Nearby.pm24
-rw-r--r--perllib/FixMyStreet/Map.pm17
-rw-r--r--t/app/controller/around.t6
-rw-r--r--t/cobrand/restriction.t2
-rw-r--r--t/map/tilma/original.t4
-rw-r--r--templates/web/base/alert/list-ajax.html2
-rw-r--r--templates/web/base/alert/list.html2
-rwxr-xr-xtemplates/web/base/around/display_location.html1
-rw-r--r--templates/web/base/js/translation_strings.html3
-rw-r--r--templates/web/base/report/_inspect.html7
-rw-r--r--templates/web/base/report/_item.html35
-rw-r--r--templates/web/base/report/_item_expandable.html55
-rw-r--r--templates/web/base/report/_item_small.html32
-rw-r--r--templates/web/base/report/duplicate-no-updates.html2
-rw-r--r--templates/web/base/report/nearby.html2
-rw-r--r--templates/web/base/report/new/duplicate_suggestions.html42
-rw-r--r--templates/web/base/report/new/fill_in_details.html2
-rw-r--r--templates/web/base/report/new/form_report.html1
-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
29 files changed, 605 insertions, 179 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7d5d4bb6..abe507e60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,9 @@
## Releases
* Unreleased
+ - New features:
+ - (Optional) auto-suggestion of similar nearby problems,
+ while reporting, to discourage duplicate reports. #2386
- Front end improvements:
- Track map state in URL to make sharing links easier. #2242
- Admin improvements:
diff --git a/perllib/FixMyStreet/App/Controller/Around.pm b/perllib/FixMyStreet/App/Controller/Around.pm
index ec8df4450..a09161494 100644
--- a/perllib/FixMyStreet/App/Controller/Around.pm
+++ b/perllib/FixMyStreet/App/Controller/Around.pm
@@ -269,7 +269,7 @@ sub map_features : Private {
# Allow the cobrand to add in any additional query parameters
my $extra_params = $c->cobrand->call_hook('display_location_extra_params');
- my ( $on_map, $nearby, $distance ) =
+ my ( $on_map, $nearby ) =
FixMyStreet::Map::map_features(
$c, %$extra,
categories => [ keys %{$c->stash->{filter_category}} ],
@@ -290,7 +290,6 @@ sub map_features : Private {
$c->stash->{pins} = \@pins;
$c->stash->{on_map} = $on_map;
$c->stash->{around_map} = $nearby;
- $c->stash->{distance} = $distance;
}
=head2 ajax
@@ -318,6 +317,18 @@ sub ajax : Path('/ajax') {
$c->forward('/reports/ajax', [ 'around/on_map_list_items.html' ]);
}
+sub nearby : Path {
+ my ($self, $c) = @_;
+
+ my $states = FixMyStreet::DB::Result::Problem->open_states();
+ $c->forward('/report/_nearby_json', [ {
+ latitude => $c->get_param('latitude'),
+ longitude => $c->get_param('longitude'),
+ categories => [ $c->get_param('filter_category') || () ],
+ states => $states,
+ } ]);
+}
+
sub location_closest_address : Path('/ajax/closest') {
my ( $self, $c ) = @_;
$c->res->content_type('application/json; charset=utf-8');
diff --git a/perllib/FixMyStreet/App/Controller/Report.pm b/perllib/FixMyStreet/App/Controller/Report.pm
index 12f6ec1d1..e032caa09 100644
--- a/perllib/FixMyStreet/App/Controller/Report.pm
+++ b/perllib/FixMyStreet/App/Controller/Report.pm
@@ -600,22 +600,40 @@ sub nearby_json :PathPart('nearby.json') :Chained('id') :Args(0) {
my ($self, $c) = @_;
my $p = $c->stash->{problem};
- my $dist = 1;
+ $c->forward('_nearby_json', [ {
+ latitude => $p->latitude,
+ longitude => $p->longitude,
+ categories => [ $p->category ],
+ ids => [ $p->id ],
+ } ]);
+}
+
+sub _nearby_json :Private {
+ my ($self, $c, $params) = @_;
# This is for the list template, this is a list on that page.
$c->stash->{page} = 'report';
- my $extra_params = $c->cobrand->call_hook('display_location_extra_params');
+ # distance in metres
+ my $dist = $c->get_param('distance') || '';
+ $dist = 1000 unless $dist =~ /^\d+$/;
+ $dist = 1000 if $dist > 1000;
+ $params->{distance} = $dist / 1000;
+
+ my $pin_size = $c->get_param('pin_size') || '';
+ $pin_size = 'small' unless $pin_size =~ /^(mini|small|normal|big)$/;
+
+ $params->{extra} = $c->cobrand->call_hook('display_location_extra_params');
+ $params->{limit} = 5;
+
+ my $nearby = $c->model('DB::Nearby')->nearby($c, %$params);
- my $nearby = $c->model('DB::Nearby')->nearby(
- $c, $dist, [ $p->id ], 5, $p->latitude, $p->longitude, [ $p->category ], undef, $extra_params
- );
# Want to treat these as if they were on map
$nearby = [ map { $_->problem } @$nearby ];
my @pins = map {
my $p = $_->pin_data($c, 'around');
[ $p->{latitude}, $p->{longitude}, $p->{colour},
- $p->{id}, $p->{title}, 'small', JSON->false
+ $p->{id}, $p->{title}, $pin_size, JSON->false
]
} @$nearby;
diff --git a/perllib/FixMyStreet/Cobrand/Borsetshire.pm b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
index 44a4a9162..e721bee0f 100644
--- a/perllib/FixMyStreet/Cobrand/Borsetshire.pm
+++ b/perllib/FixMyStreet/Cobrand/Borsetshire.pm
@@ -33,4 +33,6 @@ sub bypass_password_checks { 1 }
sub enable_category_groups { 1 }
+sub suggest_duplicates { 1 }
+
1;
diff --git a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm
index 3d8f87b9f..2ebe309e3 100644
--- a/perllib/FixMyStreet/DB/ResultSet/Nearby.pm
+++ b/perllib/FixMyStreet/DB/ResultSet/Nearby.pm
@@ -10,34 +10,34 @@ sub to_body {
}
sub nearby {
- my ( $rs, $c, $dist, $ids, $limit, $mid_lat, $mid_lon, $categories, $states, $extra_params, $report_age ) = @_;
+ my ( $rs, $c, %args ) = @_;
- unless ( $states ) {
- $states = FixMyStreet::DB::Result::Problem->visible_states();
+ unless ( $args{states} ) {
+ $args{states} = FixMyStreet::DB::Result::Problem->visible_states();
}
my $params = {
- state => [ keys %$states ],
+ state => [ keys %{$args{states}} ],
};
- $params->{id} = { -not_in => $ids }
- if $ids;
- $params->{category} = $categories if $categories && @$categories;
+ $params->{id} = { -not_in => $args{ids} }
+ if $args{ids};
+ $params->{category} = $args{categories} if $args{categories} && @{$args{categories}};
- $params->{$c->stash->{report_age_field}} = { '>=', \"current_timestamp-'$report_age'::interval" }
- if $report_age;
+ $params->{$c->stash->{report_age_field}} = { '>=', \"current_timestamp-'$args{report_age}'::interval" }
+ if $args{report_age};
FixMyStreet::DB::ResultSet::Problem->non_public_if_possible($params, $c);
$rs = $c->cobrand->problems_restriction($rs);
# Add in any optional extra query parameters
- $params = { %$params, %$extra_params } if $extra_params;
+ $params = { %$params, %{$args{extra}} } if $args{extra};
my $attrs = {
prefetch => 'problem',
- bind => [ $mid_lat, $mid_lon, $dist ],
+ bind => [ $args{latitude}, $args{longitude}, $args{distance} ],
order_by => [ 'distance', { -desc => 'created' } ],
- rows => $limit,
+ rows => $args{limit},
};
my @problems = mySociety::Locale::in_gb_locale { $rs->search( $params, $attrs )->all };
diff --git a/perllib/FixMyStreet/Map.pm b/perllib/FixMyStreet/Map.pm
index 8ed0c4b37..8b8cfe82c 100644
--- a/perllib/FixMyStreet/Map.pm
+++ b/perllib/FixMyStreet/Map.pm
@@ -104,26 +104,21 @@ sub map_features {
# if show_old_reports is on then there must be old reports
$c->stash->{num_old_reports} = 1;
} else {
- $p{report_age} = undef;
- $p{page} = 1;
- my $older = $c->cobrand->problems_on_map->around_map( $c, %p );
+ my $older = $c->cobrand->problems_on_map->around_map( $c, %p, report_age => undef, page => 1 );
$c->stash->{num_old_reports} = $older->pager->total_entries - $pager->total_entries;
}
- my $dist = FixMyStreet::Gaze::get_radius_containing_population( $p{latitude}, $p{longitude} );
-
# if there are fewer entries than our paging limit on the map then
# also return nearby entries for display
my $nearby;
if (@$on_map < $pager->entries_per_page && $pager->current_page == 1) {
- my $limit = 20;
- my @ids = map { $_->id } @$on_map;
- $nearby = $c->model('DB::Nearby')->nearby(
- $c, $dist, \@ids, $limit, @p{"latitude", "longitude", "categories", "states", "extra"}, $report_age
- );
+ $p{limit} = 20;
+ $p{ids} = [ map { $_->id } @$on_map ];
+ $p{distance} = FixMyStreet::Gaze::get_radius_containing_population( $p{latitude}, $p{longitude} );
+ $nearby = $c->model('DB::Nearby')->nearby($c, %p);
}
- return ( $on_map, $nearby, $dist );
+ return ( $on_map, $nearby );
}
sub click_to_wgs84 {
diff --git a/t/app/controller/around.t b/t/app/controller/around.t
index ed29d438c..3f0fff666 100644
--- a/t/app/controller/around.t
+++ b/t/app/controller/around.t
@@ -359,4 +359,10 @@ subtest 'check map zoom level customisation' => sub {
};
};
+subtest 'check nearby lookup' => sub {
+ my $p = FixMyStreet::DB->resultset("Problem")->search({ external_body => "Pothole-confirmed" })->first;
+ $mech->get_ok('/around/nearby?latitude=51.754926&longitude=-1.256179&filter_category=Pothole');
+ $mech->content_contains('["51.754926","-1.256179","yellow",' . $p->id . ',"Around page Test 1 for ' . $body->id . '","small",false]');
+};
+
done_testing();
diff --git a/t/cobrand/restriction.t b/t/cobrand/restriction.t
index 9e3018625..63fe326b1 100644
--- a/t/cobrand/restriction.t
+++ b/t/cobrand/restriction.t
@@ -47,7 +47,7 @@ is($c->model('DB::Problem')->count, 4, 'Four reports in database');
is($cobrand->problems->count, 2, 'Two reports in the right cobrand');
is($cobrand->updates->count, 1, 'One update in the right cobrand');
-my $nearby = $c->model('DB::Nearby')->nearby($c, 5, [], 10, 0.003, 0.004);
+my $nearby = $c->model('DB::Nearby')->nearby($c, distance => 5, ids => [], limit => 10, latitude => 0.003, longitude => 0.004);
is(@$nearby, 1, 'One report close to the origin point');
done_testing();
diff --git a/t/map/tilma/original.t b/t/map/tilma/original.t
index 42cbbd9f2..9a404a2c9 100644
--- a/t/map/tilma/original.t
+++ b/t/map/tilma/original.t
@@ -93,12 +93,10 @@ for my $test (
$report->update;
$c->stash->{report_age_field} = 'lastupdate';
- my ( $on_map, $nearby, $dist ) =
- FixMyStreet::Map::map_features($c, bbox => "0,0,0,0");
+ my ($on_map, $nearby) = FixMyStreet::Map::map_features($c, bbox => "0,0,0,0");
ok $on_map;
ok $nearby;
- ok $dist;
my $id = $report->id;
my $colour = $test->{colour};
diff --git a/templates/web/base/alert/list-ajax.html b/templates/web/base/alert/list-ajax.html
index 639af6f07..5da71b58c 100644
--- a/templates/web/base/alert/list-ajax.html
+++ b/templates/web/base/alert/list-ajax.html
@@ -4,6 +4,6 @@
%]
[% END %]
-<div id="alerts">
+<div id="alerts" class="js-alert-list">
[% INCLUDE 'alert/_list.html' %]
</div>
diff --git a/templates/web/base/alert/list.html b/templates/web/base/alert/list.html
index 385cd7d32..14215b65d 100644
--- a/templates/web/base/alert/list.html
+++ b/templates/web/base/alert/list.html
@@ -32,7 +32,7 @@
</div>
[% END %]
-<form id="alerts" name="alerts" method="post" action="/alert/subscribe">
+<form id="alerts" class="js-alert-list" name="alerts" method="post" action="/alert/subscribe">
[% INCLUDE 'alert/_list.html' %]
diff --git a/templates/web/base/around/display_location.html b/templates/web/base/around/display_location.html
index 826e70632..6c71ad631 100755
--- a/templates/web/base/around/display_location.html
+++ b/templates/web/base/around/display_location.html
@@ -33,6 +33,7 @@
SET bodyclass = 'mappage';
SET rss = [ tprintf(loc('Recent local problems, %s', "%s is the site name"), site_name), rss_url ];
+ SET extra_js = [];
INCLUDE 'header.html',
title => loc('Viewing a location')
robots => 'noindex,nofollow';
diff --git a/templates/web/base/js/translation_strings.html b/templates/web/base/js/translation_strings.html
index af3073f91..9747773d9 100644
--- a/templates/web/base/js/translation_strings.html
+++ b/templates/web/base/js/translation_strings.html
@@ -41,6 +41,9 @@ fixmystreet.password_minimum_length = [% c.cobrand.password_minimum_length %];
how_to_send: '[% loc('How to send successful reports') | replace("'", "\\'") %]',
more_details: '[% loc('Details') | replace("'", "\\'") %]',
+ this_report: '[% loc('This report') | replace("'", "\\'") %]',
+ this_is_the_problem: '[% loc('This is the problem') | replace("'", "\\'") %]',
+
or: '[% loc(' or ') | replace("'", "\\'") %]',
geolocation_declined: '[% loc('You declined; please fill in the box above') | replace("'", "\\'") %]',
diff --git a/templates/web/base/report/_inspect.html b/templates/web/base/report/_inspect.html
index e5094d02e..fa79d9912 100644
--- a/templates/web/base/report/_inspect.html
+++ b/templates/web/base/report/_inspect.html
@@ -1,3 +1,6 @@
+[% extra_js = [
+ version('/js/duplicates.js'),
+] -%]
[% permissions = c.user.permissions(problem) %]
[% second_column = BLOCK -%]
<div id="side-inspect">
@@ -120,7 +123,7 @@
<p class="[% "hidden" IF problem.duplicate_of %]">[% loc('Which report is it a duplicate of?') %]</p>
<ul class="item-list item-list--inspect-duplicates">
[% IF problem.duplicate_of %]
- [% INCLUDE 'report/_item.html' item_extra_class = 'item-list--reports__item--selected' problem = problem.duplicate_of %]
+ [% INCLUDE 'report/_item_expandable.html' item_extra_class = 'item-list__item--selected' problem = problem.duplicate_of %]
<li class="item-list__item"><a class="btn" href="#" id="js-change-duplicate-report">[% loc('Choose another') %]</a></li>
[% END %]
</ul>
@@ -129,7 +132,7 @@
<p><strong>[% loc('Duplicates') %]</strong></p>
<ul class="item-list item-list--inspect-duplicates">
[% FOR duplicate IN problem.duplicates %]
- [% INCLUDE 'report/_item.html' problem = duplicate %]
+ [% INCLUDE 'report/_item_expandable.html' problem = duplicate %]
[% END %]
</ul>
[% END %]
diff --git a/templates/web/base/report/_item.html b/templates/web/base/report/_item.html
index d710f6c81..863b87817 100644
--- a/templates/web/base/report/_item.html
+++ b/templates/web/base/report/_item.html
@@ -42,40 +42,7 @@
[% IF c.user.has_permission_to('report_inspect', problem.bodies_str_ids) %]
<div class="item-list__description">[% problem.detail | html %]</div>
[% END %]
- <small>
- [% IF NOT no_fixed AND problem.is_fixed %]
- <span class="item-list__item__state">[% prettify_state('fixed') %]</span>
- [% ELSIF NOT no_fixed AND problem.is_closed %]
- <span class="item-list__item__state">[% prettify_state('closed') %]</span>
- [% ELSIF (c.user.has_permission_to('report_edit_priority', problem.bodies_str_ids) OR c.user.has_permission_to('report_inspect', problem.bodies_str_ids)) AND problem.response_priority %]
- <span class="item-list__item__state">[% problem.response_priority.name %]</span>
- [% END %]
- [%- IF c.cobrand.moniker != 'fixamingata' %] [%# Default: %]
- [%- IF problem.days_ago > 0 AND problem.days_ago <= c.cobrand.display_days_ago_threshold %]
- [% tprintf( nget('Reported %d day ago', 'Reported %d days ago', problem.days_ago), problem.days_ago ) %]
- [%- ELSE %]
- [% prettify_dt( problem.confirmed, 1 ) %]
- [%- END %]
- [%- ELSE %] [%# Swedish cobrand fixamingata: %]
- [%- prettify_dt( problem.confirmed) %]
- [%- END %]
- [%- IF dist %], [% dist %]km[% END %]
- [%- IF problem.confirmed != problem.lastupdate AND problem.whensent != problem.lastupdate %],
- [%- IF problem.days_ago('lastupdate') > 0 AND problem.days_ago('lastupdate') <= c.cobrand.display_days_ago_threshold %]
- [% tprintf( nget('last updated %d day ago', 'last updated %d days ago', problem.days_ago('lastupdate') ), problem.days_ago('lastupdate') ) %]
- [%- ELSE %]
- [% tprintf(loc('last updated %s'), prettify_dt( problem.lastupdate, 1 ) ) %]
- [%- END %]
- [%- END %]
- [% IF include_sentinfo %]
- [% IF c.cobrand.is_council && !c.cobrand.owns_problem( problem ) %]
- (sent to [% problem.body %])
- [% ELSIF problem.bodies_str_ids.size > 2 %] [% loc('(sent to all)') %]
- [% ELSIF problem.bodies_str_ids.size == 2 %] [% loc('(sent to both)') %]
- [% ELSIF problem.bodies_str_ids.size == 0 %] [% loc('(not sent to council)') %]
- [% END %]
- [% END %]
- </small>
+ <small>[% PROCESS 'report/_item_small.html' %]</small>
[% IF c.user.has_permission_to('report_inspect', problem.bodies_str_ids) %]
<div class="item-list__item__metadata">
diff --git a/templates/web/base/report/_item_expandable.html b/templates/web/base/report/_item_expandable.html
new file mode 100644
index 000000000..6a4fe7191
--- /dev/null
+++ b/templates/web/base/report/_item_expandable.html
@@ -0,0 +1,55 @@
+[%#
+ This list item contains a full photo set and description, which is hidden
+ until the `.expanded` class is added. It is useful for times when you want
+ to surface the full information about a report, without requiring the user
+ to visit the dedicated report page.
+
+ Since the photo set includes `<a>` elements of its own, we drop the usual
+ "wrapper" `<a>` and the associated `.item-list--reports__item` class, to
+ avoid anchor nesting.
+%]
+
+[% truncated_detail = BLOCK %][% problem.detail | truncate(75, '…') | html_para %][% END ~%]
+[% full_detail = BLOCK %][% problem.detail | add_links | html_para %][% END ~%]
+[% SET show_more = truncated_detail != full_detail OR problem.photo ~%]
+
+<li class="[% 'js-expandable ' IF show_more %]item-list__item item-list__item--expandable [% item_extra_class %]"
+ data-report-id="[% problem.id | html %]"
+ id="report-[% problem.id | html %]">
+
+ [% IF problem.photo %]
+ <a href="[% c.cobrand.base_url_for_report( problem ) %][% problem.url %]" class="item-list__item--expandable__hide-when-expanded">
+ <img class="img" height="60" width="90" src="[% problem.photos.first.url_fp %]" alt="">
+ </a>
+ [% END %]
+
+ [% TRY %]
+ [% PROCESS 'report/_item_heading.html' %]
+ [% CATCH file %]
+ <h3>
+ <a href="[% c.cobrand.base_url_for_report( problem ) %][% problem.url %]">
+ [% problem.title | html %]
+ </a>
+ </h3>
+ [% END %]
+
+ <small>[% PROCESS 'report/_item_small.html' %]</small>
+
+ <div class="item-list__item--expandable__hide-when-expanded">
+ [% truncated_detail %]
+ </div>
+
+ [% IF show_more ~%]
+ <div class="item-list__item--expandable__hide-until-expanded">
+ [% full_detail %]
+ [% INCLUDE 'report/photo.html' object=problem %]
+ </div>
+ [% END %]
+
+ <div class="item-list__item--expandable__actions">
+ [% IF show_more ~%]
+ <button class="btn btn--small js-toggle-expansion" data-more="[% loc('Read more') %]" data-less="[% loc('Read less') %]">[% loc('Read more') %]</button>
+ [% END %]
+ </div>
+
+</li>
diff --git a/templates/web/base/report/_item_small.html b/templates/web/base/report/_item_small.html
new file mode 100644
index 000000000..77fc9f569
--- /dev/null
+++ b/templates/web/base/report/_item_small.html
@@ -0,0 +1,32 @@
+[% IF NOT no_fixed AND problem.is_fixed %]
+ <span class="item-list__item__state">[% prettify_state('fixed') %]</span>
+[% ELSIF NOT no_fixed AND problem.is_closed %]
+ <span class="item-list__item__state">[% prettify_state('closed') %]</span>
+[% ELSIF (c.user.has_permission_to('report_edit_priority', problem.bodies_str_ids) OR c.user.has_permission_to('report_inspect', problem.bodies_str_ids)) AND problem.response_priority %]
+ <span class="item-list__item__state">[% problem.response_priority.name %]</span>
+[% END %]
+[%- IF c.cobrand.moniker != 'fixamingata' %] [%# Default: %]
+ [%- IF problem.days_ago > 0 AND problem.days_ago <= c.cobrand.display_days_ago_threshold %]
+ [% tprintf( nget('Reported %d day ago', 'Reported %d days ago', problem.days_ago), problem.days_ago ) %]
+ [%- ELSE %]
+ [% prettify_dt( problem.confirmed, 1 ) %]
+ [%- END %]
+[%- ELSE %] [%# Swedish cobrand fixamingata: %]
+ [%- prettify_dt( problem.confirmed) %]
+[%- END %]
+[%- IF dist %], [% dist %]km[% END %]
+[%- IF problem.confirmed != problem.lastupdate AND problem.whensent != problem.lastupdate %],
+ [%- IF problem.days_ago('lastupdate') > 0 AND problem.days_ago('lastupdate') <= c.cobrand.display_days_ago_threshold %]
+ [% tprintf( nget('last updated %d day ago', 'last updated %d days ago', problem.days_ago('lastupdate') ), problem.days_ago('lastupdate') ) %]
+ [%- ELSE %]
+ [% tprintf(loc('last updated %s'), prettify_dt( problem.lastupdate, 1 ) ) %]
+ [%- END %]
+[%- END %]
+[% IF include_sentinfo %]
+ [% IF c.cobrand.is_council && !c.cobrand.owns_problem( problem ) %]
+ (sent to [% problem.body %])
+ [% ELSIF problem.bodies_str_ids.size > 2 %] [% loc('(sent to all)') %]
+ [% ELSIF problem.bodies_str_ids.size == 2 %] [% loc('(sent to both)') %]
+ [% ELSIF problem.bodies_str_ids.size == 0 %] [% loc('(not sent to council)') %]
+ [% END %]
+[% END %]
diff --git a/templates/web/base/report/duplicate-no-updates.html b/templates/web/base/report/duplicate-no-updates.html
index 7de2ae042..6cfc85887 100644
--- a/templates/web/base/report/duplicate-no-updates.html
+++ b/templates/web/base/report/duplicate-no-updates.html
@@ -4,6 +4,6 @@
[% END %]
<p>[% loc("This report is a duplicate. Please leave updates on the original report:") %]</p>
<ul class="item-list">
- [% INCLUDE 'report/_item.html' item_extra_class = 'item-list__item--with-pin item-list--reports__item--selected' problem = problem.duplicate_of %]
+ [% INCLUDE 'report/_item.html' item_extra_class = 'item-list__item--with-pin item-list__item--selected' problem = problem.duplicate_of %]
</ul>
</div>
diff --git a/templates/web/base/report/nearby.html b/templates/web/base/report/nearby.html
index c64b10d7f..1e0d6cc79 100644
--- a/templates/web/base/report/nearby.html
+++ b/templates/web/base/report/nearby.html
@@ -1,3 +1,3 @@
[%~ FOREACH problem IN reports ~%]
- [%~ INCLUDE 'reports/_list-entry.html' ~%]
+ [%~ INCLUDE 'report/_item_expandable.html' ~%]
[%~ END ~%]
diff --git a/templates/web/base/report/new/duplicate_suggestions.html b/templates/web/base/report/new/duplicate_suggestions.html
new file mode 100644
index 000000000..582ba58e9
--- /dev/null
+++ b/templates/web/base/report/new/duplicate_suggestions.html
@@ -0,0 +1,42 @@
+[% IF c.cobrand.suggest_duplicates %]
+[% extra_js.push(
+ version('/js/duplicates.js'),
+) -%]
+<div id="js-duplicate-reports" class="duplicate-report-suggestions hidden">
+ <button class="duplicate-report-suggestions__close js-hide-duplicate-suggestions">[% loc('Close') %]</button>
+ <h2 class="form-section-heading">[% loc('Already been reported?') %]</h2>
+ <div class="form-section-description">
+ [% IF c.cobrand.is_council %]
+ <p>[% loc('There are similar problems nearby that we’re already aware of, is one of them yours?') %]</p>
+ [% ELSE %]
+ <p>[% loc('We’ve already reported these nearby problems to the council. Is one of them yours?') %]</p>
+ [% END %]
+ </div>
+
+ <ul class="item-list"></ul>
+ <button class="btn btn--block js-hide-duplicate-suggestions">[% loc('Continue – report a new problem') %]</button>
+</div>
+<div class="js-template-get-updates hidden">
+ <div id="alerts" class="get-updates js-alert-list">
+ <p id="rznvy_hint">
+ [% IF c.user_exists %]
+ [% loc('Would you like us to notify you when this problem is updated or fixed?') %]
+ [% ELSE %]
+ [% loc('If you let us know your email address, we’ll notify you when this problem is updated or fixed.') %]
+ [% END %]
+ </p>
+ <input type="hidden" name="id" disabled>
+ <input type="hidden" name="token" value="[% csrf_token %]" disabled>
+ <input type="hidden" name="type" value="updates" disabled>
+ [% IF c.user_exists %]
+ <input type="submit" value="[% loc('Get updates') %]" class="btn btn--block" id="alert_email_button">
+ [% ELSE %]
+ <label for="rznvy_input">[% loc('Your email') %]</label>
+ <div class="form-txt-submit-box">
+ <input type="email" class="form-control" name="rznvy" id="rznvy_input" aria-described-by="rznvy_hint" disabled>
+ <input type="submit" value="[% loc('Get updates') %]" class="btn" id="alert_email_button">
+ </div>
+ [% END %]
+ </div>
+</div>
+[% END %]
diff --git a/templates/web/base/report/new/fill_in_details.html b/templates/web/base/report/new/fill_in_details.html
index 8fa1253da..fa7aabce3 100644
--- a/templates/web/base/report/new/fill_in_details.html
+++ b/templates/web/base/report/new/fill_in_details.html
@@ -4,6 +4,8 @@
SET bodyclass = '';
SET bodyclass = 'mappage';
SET bodyclass = bodyclass _ " with-notes" IF sidebar_html;
+ SET extra_js = [];
+ PROCESS "report/photo-js.html";
PROCESS "maps/${map.type}.html" IF report.used_map;
INCLUDE 'header.html', title => loc('Reporting a problem');
%]
diff --git a/templates/web/base/report/new/form_report.html b/templates/web/base/report/new/form_report.html
index 39e29c723..a5b378641 100644
--- a/templates/web/base/report/new/form_report.html
+++ b/templates/web/base/report/new/form_report.html
@@ -7,6 +7,7 @@
[% PROCESS "report/new/category_wrapper.html" %]
[% TRY %][% PROCESS 'report/new/after_category.html' %][% CATCH file %][% END %]
+[% PROCESS "report/new/duplicate_suggestions.html" %]
<div class="js-hide-if-invalid-category">
[% TRY %][% PROCESS 'report/new/_form_labels.html' %][% CATCH file %][% END %]
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;
}