Skip to content

Commit 7e99838

Browse files
authored
Merge pull request #292 from cmu-delphi/staging
Staging
2 parents 54d888e + 619e3c5 commit 7e99838

File tree

8 files changed

+363
-58
lines changed

8 files changed

+363
-58
lines changed

src/alternative_interface/views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ def alternative_interface_view(request):
9494
"selected_geography": geography_filter,
9595
"pathogens": pathogens,
9696
"indicators": indicators,
97-
"available_geos": get_available_geos(indicators),
9897
"chart_data": (
9998
get_chart_data(indicators, geography_filter)
10099
if geography_filter

src/assets/css/alter_dashboard.css

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ body {
150150
background-color: rgba(255, 255, 255, 0.95);
151151
font-weight: 500;
152152
transition: var(--transition);
153+
appearance: none;
154+
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8"><path fill="%2364748b" d="M6 8L0 0h12z"/></svg>');
155+
background-repeat: no-repeat;
156+
background-position: right 0.75rem center;
157+
background-size: 0.7rem;
158+
padding-right: 2.25rem;
153159
}
154160

155161
.filter-select:focus {
@@ -159,6 +165,42 @@ body {
159165
outline: none;
160166
}
161167

168+
/* Select2 styling to match filter select */
169+
.select2-container--default .select2-selection--single {
170+
min-height: 48px;
171+
border: 1px solid rgba(255, 255, 255, 0.3);
172+
border-radius: var(--border-radius);
173+
background-color: rgba(255, 255, 255, 0.95);
174+
font-weight: 500;
175+
font-size: 1rem;
176+
transition: var(--transition);
177+
display: flex;
178+
align-items: center;
179+
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8"><path fill="%2364748b" d="M6 8L0 0h12z"/></svg>');
180+
background-repeat: no-repeat;
181+
background-position: right 0.75rem center;
182+
background-size: 0.7rem;
183+
}
184+
185+
.select2-container--default .select2-selection--single .select2-selection__rendered {
186+
padding-left: 1rem;
187+
padding-right: 2.25rem;
188+
line-height: 1.5;
189+
color: var(--dark-text);
190+
font-size: 1rem;
191+
}
192+
193+
.select2-container--default .select2-selection--single .select2-selection__arrow {
194+
display: none;
195+
}
196+
197+
.select2-container--default.select2-container--focus .select2-selection--single,
198+
.select2-container--default.select2-container--open .select2-selection--single {
199+
background-color: white;
200+
border-color: rgba(255, 255, 255, 0.5);
201+
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.2);
202+
}
203+
162204

163205
.hero-title {
164206
font-size: 3rem;
@@ -475,6 +517,21 @@ footer {
475517
.filter-select {
476518
padding: 0.625rem 0.875rem;
477519
font-size: 0.875rem;
520+
background-position: right 0.65rem center;
521+
background-size: 0.65rem;
522+
padding-right: 2rem;
523+
}
524+
525+
.select2-container--default .select2-selection--single {
526+
min-height: 42px;
527+
background-position: right 0.65rem center;
528+
background-size: 0.65rem;
529+
}
530+
531+
.select2-container--default .select2-selection--single .select2-selection__rendered {
532+
padding-left: 0.875rem;
533+
padding-right: 2rem;
534+
font-size: 0.875rem;
478535
}
479536

480537
.chart-section {

src/assets/js/alter_dashboard.js

Lines changed: 112 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ class TypingAnimation {
171171
this.changeHandler = null;
172172
this.focusHandler = null;
173173
this.blurHandler = null;
174+
this.select2OpenHandler = null;
175+
this.select2CloseHandler = null;
176+
this.select2SelectionElement = null;
177+
this.select2Container = null;
178+
this.isSelect2 = false;
174179
this.currentIndex = 0;
175180
this.currentText = '';
176181
this.isDeleting = false;
@@ -187,11 +192,18 @@ class TypingAnimation {
187192
}
188193

189194
this.cleanup();
195+
this.isSelect2 = this.selectElement.classList.contains('select2-hidden-accessible');
196+
this.select2Container = this.selectElement.nextElementSibling && this.selectElement.nextElementSibling.classList.contains('select2')
197+
? this.selectElement.nextElementSibling
198+
: null;
199+
this.select2SelectionElement = this.select2Container
200+
? this.select2Container.querySelector('.select2-selection')
201+
: null;
190202
this.setupHandlers();
191203
this.checkSelection();
192204

193205
this.initTimeoutId = setTimeout(() => {
194-
if (!this.selectElement.value && document.activeElement !== this.selectElement && !this.selectElement.disabled) {
206+
if (!this.selectElement.value && !this.isFocused() && !this.selectElement.disabled) {
195207
this.typingElement.style.display = 'block';
196208
this.typingElement.textContent = '|';
197209
this.typeCharacter();
@@ -220,6 +232,20 @@ class TypingAnimation {
220232
if (this.blurHandler) {
221233
this.selectElement.removeEventListener('blur', this.blurHandler);
222234
}
235+
if (this.select2OpenHandler && window.jQuery) {
236+
window.jQuery(this.selectElement).off('select2:open', this.select2OpenHandler);
237+
}
238+
if (this.select2CloseHandler && window.jQuery) {
239+
window.jQuery(this.selectElement).off('select2:close', this.select2CloseHandler);
240+
}
241+
if (this.select2SelectionElement) {
242+
if (this.focusHandler) {
243+
this.select2SelectionElement.removeEventListener('focus', this.focusHandler);
244+
}
245+
if (this.blurHandler) {
246+
this.select2SelectionElement.removeEventListener('blur', this.blurHandler);
247+
}
248+
}
223249
}
224250

225251
if (this.typingElement) {
@@ -230,6 +256,41 @@ class TypingAnimation {
230256
this.currentIndex = 0;
231257
}
232258

259+
isFocused() {
260+
if (!this.selectElement) return false;
261+
if (this.isSelect2 && this.select2Container) {
262+
if (this.select2Container.classList.contains('select2-container--open')) {
263+
return true;
264+
}
265+
return this.select2Container.contains(document.activeElement);
266+
}
267+
return document.activeElement === this.selectElement;
268+
}
269+
270+
getDisplayText(name) {
271+
if (name === null || name === undefined) {
272+
return '';
273+
}
274+
if (typeof name === 'string' || typeof name === 'number') {
275+
return String(name);
276+
}
277+
if (typeof name === 'object') {
278+
if (typeof name.text === 'string' || typeof name.text === 'number') {
279+
return String(name.text);
280+
}
281+
if (typeof name.name === 'string' || typeof name.name === 'number') {
282+
return String(name.name);
283+
}
284+
if (typeof name.label === 'string' || typeof name.label === 'number') {
285+
return String(name.label);
286+
}
287+
if (typeof name.id === 'string' || typeof name.id === 'number') {
288+
return String(name.id);
289+
}
290+
}
291+
return '';
292+
}
293+
233294
setupHandlers() {
234295
this.changeHandler = () => this.checkSelection();
235296
this.focusHandler = () => {
@@ -249,6 +310,16 @@ class TypingAnimation {
249310
this.selectElement.addEventListener('change', this.changeHandler);
250311
this.selectElement.addEventListener('focus', this.focusHandler);
251312
this.selectElement.addEventListener('blur', this.blurHandler);
313+
if (this.isSelect2 && window.jQuery) {
314+
this.select2OpenHandler = () => this.focusHandler();
315+
this.select2CloseHandler = () => this.blurHandler();
316+
window.jQuery(this.selectElement).on('select2:open', this.select2OpenHandler);
317+
window.jQuery(this.selectElement).on('select2:close', this.select2CloseHandler);
318+
}
319+
if (this.select2SelectionElement) {
320+
this.select2SelectionElement.addEventListener('focus', this.focusHandler);
321+
this.select2SelectionElement.addEventListener('blur', this.blurHandler);
322+
}
252323
}
253324
}
254325

@@ -270,7 +341,7 @@ class TypingAnimation {
270341
clearTimeout(this.timeoutId);
271342
this.timeoutId = null;
272343
}
273-
} else if (document.activeElement !== this.selectElement) {
344+
} else if (!this.isFocused()) {
274345
this.typingElement.style.display = 'block';
275346
if (!this.timeoutId) {
276347
this.currentText = '';
@@ -291,7 +362,12 @@ class TypingAnimation {
291362
return;
292363
}
293364

294-
const currentName = names[this.currentIndex];
365+
const currentName = this.getDisplayText(names[this.currentIndex]);
366+
if (!currentName) {
367+
this.currentIndex = (this.currentIndex + 1) % names.length;
368+
this.timeoutId = setTimeout(() => this.typeCharacter(), PAUSE_AFTER_DELETE);
369+
return;
370+
}
295371

296372
if (this.selectElement.value) {
297373
if (this.typingElement) {
@@ -1233,48 +1309,46 @@ async function loadAvailableGeographies(pathogen = '', preservedGeography = '')
12331309
}
12341310

12351311
const data = await response.json();
1236-
geographySelect.innerHTML = '<option value=""></option>';
1312+
console.log(data.available_geos);
1313+
12371314

12381315
if (data && data.available_geos) {
1239-
const geos = data.available_geos;
1240-
let allGeoNames = [];
1241-
1242-
geos.forEach(group => {
1243-
const optgroup = document.createElement('optgroup');
1244-
optgroup.label = group.text;
1245-
1246-
if (group.children && Array.isArray(group.children)) {
1247-
group.children.forEach(child => {
1248-
const option = document.createElement('option');
1249-
option.value = child.id;
1250-
option.textContent = child.text;
1251-
optgroup.appendChild(option);
1252-
1253-
allGeoNames.push(child.text);
1254-
});
1255-
}
1256-
1257-
geographySelect.appendChild(optgroup);
1316+
geographySelect.innerHTML = '<option value=""></option>';
1317+
1318+
$("#geographySelect").select2({
1319+
data: data.available_geos,
1320+
minimumInputLength: 0,
1321+
maximumSelectionLength: 5,
1322+
width: '100%',
1323+
placeholder: '',
1324+
allowClear: false,
12581325
});
1326+
$("#geographySelect").val('').trigger('change.select2');
12591327

1260-
if (allGeoNames.length > 0) {
1261-
// Randomize names for typing animation
1262-
// Optimized: Partial shuffle to get just 50 random items
1263-
const count = Math.min(50, allGeoNames.length);
1264-
for (let i = 0; i < count; i++) {
1265-
const j = i + Math.floor(Math.random() * (allGeoNames.length - i));
1266-
[allGeoNames[i], allGeoNames[j]] = [allGeoNames[j], allGeoNames[i]];
1267-
}
1268-
window.geographyNames = allGeoNames.slice(0, count);
1328+
// Randomize names for typing animation
1329+
// Optimized: Partial shuffle to get just 50 random items
1330+
const count = Math.min(50, data.available_geos.length);
1331+
for (let i = 0; i < count; i++) {
1332+
const j = i + Math.floor(Math.random() * (data.available_geos.length - i));
1333+
[data.available_geos[i], data.available_geos[j]] = [data.available_geos[j], data.available_geos[i]];
12691334
}
1335+
window.geographyNames = data.available_geos.slice(0, count);
12701336

12711337
if (preservedGeography) {
1272-
const optionExists = Array.from(geographySelect.options).some(opt => opt.value === preservedGeography);
1273-
if (optionExists) {
1274-
geographySelect.value = preservedGeography;
1275-
if (typeof handleGeographyChange === 'function') {
1276-
handleGeographyChange();
1277-
}
1338+
const hasGeoOption = (items, id) => {
1339+
if (!Array.isArray(items)) return false;
1340+
return items.some(item => {
1341+
if (!item) return false;
1342+
if (item.id === id) return true;
1343+
if (Array.isArray(item.children)) {
1344+
return hasGeoOption(item.children, id);
1345+
}
1346+
return false;
1347+
});
1348+
};
1349+
1350+
if (hasGeoOption(data.available_geos, preservedGeography)) {
1351+
$("#geographySelect").val(preservedGeography).trigger('change');
12781352
}
12791353
}
12801354
}

src/assets/js/selectedIndicatorsModal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,11 @@ function showNotCoveredGeoWarningMessage(notCoveredIndicators, geoValue) {
147147
var warningMessage = "";
148148
notCoveredIndicators.forEach((indicator) => {
149149
if (currentMode === "epivis") {
150-
warningMessage += `Indicator ${indicator.display_name} is not available for Location ${geoValue.text} <br>`;
150+
warningMessage += `Indicator "${indicator.display_name}" is not available for Location "${geoValue.text}" <br>`;
151151
} else {
152152
var startDate = document.getElementById("start_date").value;
153153
var endDate = document.getElementById("end_date").value;
154-
warningMessage += `Indicator ${indicator.display_name} is not available for Location ${geoValue.text} for the time period from ${startDate} to ${endDate} <br>`;
154+
warningMessage += `Indicator "${indicator.display_name}" is not available for Location "${geoValue.text}" for the time period from "${startDate}" to "${endDate}" <br>`;
155155
}
156156
});
157157
appendAlert(warningMessage, "warning");

0 commit comments

Comments
 (0)