diff --git a/commcare_connect/static/js/project.js b/commcare_connect/static/js/project.js index 83ba289b..ef6e4946 100644 --- a/commcare_connect/static/js/project.js +++ b/commcare_connect/static/js/project.js @@ -24,13 +24,22 @@ window.circle = circle; * @param {Array.<{lng: float, lat: float, precision: float}> visit_data - Visit location data for User */ function addAccuracyCircles(map, visit_data) { - map.on('load', () => { - const visit_accuracy_circles = []; - visit_data.forEach((loc) => { - visit_accuracy_circles.push( - circle([loc.lng, loc.lat], loc.precision, { units: 'meters' }), - ); + const FILL_OPACITY = 0.1; + const OUTLINE_COLOR = '#fcbf49'; + const OUTLINE_WIDTH = 3; + const OUTLINE_OPACITY = 0.5; + + const visit_accuracy_circles = visit_data.map((loc) => + circle([loc.lng, loc.lat], loc.precision, { units: 'meters' }), + ); + + // Check if the source exists, then update or add the source + if (map.getSource('visit_accuracy_circles')) { + map.getSource('visit_accuracy_circles').setData({ + type: 'FeatureCollection', + features: visit_accuracy_circles, }); + } else { map.addSource('visit_accuracy_circles', { type: 'geojson', data: { @@ -45,21 +54,22 @@ function addAccuracyCircles(map, visit_data) { type: 'fill', paint: { 'fill-antialias': true, - 'fill-opacity': 0.3, + 'fill-opacity': FILL_OPACITY, }, }); + // Add the outline layer map.addLayer({ id: 'visit-accuracy-circle-outlines-layer', source: 'visit_accuracy_circles', type: 'line', paint: { - 'line-color': '#fcbf49', - 'line-width': 3, - 'line-opacity': 0.5, + 'line-color': OUTLINE_COLOR, + 'line-width': OUTLINE_WIDTH, + 'line-opacity': OUTLINE_OPACITY, }, }); - }); + } } window.addAccuracyCircles = addAccuracyCircles; @@ -67,16 +77,21 @@ window.addAccuracyCircles = addAccuracyCircles; function addCatchmentAreas(map, catchments) { const ACTIVE_COLOR = '#3366ff'; const INACTIVE_COLOR = '#ff4d4d'; - const CIRCLE_OPACITY = 0.3; + const CIRCLE_OPACITY = 0.15; - map.on('load', () => { - const catchmentCircles = catchments.map((catchment) => - circle([catchment.lng, catchment.lat], catchment.radius, { - units: 'meters', - properties: { active: catchment.active }, - }), - ); + const catchmentCircles = catchments.map((catchment) => + circle([catchment.lng, catchment.lat], catchment.radius, { + units: 'meters', + properties: { active: catchment.active }, + }), + ); + if (map.getSource('catchment_circles')) { + map.getSource('catchment_circles').setData({ + type: 'FeatureCollection', + features: catchmentCircles, + }); + } else { map.addSource('catchment_circles', { type: 'geojson', data: { @@ -105,17 +120,17 @@ function addCatchmentAreas(map, catchments) { 'line-opacity': 0.5, }, }); + } - if (catchments?.length) { - window.Alpine.nextTick(() => { - const legendElement = document.getElementById('legend'); - if (legendElement) { - const legendData = window.Alpine.$data(legendElement); - legendData.show = true; - } - }); - } - }); + if (catchments?.length) { + window.Alpine.nextTick(() => { + const legendElement = document.getElementById('legend'); + if (legendElement) { + const legendData = window.Alpine.$data(legendElement); + legendData.show = true; + } + }); + } } window.addCatchmentAreas = addCatchmentAreas; diff --git a/commcare_connect/templates/opportunity/user_profile.html b/commcare_connect/templates/opportunity/user_profile.html index fe4fd4e0..714a1c68 100644 --- a/commcare_connect/templates/opportunity/user_profile.html +++ b/commcare_connect/templates/opportunity/user_profile.html @@ -22,55 +22,112 @@ -
-
-

{{access.display_name}}

-
{{access.user.username}}
-
-
-
-
{% translate "Phone" %}
-
{{access.user.phone_number}}
-
-
-
{% translate "Learn Progress" %}
-
{{access.learn_progress}}%
-
-
-
{% translate "Total Visits" %}
-
{{access.visit_count}}
+
+
+
+ +
+
+ {{access.display_name|slice:":1"}} +
+

{{access.display_name}}

+
{{access.user.username}}
-
-
{% translate "Last Visit Date" %}
-
{{access.last_visit_date}}
+ + +
+
+
+
+ +
{% translate "Phone" %}
+
{{access.user.phone_number}}
+
+
+
+
+
+
+ +
{% translate "Learn Progress" %}
+
+
+
+
+ {{access.learn_progress}}% +
+
+
+
+
+
+
+ +
{% translate "Total Visits" %}
+
{{access.visit_count}}
+
+
+
+
+
+
+ +
{% translate "Last Visit" %}
+
{{access.last_visit_date}}
+
+
+
-
-
-
-
-
-
Catchment Areas
-
- - Active + + +
+
+
Visit Locations
+
+ + +
-
- - Inactive +
+
+
+
+
Catchment Areas
+
+ + Active +
+
+ + Inactive +
+
+
+ + +
+ {% if access.suspended %} + + {% translate "Revoke Suspension" %} + + {% else %} + + {% endif %}
- {% if access.suspended %} - - {% translate "Revoke Suspension" %} - - {% else %} - - {% endif %}
{% endblock content %} @@ -84,20 +141,43 @@
Catchment Areas
mapboxgl.accessToken = "{{ MAPBOX_TOKEN }}"; const map = new mapboxgl.Map({ container: 'user-visit-map', - style: 'mapbox://styles/mapbox/satellite-streets-v12', + style: 'mapbox://styles/mapbox/streets-v12', center: [{{ lng_avg }}, {{ lat_avg }}], zoom: 14, }); + const userVisits = JSON.parse(document.getElementById('userVisits').textContent); - userVisits.forEach(loc => { - new mapboxgl.Marker() - .setLngLat([loc.lng, loc.lat]) - .setPopup(new mapboxgl.Popup().setHTML(`${loc.entity_name}
${loc.visit_date}`)) - .addTo(map) - }) - addAccuracyCircles(map, userVisits); const userCatchments = JSON.parse(document.getElementById('userCatchments').textContent); - addCatchmentAreas(map, userCatchments) + + map.on('load', () => { + userVisits.forEach(loc => { + new mapboxgl.Marker() + .setLngLat([loc.lng, loc.lat]) + .setPopup(new mapboxgl.Popup().setHTML(`${loc.entity_name}
${loc.visit_date}`)) + .addTo(map) + }); + + addAccuracyCircles(map, userVisits); + addCatchmentAreas(map, userCatchments); + }); + + // Watch for Alpine.js style changes + Alpine.effect(() => { + const alpineData = Alpine.$data(document.querySelector('[x-data]')); + const currentStyle = alpineData.currentStyle; + const styles = { + 'streets-v12': 'mapbox://styles/mapbox/streets-v12', + 'satellite-streets-v12': 'mapbox://styles/mapbox/satellite-streets-v12' + }; + map.setStyle(styles[currentStyle]); + + // Re-add circles and catchments after style changes + map.once('style.load', () => { + alpineData.currentStyle = currentStyle; + addAccuracyCircles(map, userVisits); + addCatchmentAreas(map, userCatchments); + }); + }); }); {% endblock %}