-
{{@field}}
+
\ No newline at end of file
diff --git a/app/components/word-cloud.hbs b/app/components/word-cloud.hbs
new file mode 100644
index 00000000..9752b6f7
--- /dev/null
+++ b/app/components/word-cloud.hbs
@@ -0,0 +1,6 @@
+{{! word cloud forms in this component with the help of d3.js }}
+{{#if @isWordCloud}}
+
+
+
+{{/if}}
\ No newline at end of file
diff --git a/app/components/word-cloud.js b/app/components/word-cloud.js
new file mode 100644
index 00000000..e0824ed5
--- /dev/null
+++ b/app/components/word-cloud.js
@@ -0,0 +1,24 @@
+import Component from '@glimmer/component';
+import { inject as service } from '@ember/service';
+import { registerDestructor } from '@ember/destroyable';
+
+const SHOW_WORD_CLOUD_AFTER_TIME = 1000; //time in milliseconds
+export default class WordCloudComponent extends Component {
+ @service survey;
+ @service fastboot;
+
+ constructor() {
+ super(...arguments);
+ let timeout;
+
+ if (!this.fastboot.isFastBoot) {
+ timeout = setTimeout(() => {
+ this.survey.showWordCloud();
+ }, SHOW_WORD_CLOUD_AFTER_TIME);
+ }
+
+ registerDestructor(this, () => {
+ clearTimeout(timeout);
+ });
+ }
+}
diff --git a/app/constants/live.js b/app/constants/live.js
index 501cfcc5..02dc92ea 100644
--- a/app/constants/live.js
+++ b/app/constants/live.js
@@ -39,3 +39,12 @@ export const EVENTS_LOGS_POLL_TIME = 40000;
export const EVENTS_LOGS_TYPE = {
EVENTS_REMOVE_PEER: 'EVENTS_REMOVE_PEER',
};
+
+export const QUESTION_DEBOUNCE_TIME = 200;
+export const QUESTION_MIN_LENGTH = 2;
+export const ANSWER_MIN_LENGTH = 2;
+export const ANSWER_STATUS = {
+ PENDING: 'PENDING',
+ APPROVED: 'APPROVED',
+ REJECTED: 'REJECTED',
+};
diff --git a/app/controllers/live.js b/app/controllers/live.js
index e432b0a8..718d0e55 100644
--- a/app/controllers/live.js
+++ b/app/controllers/live.js
@@ -4,18 +4,30 @@ import { tracked } from '@glimmer/tracking';
import { inject as service } from '@ember/service';
import { getOwner } from '@ember/application';
import { globalRef } from 'ember-ref-bucket';
-import { ROLES, BUTTONS_TYPE } from '../constants/live';
+import { registerDestructor } from '@ember/destroyable';
+import {
+ ROLES,
+ BUTTONS_TYPE,
+ ANSWER_STATUS,
+ ANSWER_MIN_LENGTH,
+} from '../constants/live';
import { TOAST_OPTIONS } from '../constants/toast-options';
+import { APPS } from '../constants/urls';
export default class LiveController extends Controller {
queryParams = ['dev'];
ROLES = ROLES;
+ @service featureFlag;
@service login;
@service toast;
+ @service fastboot;
+ @service survey;
+ answerEventSource;
+ questionEventSource;
@tracked TABS = [
{ id: 1, label: 'Screenshare', active: true },
- { id: 2, label: 'Previous Events', active: false },
- { id: 3, label: 'Real Dev Squad', active: false },
+ { id: 2, label: 'Survey', active: false },
{ id: 4, label: 'Logs', active: false },
+ { id: 3, label: 'More', active: false },
];
@tracked activeTab = 'Screenshare';
@tracked isLoading = false;
@@ -30,6 +42,17 @@ export default class LiveController extends Controller {
@tracked newRoomCode = '';
@tracked isActiveEventFound;
@tracked buttonText = '';
+ @tracked isAnswerReplyModalOpen = false;
+ @tracked answerValue = '';
+ @tracked answerValidationDetails = {
+ isError: false,
+ isHelperTextVisible: true,
+ helperText: `Minimum character limit is ${ANSWER_MIN_LENGTH} characters`,
+ };
+ @tracked answerSubmitButtonState = {
+ isDisabled: true,
+ isLoading: false,
+ };
@globalRef('videoEl') videoEl;
get liveService() {
return getOwner(this).lookup('service:live');
@@ -37,9 +60,24 @@ export default class LiveController extends Controller {
constructor() {
super(...arguments);
+
+ if (!this.fastboot.isFastBoot) {
+ const queryParams = new URLSearchParams(window.location.search);
+ const isWordCloudFeatureOn = queryParams.get('wordCloud') === 'true';
+
+ if (isWordCloudFeatureOn) {
+ this.questionSSEListener();
+ this.answerSSEListener();
+ }
+ }
setTimeout(() => {
this.isLoading = false;
}, 4000);
+
+ registerDestructor(this, () => {
+ this.questionEventSource?.close();
+ this.answerEventSource?.close();
+ });
}
@action inputHandler(type, event) {
@@ -136,6 +174,82 @@ export default class LiveController extends Controller {
this.isWarningModalOpen = !this.isWarningModalOpen;
}
+ @action openAnswerReplyModal() {
+ this.isAnswerReplyModalOpen = true;
+ }
+
+ @action closeAnswerReplyModal() {
+ this.isAnswerReplyModalOpen = false;
+ }
+
+ @action onAnswerInput(event) {
+ const maxCharacters = this.survey.recentQuestion.max_characters;
+
+ this.answerValue = event.target.value;
+ const answerLength = this.answerValue.trim().length;
+ const isAnswerEqualToMinLength = answerLength >= ANSWER_MIN_LENGTH;
+
+ if (!isAnswerEqualToMinLength) {
+ this.answerValidationDetails.helperText = `Minimum character limit is ${ANSWER_MIN_LENGTH} characters`;
+ this.answerValidationDetails.isHelperTextVisible = true;
+
+ this.answerValidationDetails = this.answerValidationDetails;
+
+ this.answerSubmitButtonState.isDisabled = true;
+ this.answerSubmitButtonState = this.answerSubmitButtonState;
+
+ return;
+ }
+
+ if (maxCharacters === null) {
+ this.resetAnswerValidators();
+ return;
+ }
+
+ if (this.answerValue.trim().length > maxCharacters) {
+ this.answerValidationDetails.isError = true;
+ this.answerValidationDetails.helperText = `Maximum character limit is ${maxCharacters} characters`;
+ this.answerValidationDetails.isHelperTextVisible = true;
+ this.answerValidationDetails = this.answerValidationDetails;
+
+ this.answerSubmitButtonState.isDisabled = true;
+ this.answerSubmitButtonState = this.answerSubmitButtonState;
+ } else {
+ this.resetAnswerValidators();
+ }
+ }
+
+ @action async submitAnswer() {
+ this.answerSubmitButtonState.isLoading = true;
+ this.answerSubmitButtonState.isDisabled = true;
+
+ this.answerSubmitButtonState = this.answerSubmitButtonState;
+
+ const answerBody = {
+ answer: this.answerValue.trim(),
+ answeredBy: this.liveService.localPeer?.id,
+ eventId: this.liveService?.activeRoomId,
+ questionId: this.survey.recentQuestion?.id,
+ };
+
+ const { error } = await this.survey.answerSubmitHandler(answerBody);
+
+ if (!error) {
+ this.isAnswerReplyModalOpen = false;
+ this.answerSubmitButtonState.isLoading = false;
+ this.answerSubmitButtonState.isDisabled = false;
+ this.answerSubmitButtonState = this.answerSubmitButtonState;
+ }
+ }
+
+ @action async onAnswerReject(id) {
+ this.survey.answerRejectHandler(id);
+ }
+
+ @action async onAnswerApprove(id) {
+ this.survey.answerApproveHandler(id);
+ }
+
@action buttonClickHandler(buttonId) {
switch (buttonId) {
case BUTTONS_TYPE.SCREEN_SHARE:
@@ -172,4 +286,85 @@ export default class LiveController extends Controller {
this.newRoomCode = '';
}
}
+
+ resetAnswerValidators() {
+ this.answerValidationDetails.isError = false;
+ this.answerValidationDetails.helperText = '';
+ this.answerValidationDetails.isHelperTextVisible = false;
+ this.answerValidationDetails = this.answerValidationDetails;
+
+ this.answerSubmitButtonState.isDisabled = false;
+ this.answerSubmitButtonState = this.answerSubmitButtonState;
+ }
+ questionSSEListener() {
+ const event = new EventSource(`${APPS.API_BACKEND}/questions`);
+ this.questionEventSource = event;
+
+ event.onmessage = async (event) => {
+ const parsedQuestion = JSON.parse(event.data);
+ const question = parsedQuestion || {};
+
+ const isQuestionChanged = question?.id !== this.survey.recentQuestion?.id;
+
+ this.survey.setRecentQuestion(question);
+
+ this.answerValue = '';
+ this.answerValidationDetails.isError = false;
+ this.answerValidationDetails.helperText = `Minimum character limit is ${ANSWER_MIN_LENGTH} characters`;
+ this.answerValidationDetails.isHelperTextVisible = true;
+ this.answerValidationDetails = this.answerValidationDetails;
+
+ if (isQuestionChanged) {
+ this.answerEventSource?.close();
+ this.answerSSEListener();
+ }
+
+ if (
+ question &&
+ this.liveService.isJoined &&
+ this.liveService.localPeer.roleName !== this.ROLES.host
+ ) {
+ this.isAnswerReplyModalOpen = true;
+ }
+ };
+
+ event.onerror = (event) => {
+ console.error(event);
+ };
+ }
+
+ answerSSEListener() {
+ const localPeerRole = this.liveService.localPeer?.roleName;
+ const isHost = localPeerRole === this.ROLES.host;
+ const isModerator = localPeerRole === this.ROLES.moderator;
+ const activeEventId = this.liveService?.activeRoomId;
+ let answersEventStreamURL = '';
+
+ if (isHost || isModerator) {
+ answersEventStreamURL = `${APPS.API_BACKEND}/answers?eventId=${activeEventId}&questionId=${this.survey.recentQuestion?.id}`;
+ } else {
+ answersEventStreamURL = `${APPS.API_BACKEND}/answers?eventId=${activeEventId}&questionId=${this.survey.recentQuestion?.id}&status=${ANSWER_STATUS.APPROVED}`;
+ }
+
+ const event = new EventSource(answersEventStreamURL);
+ this.answerEventSource = event;
+
+ event.onmessage = async (event) => {
+ const parsedAnswers = JSON.parse(event.data);
+ const answers = parsedAnswers || [];
+
+ if (isHost || isModerator) {
+ this.survey.setAnswers(answers);
+ } else {
+ this.survey.setApprovedAnswers(answers);
+ }
+ this.survey.showWordCloud();
+
+ console.log('answerSSEListener answers ', answers);
+ };
+
+ event.onerror = (event) => {
+ console.error(event);
+ };
+ }
}
diff --git a/app/d3/word-cloud.js b/app/d3/word-cloud.js
new file mode 100644
index 00000000..e1dcdd8b
--- /dev/null
+++ b/app/d3/word-cloud.js
@@ -0,0 +1,122 @@
+import cloud from 'd3-cloud';
+import { select } from 'd3-selection';
+
+const colors = [
+ '#FF6633',
+ '#FFB399',
+ '#FF33FF',
+ '#FFFF99',
+ '#00B3E6',
+ '#E6B333',
+ '#3366E6',
+ '#999966',
+ '#99FF99',
+ '#B34D4D',
+ '#80B300',
+ '#809900',
+ '#E6B3B3',
+ '#6680B3',
+ '#66991A',
+ '#FF99E6',
+ '#CCFF1A',
+ '#FF1A66',
+ '#E6331A',
+ '#33FFCC',
+ '#66994D',
+ '#B366CC',
+ '#4D8000',
+ '#B33300',
+ '#CC80CC',
+ '#66664D',
+ '#991AFF',
+ '#E666FF',
+ '#4DB3FF',
+ '#1AB399',
+ '#E666B3',
+ '#33991A',
+ '#CC9999',
+ '#B3B31A',
+ '#00E680',
+ '#4D8066',
+ '#809980',
+ '#E6FF80',
+ '#1AFF33',
+ '#999933',
+ '#FF3380',
+ '#CCCC00',
+ '#66E64D',
+ '#4D80CC',
+ '#9900B3',
+ '#E64D66',
+ '#4DB380',
+ '#FF4D4D',
+ '#99E6E6',
+ '#6666FF',
+];
+
+const defaultSize = {
+ x: 500,
+ y: 400,
+};
+function generateWordCloud(words, elementSelector, size = defaultSize) {
+ const container = select(elementSelector);
+ container.selectAll('*').remove();
+
+ var layout = cloud()
+ .size([size.x, size.y])
+ .words(
+ words?.map(function (d) {
+ return {
+ text: d,
+ size: 10 + Math.random() * 37,
+ color: colors[Math.floor(Math.random() * colors.length)],
+ };
+ }),
+ )
+ .padding(5)
+ .rotate(function () {
+ return ~~(Math.random() * 2) * 90;
+ })
+ .font('raleway')
+ .fontSize(function (d) {
+ return d.size;
+ })
+ .timeInterval(100)
+ .on('end', draw);
+
+ function draw(words) {
+ const container = select(elementSelector);
+
+ container
+ .append('svg')
+ .attr('width', layout.size()[0])
+ .attr('height', layout.size()[1])
+ .append('g')
+ .attr(
+ 'transform',
+ 'translate(' + layout.size()[0] / 2 + ',' + layout.size()[1] / 2 + ')',
+ )
+ .selectAll('text')
+ .data(words)
+ .enter()
+ .append('text')
+ .style('font-size', function (d) {
+ return d.size + 'px';
+ })
+ .style('fill', function (d) {
+ return d.color;
+ })
+ .style('font-family', 'Impact')
+ .attr('text-anchor', 'middle')
+ .attr('transform', function (d) {
+ return 'translate(' + [d.x, d.y] + ')rotate(' + d.rotate + ')';
+ })
+ .text(function (d) {
+ return d.text;
+ });
+ }
+
+ //layout started
+ layout.start();
+}
+export { generateWordCloud };
diff --git a/app/services/feature-flag.js b/app/services/feature-flag.js
index 87fa2ec7..6ee6188e 100644
--- a/app/services/feature-flag.js
+++ b/app/services/feature-flag.js
@@ -7,4 +7,9 @@ export default class FeatureFlagService extends Service {
const queryParams = this.router?.currentRoute?.queryParams;
return queryParams?.dev === 'true';
}
+
+ get isWordCloud() {
+ const queryParams = this.router?.currentRoute?.queryParams;
+ return queryParams?.wordCloud === 'true';
+ }
}
diff --git a/app/services/live.js b/app/services/live.js
index 92c9fd8b..d232e848 100644
--- a/app/services/live.js
+++ b/app/services/live.js
@@ -327,6 +327,7 @@ export default class LiveService extends Service {
});
const peer = this.hmsStore.getState(selectLocalPeer);
this.localPeer = peer;
+ this.activeRoomId = roomId;
const addedPeerData = await this.addPeer(roomId, peer);
if (addedPeerData) {
this.toast.success(
@@ -346,6 +347,7 @@ export default class LiveService extends Service {
});
const peer = this.hmsStore.getState(selectLocalPeer);
this.localPeer = peer;
+ this.activeRoomId = roomId;
const addedPeerData = await this.addPeer(roomId, peer);
if (addedPeerData) {
this.toast.success(
diff --git a/app/services/survey.js b/app/services/survey.js
new file mode 100644
index 00000000..1c0b921c
--- /dev/null
+++ b/app/services/survey.js
@@ -0,0 +1,164 @@
+import Service, { inject as service } from '@ember/service';
+import { registerDestructor } from '@ember/destroyable';
+import { tracked } from '@glimmer/tracking';
+import { TOAST_OPTIONS } from '../constants/toast-options';
+import { APPS } from '../constants/urls';
+import {
+ ANSWER_STATUS,
+ API_METHOD,
+ PATCH_API_CONFIGS,
+ ROLES,
+} from '../constants/live';
+import { generateWordCloud } from '../d3/word-cloud';
+
+export default class SurveyService extends Service {
+ @service router;
+ @service toast;
+ @service live;
+ @service fastboot;
+ @tracked answers = [];
+ @tracked approvedAnswers = [];
+ @tracked recentQuestion;
+ @tracked screenWidth;
+
+ constructor() {
+ super(...arguments);
+
+ const onResize = () => {
+ this.screenWidth = window.innerWidth;
+ this.showWordCloud();
+ };
+
+ if (!this.fastboot.isFastBoot) {
+ this.screenWidth = window.innerWidth;
+ window.addEventListener('resize', onResize);
+ registerDestructor(this, () => {
+ window.removeEventListener('resize', onResize);
+ });
+ }
+ }
+ setApprovedAnswers(approvedAnswers) {
+ this.approvedAnswers = approvedAnswers;
+ }
+
+ setAnswers(answers) {
+ this.answers = answers;
+ }
+
+ setRecentQuestion(question) {
+ this.recentQuestion = question;
+ }
+
+ async answerSubmitHandler(payload) {
+ let error = null,
+ answer = null;
+
+ try {
+ const answerResponse = await fetch(`${APPS.API_BACKEND}/answers`, {
+ method: API_METHOD.POST,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+
+ body: JSON.stringify(payload),
+ });
+ answer = await answerResponse.json();
+
+ if (!answerResponse.ok)
+ return this.toast.error(answer.message, answer.error, TOAST_OPTIONS);
+
+ this.toast.success(answer.message, answer.error, TOAST_OPTIONS);
+
+ return { error: error, result: answer };
+ } catch (error) {
+ console.error('Error while submitting answer: ', error);
+ return { error: error, result: null };
+ }
+ }
+ async answerApproveHandler(id) {
+ const approvalPayload = {
+ status: ANSWER_STATUS.APPROVED,
+ };
+
+ try {
+ const approveResponse = await fetch(`${APPS.API_BACKEND}/answers/${id}`, {
+ ...PATCH_API_CONFIGS,
+ body: JSON.stringify(approvalPayload),
+ });
+
+ if (!approveResponse.ok) throw new Error();
+
+ this.toast.success(
+ 'Answer approved successfully',
+ 'Success',
+ TOAST_OPTIONS,
+ );
+ } catch (error) {
+ console.error('Error while approving answer: ', error);
+ this.toast.error('Error while approving answer', 'Error', TOAST_OPTIONS);
+ }
+ }
+
+ async answerRejectHandler(id) {
+ const rejectionPayload = {
+ status: ANSWER_STATUS.REJECTED,
+ };
+ try {
+ const rejectResponse = await fetch(`${APPS.API_BACKEND}/answers/${id}`, {
+ ...PATCH_API_CONFIGS,
+ body: JSON.stringify(rejectionPayload),
+ });
+
+ if (!rejectResponse.ok) throw new Error();
+
+ this.toast.success(
+ 'Answer rejected successfully',
+ 'Success',
+ TOAST_OPTIONS,
+ );
+ } catch (error) {
+ console.error('Error while rejecting answer: ', error);
+ this.toast.error('Error while rejecting answer', 'Error', TOAST_OPTIONS);
+ }
+ }
+
+ getFilteredApprovedAnswersArray() {
+ const isHost = this.live.localPeer?.roleName === ROLES.host;
+ const isModerator = this.live.localPeer?.roleName === ROLES.moderator;
+ const filteredApprovedAnswersArray = [];
+
+ if (isHost || isModerator) {
+ const filteredApprovedAnswers = this.answers?.filter(
+ (answer) => answer.status === ANSWER_STATUS.APPROVED,
+ );
+ filteredApprovedAnswers?.forEach((answer) => {
+ filteredApprovedAnswersArray.push(answer.answer);
+ });
+ return filteredApprovedAnswersArray;
+ }
+
+ this.approvedAnswers?.forEach((answer) => {
+ filteredApprovedAnswersArray.push(answer.answer);
+ });
+ return filteredApprovedAnswersArray;
+ }
+
+ showWordCloud() {
+ const element = '.word-cloud';
+ const words = this.getFilteredApprovedAnswersArray();
+
+ let wordCloudSize = {
+ x: this.screenWidth,
+ y: this.screenWidth,
+ };
+
+ if (!words?.length) return;
+
+ //for mobile/small screens
+ if (this.screenWidth < 500)
+ return generateWordCloud(words, element, wordCloudSize);
+
+ // for screen >=500px
+ generateWordCloud(words, element);
+ }
+}
diff --git a/app/styles/answer-reply-modal.module.css b/app/styles/answer-reply-modal.module.css
new file mode 100644
index 00000000..107bd4c7
--- /dev/null
+++ b/app/styles/answer-reply-modal.module.css
@@ -0,0 +1,76 @@
+.answer-reply-modal {
+ width: 31.25rem;
+ box-sizing: border-box;
+ background-color: var(--color-white);
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ max-width: 96svw;
+ border-radius: 25px;
+ padding: 1.25rem;
+ padding-right: 40px;
+}
+
+.answer-reply-modal__heading {
+ text-align: center;
+ color: var(--color-pink);
+ font-size: 1.5rem;
+ text-decoration: underline;
+ font-style: normal;
+ font-weight: 800;
+ line-height: normal;
+ margin-bottom: 40px;
+}
+
+.answer-reply-modal__answer-label {
+ font-size: 1.2rem;
+ font-weight: 600;
+ color: var(--color-navyblue);
+}
+
+.answer-reply-modal__answer-input {
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ font-size: 1.2rem;
+ border: 2px solid var(--color-navyblue);
+ border-radius: 10px;
+ margin-top: 10px;
+}
+
+.answer-reply-modal__answer-input:focus {
+ outline: none;
+ box-shadow: 0 0 0 4px var(--color-light-navyblue);
+}
+
+.answer-reply-modal__info-text {
+ font-size: 0.8rem;
+ color: var(--color-lightgrey);
+}
+
+.answer-reply-modal__info-icon {
+ border: 1px solid var(--color-darkgrey);
+ padding: 1px 7px;
+ border-radius: 50%;
+ font-size: 0.7rem;
+ color: var(--color-darkgrey);
+}
+
+.answer-reply-modal__actions {
+ text-align: center;
+}
+
+/* media queries */
+@media only screen and (width <= 425px) {
+ .answer-reply-modal__submit-button,
+ .answer-reply-modal__cancel-button {
+ margin-right: 0;
+ width: fit-content;
+ padding: 0 20px;
+ }
+
+ .answer-reply-modal__submit-button {
+ padding: 0 34px;
+ }
+}
diff --git a/app/styles/answer-view-card.module.css b/app/styles/answer-view-card.module.css
new file mode 100644
index 00000000..936a0422
--- /dev/null
+++ b/app/styles/answer-view-card.module.css
@@ -0,0 +1,48 @@
+.answer-view-card {
+ box-shadow: 0 0 4px 0 var(--color-lightgrey);
+ min-height: 5rem;
+ height: fit-content;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex: 3 1;
+ border-radius: 10px;
+ box-sizing: border-box;
+ padding: 1rem;
+}
+
+.answer-view-card__text {
+ width: 80%;
+}
+
+.answer-view-card__read-more-button {
+ all: unset;
+ color: var(--color-pink);
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.answer-view-card--pending {
+ background-color: #ffffe0;
+}
+
+.answer-view-card--approved {
+ background-color: #98fb98;
+}
+
+.answer-view-card--rejected {
+ background-color: var(--color-pink-low-opacity);
+}
+
+@media (width <=1024px) {
+ .answer-view-card {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .answer-view-card__text {
+ width: 100%;
+ padding-bottom: 10px;
+ border-bottom: 1px solid var(--color-lightgrey);
+ }
+}
diff --git a/app/styles/app.css b/app/styles/app.css
index 090c4937..459dc41c 100644
--- a/app/styles/app.css
+++ b/app/styles/app.css
@@ -31,6 +31,11 @@
@import url("identity-card.module.css");
@import url("wheel-animations.css");
@import url("goto.module.css");
+@import url("survey-page.module.css");
+@import url("ask-question-modal.module.css");
+@import url("answer-reply-modal.module.css");
+@import url("answer-view-card.module.css");
+@import url("word-cloud.module.css");
@import url("event-card.module.css");
@import url("status-card.module.css");
@import url("tooltip.module.css");
diff --git a/app/styles/ask-question-modal.module.css b/app/styles/ask-question-modal.module.css
new file mode 100644
index 00000000..9f859f30
--- /dev/null
+++ b/app/styles/ask-question-modal.module.css
@@ -0,0 +1,147 @@
+.ask-question-modal {
+ height: 21rem;
+ width: 31.25rem;
+ background-color: var(--color-white);
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ border-radius: 10px;
+ padding: 1.25rem;
+ max-width: 80svw;
+}
+
+.ask-question-modal__heading {
+ text-align: center;
+ color: var(--color-pink);
+ font-size: 1.5rem;
+ text-decoration: underline;
+ font-style: normal;
+ font-weight: 800;
+ line-height: normal;
+}
+
+.ask-question-modal__textarea {
+ width: 100%;
+ height: 47%;
+ outline: none;
+ border: 0;
+ resize: none;
+ margin-top: 1.25rem;
+ font-family: raleway, sans-serif;
+ font-size: 1.1rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ overflow-y: auto;
+}
+
+.ask-question-modal__textarea::-webkit-scrollbar {
+ width: 5px;
+}
+
+.ask-question-modal__textarea::-webkit-scrollbar-thumb {
+ background: var(--color-darkgrey);
+ border-radius: 10px;
+}
+
+.ask-question-modal__checkbox-container {
+ display: flex;
+ gap: 0.5rem;
+}
+
+.ask-question-modal__checkbox {
+ appearance: none;
+ background-color: #fff;
+ margin: 0;
+ font: inherit;
+ color: currentcolor;
+ width: 1.15rem;
+ height: 1.15rem;
+ border: 0.15em solid var(--color-navyblue);
+ border-radius: 0.15em;
+ display: grid;
+ place-content: center;
+}
+
+.ask-question-modal__checkbox::before {
+ content: "";
+ width: 0.65rem;
+ height: 0.65rem;
+ transform: scale(0);
+ transition: 120ms transform ease-in-out;
+ box-shadow: inset 1em 1em var(--color-pink);
+ background-color: CanvasText;
+ transform-origin: bottom left;
+ clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
+}
+
+.ask-question-modal__checkbox:checked {
+ border: 2px solid var(--color-pink);
+}
+
+.ask-question-modal__checkbox:checked::before {
+ transform: scale(1);
+}
+
+.ask-question-modal__checkbox-label {
+ font-size: 1rem;
+ font-weight: 600;
+}
+
+.ask-question-modal__max-characters-input {
+ width: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ font-size: 1.2rem;
+ border: 2px solid var(--color-navyblue);
+ border-radius: 10px;
+ margin-top: 10px;
+}
+
+.ask-question-modal__max-characters-input:focus {
+ outline: none;
+ box-shadow: 0 0 0 4px var(--color-light-navyblue);
+}
+
+.ask-question-modal__actions {
+ text-align: center;
+}
+
+.ask-question-modal__cancel-button {
+ margin-right: 1rem;
+}
+
+/* media queries */
+@media only screen and (width <=625px) {
+ .ask-question-modal {
+ height: 23rem;
+ padding: 1.25rem 1rem;
+ }
+
+ .ask-question-modal__checkbox {
+ width: 1.2rem;
+ }
+}
+
+@media only screen and (width <=425px) {
+ .ask-question-modal__checkbox {
+ width: 1.6rem;
+ }
+
+ .ask-question-modal__submit-button,
+ .ask-question-modal__cancel-button {
+ margin-right: 0;
+ width: fit-content;
+ padding: 0 20px;
+ }
+}
+
+/* utils css */
+.visibility--hidden {
+ visibility: hidden;
+}
+
+.visibility--visible {
+ visibility: visible;
+}
diff --git a/app/styles/button.module.css b/app/styles/button.module.css
index 520dafcb..f3a73117 100644
--- a/app/styles/button.module.css
+++ b/app/styles/button.module.css
@@ -32,6 +32,11 @@
background-color: var(--color-pink);
}
+.btn-pink:disabled {
+ opacity: 0.4;
+ color: var(--color-white);
+}
+
.btn--sm {
width: 8rem;
}
diff --git a/app/styles/icon-button.module.css b/app/styles/icon-button.module.css
index 15d81d2b..f0697b8e 100644
--- a/app/styles/icon-button.module.css
+++ b/app/styles/icon-button.module.css
@@ -27,7 +27,36 @@
transform: translate(50%, 50%);
}
-@media (width <= 425px) {
+.icon-button--sm {
+ border-radius: 50%;
+ height: 37px;
+ width: 38px;
+ margin-right: 2rem;
+ position: relative;
+}
+
+.icon-button--sm .iconify.iconify--material-symbols {
+ height: 50%;
+ width: 50%;
+ position: absolute;
+ bottom: 50%;
+ right: 50%;
+ transform: translate(50%, 50%);
+}
+
+.icon-button--green {
+ background-color: var(--color-green);
+ color: var(--color-white);
+ border: none;
+}
+
+.icon-button--red-outlined {
+ border: 2px solid var(--text-red);
+ background-color: var(--color-white);
+ color: var(--text-red);
+}
+
+@media (width <=425px) {
.icon-button--md {
margin-right: 1rem;
}
diff --git a/app/styles/input.module.css b/app/styles/input.module.css
index 0982e719..6173a8b6 100644
--- a/app/styles/input.module.css
+++ b/app/styles/input.module.css
@@ -47,3 +47,41 @@
justify-content: space-between;
overflow-y: scroll;
}
+
+.user-input--error {
+ animation: error-animation 0.1s ease-in-out 3;
+ border: 1px solid red;
+}
+
+.user-input--error.user-input:focus {
+ box-shadow: 0 0 0 4px var(--color-soft-magenta);
+}
+
+.input-box .input--full-width {
+ width: 100%;
+}
+
+.input-box__helper-text {
+ font-size: 0.8rem;
+ margin-left: 5px;
+ margin-top: 5px;
+ color: var(--color-lightgrey);
+}
+
+.input-box__helper-text--error {
+ color: var(--text-red);
+}
+
+@keyframes error-animation {
+ 0% {
+ transform: translateX(-4px);
+ }
+
+ 50% {
+ transform: translateX(4px);
+ }
+
+ 100% {
+ transform: translateX(-4px);
+ }
+}
diff --git a/app/styles/survey-page.module.css b/app/styles/survey-page.module.css
new file mode 100644
index 00000000..08e9edfc
--- /dev/null
+++ b/app/styles/survey-page.module.css
@@ -0,0 +1,43 @@
+.survey-page {
+ width: 100%;
+ margin-top: 3.1rem;
+ min-height: 80vh;
+ box-sizing: border-box;
+ padding: 0.6rem 1rem;
+ border-radius: 1rem;
+}
+
+.survey-page__question-container {
+ display: flex;
+ align-items: center;
+ gap: 2rem;
+}
+
+.survey-page__filter {
+ margin-bottom: 1rem;
+}
+
+.survey-page__answers {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.survey-page__answers-heading {
+ border-radius: 10px;
+ background: var(--color-light-gray);
+ color: var(--color-pink);
+ font-size: 2.5rem;
+ padding: 0 10px;
+ line-height: 150%;
+ margin-bottom: 20px;
+ box-sizing: border-box;
+ height: 65px;
+}
+
+@media (width <=768px) {
+ .survey-page__answers-heading {
+ font-size: 2rem;
+ line-height: 191%;
+ }
+}
diff --git a/app/styles/word-cloud.module.css b/app/styles/word-cloud.module.css
new file mode 100644
index 00000000..9f4ec580
--- /dev/null
+++ b/app/styles/word-cloud.module.css
@@ -0,0 +1,4 @@
+.word-cloud {
+ min-height: 400px;
+ margin-top: 10px;
+}
diff --git a/app/templates/live.hbs b/app/templates/live.hbs
index 38e94fe7..038fbf8b 100644
--- a/app/templates/live.hbs
+++ b/app/templates/live.hbs
@@ -1,14 +1,25 @@
-{{page-title 'Live'}}
+{{page-title "Live"}}
-
+
{{#if this.liveService.isJoined}}
+ {{!-- {{#if true}} --}}
{{/if}}
-
+
@@ -33,7 +44,7 @@
@roomCodes={{this.liveService.roomCodesForMaven}}
@closeModal={{modal.closeModal}}
@newCode={{this.newRoomCode}}
- @onInput={{fn this.inputHandler 'newRoomCode'}}
+ @onInput={{fn this.inputHandler "newRoomCode"}}
@createRoomCode={{this.createRoomCodeHandler}}
@toast={{this.liveService.toast}}
@isLoading={{this.liveService.roomCodeLoading}}
@@ -47,34 +58,35 @@
>
{{! TODO - add more else if statement instead of only else }}
- {{#if (eq this.activeTab 'Screenshare')}}
+ {{#if (eq this.activeTab "Screenshare")}}
{{#if this.liveService.isLoading}}
-
-
+
+
{{else}}
{{#if this.liveService.isJoined}}
-
+ {{!-- {{#if true}} --}}
+
{{! TODO - add skeleton to the whole structure }}
{{#if this.isLoading}}
{{else}}
-
+
{{/if}}
-
-
+
{{#if this.isLoading}}
{{else}}
@@ -100,8 +112,9 @@
/>
{{/if}}
+
{{else}}
- {{#if (eq this.role '')}}
+ {{#if (eq this.role "")}}
+ {{else if (eq this.activeTab "Logs")}}
+
+ {{else if (eq this.activeTab "Survey")}}
+
{{else}}
{{! TODO - add the respective component here }}
- Coming Soon!
+ Coming Soon!
{{/if}}
\ No newline at end of file
diff --git a/app/utils/common-utils.js b/app/utils/common-utils.js
new file mode 100644
index 00000000..971ad825
--- /dev/null
+++ b/app/utils/common-utils.js
@@ -0,0 +1,11 @@
+export const readMoreFormatter = (string, lengthToDisplay) => {
+ if (!string) {
+ return string;
+ }
+
+ if (string.length > lengthToDisplay) {
+ return string.slice(0, lengthToDisplay) + '...';
+ } else {
+ return string;
+ }
+};
diff --git a/package.json b/package.json
index 0ce7d021..f3330486 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,10 @@
},
"dependencies": {
"@100mslive/hms-video-store": "^0.10.6",
+ "d3-cloud": "^1.2.7",
"dotenv": "^16.0.2",
+ "ember-d3": "^0.5.1",
+ "exists-sync": "^0.1.0",
"fastboot-app-server": "^3.3.2"
},
"devDependencies": {
diff --git a/tests/integration/components/answer-reply-modal-test.js b/tests/integration/components/answer-reply-modal-test.js
new file mode 100644
index 00000000..b7cdb618
--- /dev/null
+++ b/tests/integration/components/answer-reply-modal-test.js
@@ -0,0 +1,53 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+import Service from '@ember/service';
+class MockSurveyService extends Service {
+ recentQuestion = 'This is mock recent question';
+}
+module('Integration | Component | answer-reply-modal', function (hooks) {
+ setupRenderingTest(hooks);
+
+ hooks.beforeEach(function (assert) {
+ // Registering the mock survey service
+ this.owner.register('service:survey', MockSurveyService);
+ const surveyService = this.owner.lookup('service:survey');
+
+ this.setProperties({
+ openModal: () => assert.ok(true, 'openModal working fine!'),
+ closeModal: () => assert.ok(true, 'closeModal working fine!'),
+ isOpen: true,
+ inputHandler: () => assert.ok(true, 'onAnswerInput working fine!'),
+ answerValue: 'mock answer value',
+ onSubmit: () => assert.ok(true, 'submitAnswer working fine!'),
+ validationDetails: {
+ isError: false,
+ isHelperTextVisible: true,
+ helperText: `Minimum character limit is 10 characters`,
+ },
+ submitButtonState: {
+ isDisabled: true,
+ isLoading: false,
+ },
+ survey: surveyService,
+ });
+ });
+
+ test('it renders', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.dom('[data-test-answer-reply-modal]').exists();
+ });
+});
diff --git a/tests/integration/components/ask-question-modal-test.js b/tests/integration/components/ask-question-modal-test.js
new file mode 100644
index 00000000..d3d482c1
--- /dev/null
+++ b/tests/integration/components/ask-question-modal-test.js
@@ -0,0 +1,17 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+
+module('Integration | Component | ask-question-modal', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ assert.ok(true);
+ // TODO - add tests
+ // Set any properties with this.set('myProperty', 'value');
+ // Handle any actions with this.set('myAction', function(val) { ... });
+
+ // await render(hbs`
`);
+
+ // assert.dom(this.element).hasText('');
+ });
+});
diff --git a/tests/integration/components/events/survey-page-test.js b/tests/integration/components/events/survey-page-test.js
new file mode 100644
index 00000000..3697cd14
--- /dev/null
+++ b/tests/integration/components/events/survey-page-test.js
@@ -0,0 +1,14 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | survey-page', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('Events::SurveyPage renders', async function (assert) {
+ await render(hbs`
`);
+
+ assert.ok(true, 'survey page tests');
+ });
+});
diff --git a/tests/integration/components/word-cloud-test.js b/tests/integration/components/word-cloud-test.js
new file mode 100644
index 00000000..0fb321b8
--- /dev/null
+++ b/tests/integration/components/word-cloud-test.js
@@ -0,0 +1,22 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'website-www/tests/helpers';
+import { render } from '@ember/test-helpers';
+import { hbs } from 'ember-cli-htmlbars';
+
+module('Integration | Component | word-cloud', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ this.set('isWordCloud', true);
+
+ await render(hbs`
`);
+
+ assert.dom('[data-test-word-cloud]').exists();
+
+ this.set('isWordCloud', false);
+
+ assert.dom('[data-test-word-cloud]').doesNotExist();
+ });
+});
diff --git a/tests/unit/utils/common-test.js b/tests/unit/utils/common-test.js
new file mode 100644
index 00000000..443f0bcd
--- /dev/null
+++ b/tests/unit/utils/common-test.js
@@ -0,0 +1,28 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'website-www/tests/helpers';
+import { readMoreFormatter } from 'website-www/utils/common-utils';
+
+module('Unit | Util | readMoreFormatter', function (hooks) {
+ setupTest(hooks);
+
+ test('should return empty string if empty string is passed', function (assert) {
+ const result = readMoreFormatter('', 10);
+
+ assert.strictEqual(result, '')
+ });
+
+ test('should return the string if length of string is less than the length passed to format the string', function (assert) {
+ const demoString = 'Lorem Ipsum is simply dummy text';
+
+ const result = readMoreFormatter(demoString, 32);
+ assert.strictEqual(result, demoString)
+ });
+
+ test('should format the string upto the given length in read more format', function (assert) {
+ const demoString =
+ 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.';
+
+ const result = readMoreFormatter(demoString, 32);
+ assert.strictEqual(result, 'Lorem Ipsum is simply dummy text...')
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index 889a4772..54091bf3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5571,7 +5571,7 @@ broccoli-merge-trees@^2.0.0:
broccoli-plugin "^1.3.0"
merge-trees "^1.0.1"
-broccoli-merge-trees@^3.0.1, broccoli-merge-trees@^3.0.2:
+broccoli-merge-trees@^3.0.0, broccoli-merge-trees@^3.0.1, broccoli-merge-trees@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-3.0.2.tgz#f33b451994225522b5c9bcf27d59decfd8ba537d"
integrity sha512-ZyPAwrOdlCddduFbsMyyFzJUrvW6b04pMvDiAQZrCwghlvgowJDY+EfoXn+eR1RRA5nmGHJ+B68T63VnpRiT1A==
@@ -6303,6 +6303,11 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"
+commander@2, commander@^2.20.0, commander@^2.6.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
commander@2.8.x:
version "2.8.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4"
@@ -6320,11 +6325,6 @@ commander@^10.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
-commander@^2.20.0, commander@^2.6.0:
- version "2.20.3"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-
commander@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
@@ -6668,6 +6668,269 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
+d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
+ integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==
+
+d3-axis@1:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.12.tgz#cdf20ba210cfbb43795af33756886fb3638daac9"
+ integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==
+
+d3-brush@1:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b"
+ integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==
+ dependencies:
+ d3-dispatch "1"
+ d3-drag "1"
+ d3-interpolate "1"
+ d3-selection "1"
+ d3-transition "1"
+
+d3-chord@1:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f"
+ integrity sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==
+ dependencies:
+ d3-array "1"
+ d3-path "1"
+
+d3-cloud@^1.2.7:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/d3-cloud/-/d3-cloud-1.2.7.tgz#5a733c4bae43238cbb4760bb8f2d15912a8ad7a5"
+ integrity sha512-8TrgcgwRIpoZYQp7s3fGB7tATWfhckRb8KcVd1bOgqkNdkJRDGWfdSf4HkHHzZxSczwQJdSxvfPudwir5IAJ3w==
+ dependencies:
+ d3-dispatch "^1.0.3"
+
+d3-collection@1:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
+ integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
+
+d3-color@1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
+ integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
+
+d3-contour@1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/d3-contour/-/d3-contour-1.3.2.tgz#652aacd500d2264cb3423cee10db69f6f59bead3"
+ integrity sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==
+ dependencies:
+ d3-array "^1.1.1"
+
+d3-dispatch@1, d3-dispatch@^1.0.3:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58"
+ integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==
+
+d3-drag@1:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70"
+ integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==
+ dependencies:
+ d3-dispatch "1"
+ d3-selection "1"
+
+d3-dsv@1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c"
+ integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==
+ dependencies:
+ commander "2"
+ iconv-lite "0.4"
+ rw "1"
+
+d3-ease@1:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2"
+ integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==
+
+d3-fetch@1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7"
+ integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==
+ dependencies:
+ d3-dsv "1"
+
+d3-force@1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.2.1.tgz#fd29a5d1ff181c9e7f0669e4bd72bdb0e914ec0b"
+ integrity sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==
+ dependencies:
+ d3-collection "1"
+ d3-dispatch "1"
+ d3-quadtree "1"
+ d3-timer "1"
+
+d3-format@1:
+ version "1.4.5"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
+ integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
+
+d3-geo@1:
+ version "1.12.1"
+ resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f"
+ integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==
+ dependencies:
+ d3-array "1"
+
+d3-hierarchy@1:
+ version "1.1.9"
+ resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83"
+ integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==
+
+d3-interpolate@1:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
+ integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
+ dependencies:
+ d3-color "1"
+
+d3-path@1:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
+ integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
+
+d3-polygon@1:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e"
+ integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==
+
+d3-quadtree@1:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135"
+ integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==
+
+d3-random@1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.2.tgz#2833be7c124360bf9e2d3fd4f33847cfe6cab291"
+ integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==
+
+d3-scale-chromatic@1:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98"
+ integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==
+ dependencies:
+ d3-color "1"
+ d3-interpolate "1"
+
+d3-scale@2:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f"
+ integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==
+ dependencies:
+ d3-array "^1.2.0"
+ d3-collection "1"
+ d3-format "1"
+ d3-interpolate "1"
+ d3-time "1"
+ d3-time-format "2"
+
+d3-selection-multi@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/d3-selection-multi/-/d3-selection-multi-1.0.1.tgz#cd6c25413d04a2cb97470e786f2cd877f3e34f58"
+ integrity sha512-mEnRkJ6A+Otd1LuRPV3az+s/RLDmIEhuz/MnT21O2nPcWNH7wZotdIlRjMA4Wpr+n7AESQ5fD8v1J3nL2Mnw9g==
+ dependencies:
+ d3-selection "1"
+ d3-transition "1"
+
+d3-selection@1, d3-selection@^1.1.0:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
+ integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
+
+d3-shape@1:
+ version "1.3.7"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
+ integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
+ dependencies:
+ d3-path "1"
+
+d3-time-format@2:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
+ integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==
+ dependencies:
+ d3-time "1"
+
+d3-time@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
+ integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
+
+d3-timer@1:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
+ integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
+
+d3-transition@1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398"
+ integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==
+ dependencies:
+ d3-color "1"
+ d3-dispatch "1"
+ d3-ease "1"
+ d3-interpolate "1"
+ d3-selection "^1.1.0"
+ d3-timer "1"
+
+d3-voronoi@1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.4.tgz#dd3c78d7653d2bb359284ae478645d95944c8297"
+ integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
+
+d3-zoom@1:
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a"
+ integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==
+ dependencies:
+ d3-dispatch "1"
+ d3-drag "1"
+ d3-interpolate "1"
+ d3-selection "1"
+ d3-transition "1"
+
+d3@^5.0.0:
+ version "5.16.0"
+ resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877"
+ integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==
+ dependencies:
+ d3-array "1"
+ d3-axis "1"
+ d3-brush "1"
+ d3-chord "1"
+ d3-collection "1"
+ d3-color "1"
+ d3-contour "1"
+ d3-dispatch "1"
+ d3-drag "1"
+ d3-dsv "1"
+ d3-ease "1"
+ d3-fetch "1"
+ d3-force "1"
+ d3-format "1"
+ d3-geo "1"
+ d3-hierarchy "1"
+ d3-interpolate "1"
+ d3-path "1"
+ d3-polygon "1"
+ d3-quadtree "1"
+ d3-random "1"
+ d3-scale "2"
+ d3-scale-chromatic "1"
+ d3-selection "1"
+ d3-shape "1"
+ d3-time "1"
+ d3-time-format "2"
+ d3-timer "1"
+ d3-transition "1"
+ d3-voronoi "1"
+ d3-zoom "1"
+
dag-map@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/dag-map/-/dag-map-2.0.2.tgz#9714b472de82a1843de2fba9b6876938cab44c68"
@@ -7128,7 +7391,7 @@ ember-cli-babel-plugin-helpers@^1.0.0, ember-cli-babel-plugin-helpers@^1.1.1:
resolved "https://registry.yarnpkg.com/ember-cli-babel-plugin-helpers/-/ember-cli-babel-plugin-helpers-1.1.1.tgz#5016b80cdef37036c4282eef2d863e1d73576879"
integrity sha512-sKvOiPNHr5F/60NLd7SFzMpYPte/nnGkq/tMIfXejfKHIhaiIkYFqX8Z9UFTKWLLn+V7NOaby6niNPZUdvKCRw==
-ember-cli-babel@^7.10.0, ember-cli-babel@^7.13.0, ember-cli-babel@^7.18.0, ember-cli-babel@^7.22.1, ember-cli-babel@^7.23.0, ember-cli-babel@^7.23.1, ember-cli-babel@^7.26.11, ember-cli-babel@^7.26.3, ember-cli-babel@^7.26.5, ember-cli-babel@^7.26.6, ember-cli-babel@^7.7.3:
+ember-cli-babel@^7.1.2, ember-cli-babel@^7.10.0, ember-cli-babel@^7.13.0, ember-cli-babel@^7.18.0, ember-cli-babel@^7.22.1, ember-cli-babel@^7.23.0, ember-cli-babel@^7.23.1, ember-cli-babel@^7.26.11, ember-cli-babel@^7.26.3, ember-cli-babel@^7.26.5, ember-cli-babel@^7.26.6, ember-cli-babel@^7.7.3:
version "7.26.11"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.26.11.tgz#50da0fe4dcd99aada499843940fec75076249a9f"
integrity sha512-JJYeYjiz/JTn34q7F5DSOjkkZqy8qwFOOxXfE6pe9yEJqWGu4qErKxlz8I22JoVEQ/aBUO+OcKTpmctvykM9YA==
@@ -7666,6 +7929,17 @@ ember-composable-helpers@^5.0.0:
ember-cli-babel "^7.26.3"
resolve "^1.10.0"
+ember-d3@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/ember-d3/-/ember-d3-0.5.1.tgz#b23ce145863f082b5e73d25d9a43a0f1d9e9f412"
+ integrity sha512-NyjTUuIOxGxZdyrxLasNwwjqyFgay1pVHGRAWFj7mriwTI44muKsM9ZMl6YeepqixceuFig2fDxHmLLrkQV+QQ==
+ dependencies:
+ broccoli-funnel "^2.0.0"
+ broccoli-merge-trees "^3.0.0"
+ d3 "^5.0.0"
+ d3-selection-multi "^1.0.1"
+ ember-cli-babel "^7.1.2"
+
ember-data@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/ember-data/-/ember-data-5.3.0.tgz#d7be6b77653a41ae8ed045ffb904f1adbdcb8920"
@@ -8469,6 +8743,11 @@ execa@^7.1.1:
signal-exit "^3.0.7"
strip-final-newline "^3.0.0"
+exists-sync@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.1.0.tgz#318d545213d2b2a31499e92c35f74c94196a22f7"
+ integrity sha512-qEfFekfBVid4b14FNug/RNY1nv+BADnlzKGHulc+t6ZLqGY4kdHGh1iFha8lnE3sJU/1WzMzKRNxS6EvSakJUg==
+
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -9757,7 +10036,7 @@ human-signals@^4.3.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==
-iconv-lite@0.4.24, iconv-lite@^0.4.24:
+iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -12790,6 +13069,11 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
+rw@1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
+ integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==
+
rxjs@^6.4.0, rxjs@^6.6.0:
version "6.6.7"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"