Skip to content

Commit

Permalink
EVENT-826 New Profile Questions (#838)
Browse files Browse the repository at this point in the history
Add Cru Opportunities, Ethnicity & Graduation Date profile questions.
  • Loading branch information
caleballdrin authored Aug 16, 2023
1 parent 0b1e61e commit 5a12dd5
Show file tree
Hide file tree
Showing 19 changed files with 491 additions and 21 deletions.
11 changes: 8 additions & 3 deletions app/scripts/components/FormStatusModal/FormStatusModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const FormStatusModal = ({
>
×
</button>
<h4>Form Status</h4>
<h4>Liability Release Form Status</h4>
</div>
<div className="modal-body tab-content-spacing-above">
<div className="row">
Expand All @@ -92,8 +92,13 @@ const FormStatusModal = ({
</div>
<div className="row">
<div className="col-sm-4">
<label>Form Status:</label>
<p>{eformStatus}</p>
<label>Status:</label>
<p>
{eformStatus
? eformStatus.charAt(0).toUpperCase() +
eformStatus.slice(1).toLowerCase()
: ''}
</p>
</div>
<div className="col-sm-5">
<label>Check Form Status:</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const FormStatusPopover = (): JSX.Element => {
return (
<div className="text-center">
Liability Release Form Status
<button className="btn btn-danger btn-xs" style={{ margin: '0 2px' }}>
Voided/Declined
</button>
Expand Down
15 changes: 15 additions & 0 deletions app/scripts/directives/blockEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,12 @@ angular.module('confRegistrationWebApp').directive('blockEditor', function () {
$scope.typeToProfile.addressQuestion = 'ADDRESS';
$scope.typeToProfile.genderQuestion = 'GENDER';
$scope.typeToProfile.yearInSchoolQuestion = 'YEAR_IN_SCHOOL';
$scope.typeToProfile.opportunitiesQuestion = 'OPPORTUNITIES';
$scope.typeToProfile.birthDateQuestion = 'BIRTH_DATE';
$scope.typeToProfile.campusQuestion = 'CAMPUS';
$scope.typeToProfile.dormitoryQuestion = 'DORMITORY';
$scope.typeToProfile.graduationDateQuestion = 'GRADUATION_DATE';
$scope.typeToProfile.ethnicityQuestion = 'ETHNICITY';

$scope.profileCheck = !_.isNull($scope.block.profileType);
$scope.profileOption = _.has($scope.typeToProfile, $scope.block.type);
Expand Down Expand Up @@ -432,6 +435,18 @@ angular.module('confRegistrationWebApp').directive('blockEditor', function () {
$scope.choiceVisible = function () {
return true;
};

$scope.eventHasQuestionType = function (questionType) {
let questionTypeFound = false;
$scope.conference.registrationPages.forEach(function (page) {
page.blocks.forEach(function (block) {
if (block.type === questionType) {
questionTypeFound = true;
}
});
});
return questionTypeFound;
};
},
};
});
4 changes: 4 additions & 0 deletions app/scripts/directives/blockRegistration.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ angular
case 'selectQuestion':
case 'dateQuestion':
case 'birthDateQuestion':
case 'ethnicityQuestion':
case 'campusQuestion':
case 'opportunitiesQuestion':
case 'graduationDateQuestion':
return blockDefault || '';
case 'numberQuestion':
return blockDefault || null;
Expand Down
114 changes: 114 additions & 0 deletions app/scripts/directives/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ import textQuestionTemplate from 'views/blocks/textQuestion.html';
import genderQuestionTemplate from 'views/blocks/genderQuestion.html';
import dateQuestionTemplate from 'views/blocks/dateQuestion.html';
import yearInSchoolQuestionTemplate from 'views/blocks/yearInSchoolQuestion.html';
import opportunitiesQuestionTemplate from 'views/blocks/opportunitiesQuestion.html';
import textareaQuestionTemplate from 'views/blocks/textareaQuestion.html';
import campusQuestionTemplate from 'views/blocks/campusQuestion.html';
import graduationDateQuestionTemplate from 'views/blocks/graduationDateQuestion.html';
import ethnicityQuestionTemplate from 'views/blocks/ethnicityQuestion.html';

angular.module('confRegistrationWebApp').directive('nameQuestion', function () {
return {
Expand Down Expand Up @@ -207,6 +210,15 @@ angular
return {
templateUrl: yearInSchoolQuestionTemplate,
restrict: 'E',
controller: function ($scope) {
$scope.block.content.staticChoices = [
'Freshman',
'Sophomore',
'Junior',
'Senior',
'Graduate Student',
];
},
};
});

Expand Down Expand Up @@ -242,3 +254,105 @@ angular
},
};
});

angular
.module('confRegistrationWebApp')
.directive('opportunitiesQuestion', function () {
return {
templateUrl: opportunitiesQuestionTemplate,
restrict: 'E',
controller: function ($scope) {
$scope.block.content.staticChoices = [
'Yes, via email',
'Yes, via text',
'Yes, via email & text',
'No',
];
},
};
});

angular
.module('confRegistrationWebApp')
.directive('graduationDateQuestion', function () {
return {
templateUrl: graduationDateQuestionTemplate,
restrict: 'E',
};
});

angular
.module('confRegistrationWebApp')
.directive('ethnicityQuestion', function () {
return {
templateUrl: ethnicityQuestionTemplate,
restrict: 'E',
controller: function ($scope, $timeout) {
$scope.otherSentinel = '__other__';

$scope.block.content.staticChoices = [
'American Indian/Native Alaskan',
'Arab/Middle Eastern',
'Asian/Asian American',
'Black/African American',
'Hispanic/Latino',
'Multi-racial/Multi-ethnic',
'Pacific Islander/Native Hawaiian',
'White/European American',
'Prefer not to answer',
];

$scope.selectOtherAnswer = () => {
if ($scope.otherAnswer) {
$scope.selectedAnswer = $scope.otherSentinel;
}
};

$scope.clearOther = () => {
if ($scope.selectedAnswer != $scope.otherSentinel)
$scope.otherAnswer = '';
};

const answerValue = $scope.answer ? $scope.answer.value : '';
if (
answerValue &&
!_.includes($scope.block.content.staticChoices, answerValue)
) {
// An answer is present that isn't in the list of available choices, so it is an "Other" answer
$scope.selectedAnswer = $scope.otherSentinel;
$scope.otherAnswer = answerValue;
} else {
$scope.selectedAnswer = answerValue;
$scope.otherAnswer = '';
}

// We would use ng-model-options="{ debounce: 1000 }", but that causes the selection to disappear when
// rapidly changing the selected option (https://stackoverflow.com/questions/31254291). To work-around,
// we perform the debounce ourselves. We're writing our own debounce function instead of using _.debounce
// because lodash's implementation uses setTimeout, but we need to use $timeout so that the timeout can
// be manually flushed in tests.
let currentTimeout = null;
const updateAnswerValueDebounced = () => {
if (currentTimeout) {
// Cancel the previous timeout and start a new one
$timeout.cancel(currentTimeout);
}

// Update the answer after one second
currentTimeout = $timeout(() => {
if ($scope.answer) {
$scope.answer.value =
$scope.selectedAnswer === $scope.otherSentinel
? $scope.otherAnswer
: $scope.selectedAnswer;
}
}, 300);
};

$scope.$watchGroup(
['selectedAnswer', 'otherAnswer'],
updateAnswerValueDebounced,
);
},
};
});
40 changes: 34 additions & 6 deletions app/scripts/directives/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,53 @@ angular
scope: {
localModel: '=model',
ngDisabled: '=',
monthYearOnly: '=monthYearOnly',
},
controller: function ($timeout, $scope) {
$scope.updateTimeStamp = function (timestamp) {
$scope.$apply(function () {
$scope.localModel = moment(timestamp).format('YYYY-MM-DD HH:mm:ss');
$scope.localModel = moment(new Date(timestamp)).format(
'YYYY-MM-DD HH:mm:ss',
);
});
};
},
link: function (scope, element) {
var datePickerElement = angular.element(element).find('.datepicker');
var initialDate = scope.localModel
? moment(scope.localModel).format('MM/DD/YYYY hh:mm A')
: null;
var initialDate =
scope.localModel && scope.monthYearOnly
? scope.localModel
: scope.localModel
? moment(new Date(scope.localModel)).format('MM/DD/YYYY hh:mm A')
: null;
let dateOptions = scope.monthYearOnly
? {
viewMode: 'years',
format: 'MMMM YYYY',
defaultDate: initialDate,
useCurrent: false,
keepOpen: false,
allowInputToggle: true,
extraFormats: [
'MM/YY',
'MM/YYYY',
'MM-YY',
'MM-YYYY',
'MMM-YYYY',
'MMMM-YYYY',
],
}
: {
defaultDate: initialDate,
};
datePickerElement
.datetimepicker()
.datetimepicker('defaultDate', initialDate)
.datetimepicker(dateOptions)
.on('dp.change', function (ev) {
scope.updateTimeStamp(ev.date);
});
if (scope.monthYearOnly) {
datePickerElement.find('input')[0].placeholder = 'Month - Year';
}

scope.$on('$destroy', function () {
if (angular.isDefined(datePickerElement.data('DateTimePicker'))) {
Expand Down
26 changes: 25 additions & 1 deletion app/scripts/directives/questionToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ angular
id: 'birthDateQuestion',
defaultTitle: 'Date of Birth',
defaultProfile: 'BIRTH_DATE',
iconClass: 'fa-calendar',
iconClass: 'fa-birthday-cake',
name: 'Date of Birth',
},
{
Expand All @@ -184,6 +184,30 @@ angular
iconClass: 'fa-building',
name: 'Dormitory',
},
{
id: 'opportunitiesQuestion',
defaultTitle:
'Would you like to be informed about new opportunities (like conferences, mission trips, career opportunities, etc.) to be involved with Cru around the U.S. & world?',
defaultProfile: 'OPPORTUNITIES',
defaultExportFieldTitle: 'Cru Opportunities',
iconClass: 'fa-lightbulb-o',
name: 'Cru Opportunities',
},
{
id: 'graduationDateQuestion',
defaultTitle: 'Expected Graduation Date',
defaultProfile: 'GRADUATION_DATE',
iconClass: 'fa-graduation-cap',
name: 'Graduation Date',
},
{
id: 'ethnicityQuestion',
defaultTitle: 'Which race or ethnicity best describes you?',
defaultExportFieldTitle: 'Race/Ethnicity',
defaultProfile: 'ETHNICITY',
iconClass: 'fa-globe',
name: 'Racial/Ethnic Identity',
},
];
},
};
Expand Down
18 changes: 11 additions & 7 deletions app/scripts/directives/rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ angular.module('confRegistrationWebApp').directive('rule', function () {
'dateQuestion',
'genderQuestion',
'yearInSchoolQuestion',
'opportunitiesQuestion',
'ethnicityQuestion',
'graduationDateQuestion',
];

//keep valid block types that can be used in rules
Expand All @@ -129,15 +132,13 @@ angular.module('confRegistrationWebApp').directive('rule', function () {
case 'genderQuestion':
return ['M', 'F'];
case 'yearInSchoolQuestion':
return [
'Freshman',
'Sophomore',
'Junior',
'Senior',
'Graduate Student',
];
return block.content.staticChoices;
case 'numberQuestion':
return block.content.range;
case 'opportunitiesQuestion':
return block.content.staticChoices;
case 'ethnicityQuestion':
return block.content.staticChoices;
default:
return [];
}
Expand Down Expand Up @@ -172,10 +173,13 @@ angular.module('confRegistrationWebApp').directive('rule', function () {
case 'selectQuestion':
case 'radioQuestion':
case 'yearInSchoolQuestion':
case 'opportunitiesQuestion':
case 'ethnicityQuestion':
return 'select';
case 'genderQuestion':
return 'gender';
case 'dateQuestion':
case 'graduationDateQuestion':
return 'date';
case 'numberQuestion':
return 'number';
Expand Down
39 changes: 39 additions & 0 deletions app/views/blocks/ethnicityQuestion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<div class="radio" ng-repeat="choice in block.content.staticChoices">
<label>
<input
type="radio"
show-errors
name="{{ block.id }}"
ng-required="block.required"
ng-model="$parent.selectedAnswer"
ng-value="choice"
ng-change="clearOther()"
/>
<strong>{{ choice }}</strong>
</label>
</div>
<div class="radio">
<label>
<input
type="radio"
show-errors
name="{{ block.id }}"
ng-required="block.required"
ng-model="selectedAnswer"
ng-value="otherSentinel"
/>
<strong>Other</strong>
<input
type="text"
class="form-control"
show-errors
ng-model="otherAnswer"
ng-change="selectOtherAnswer()"
ng-required="selectedAnswer === otherSentinel"
style="display: inline; width: auto"
autocomplete="off"
aria-autocomplete="none"
ng-show="selectedAnswer == otherSentinel"
/>
</label>
</div>
Loading

0 comments on commit 5a12dd5

Please sign in to comment.