-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9ec7ce3
commit 4ba3261
Showing
3 changed files
with
143 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"title": "Survey Responses", | ||
"onboarding": { | ||
"title": "Onboarding Survey", | ||
"description": "Filled out once before recipient is joining the program" | ||
}, | ||
"checkin": { | ||
"title": "Check-in Survey", | ||
"description": "Filled out every 6 months while recipient is in the program" | ||
}, | ||
"offboarding": { | ||
"title": "Offboarding Survey", | ||
"description": "Filled out once when recipient is finishing the program" | ||
}, | ||
"offboarded-checkin": { | ||
"title": "Follow-up Survey", | ||
"description": "Filled out every 6 months after recipient left the program" | ||
} | ||
|
||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import _ from 'lodash'; | ||
import { FirestoreAdmin } from '../../firebase/admin/FirestoreAdmin'; | ||
import { Recipient, RECIPIENT_FIRESTORE_PATH } from '../../types/recipient'; | ||
import { Survey, SURVEY_FIRETORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey'; | ||
|
||
export interface SurveyStats { | ||
total: number; | ||
type: SurveyQuestionnaire; | ||
} | ||
|
||
export interface SurveyAnswersByType { | ||
answers: any[]; | ||
} | ||
|
||
export class SurveyStatsCalculator { | ||
private readonly _data: SurveyStats[]; | ||
private readonly _aggregatedData: { [key: string]: { [key: string]: SurveyAnswersByType } }; | ||
|
||
private constructor( | ||
data: SurveyStats[], | ||
aggregatedData: { | ||
[key: string]: { [key: string]: SurveyAnswersByType }; | ||
}, | ||
) { | ||
this._data = data; | ||
this._aggregatedData = aggregatedData; | ||
} | ||
|
||
/** | ||
* @param firestoreAdmin | ||
*/ | ||
static async build(firestoreAdmin: FirestoreAdmin): Promise<SurveyStatsCalculator> { | ||
const recipients = await firestoreAdmin.collection<Recipient>(RECIPIENT_FIRESTORE_PATH).get(); | ||
let documents = await Promise.all( | ||
recipients.docs | ||
.filter((recipient) => !recipient.get('test_recipient')) | ||
.map(async (recipient) => { | ||
return await firestoreAdmin | ||
.collection<Survey>(`${RECIPIENT_FIRESTORE_PATH}/${recipient.id}/${SURVEY_FIRETORE_PATH}`) | ||
.get(); | ||
}), | ||
); | ||
|
||
let surveysData = documents.flatMap((snapshot) => snapshot.docs).map((survey) => survey.data()); | ||
|
||
let aggregatedData: { [key: string]: { [key: string]: SurveyAnswersByType } } = {}; | ||
const typeCounts: { [type: string]: number } = {}; | ||
surveysData.forEach((item) => { | ||
if (item.status === SurveyStatus.Completed) { | ||
typeCounts[item.questionnaire] = (typeCounts[item.questionnaire] || 0) + 1; | ||
for (const [key, value] of Object.entries(item.data)) { | ||
aggregatedData[item.questionnaire] = aggregatedData[item.questionnaire] || {}; | ||
aggregatedData[item.questionnaire][key] = aggregatedData[item.questionnaire][key] || { answers: [] }; | ||
aggregatedData[item.questionnaire][key].answers.push(value); | ||
} | ||
} | ||
}); | ||
const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats); | ||
|
||
return new SurveyStatsCalculator(data, aggregatedData); | ||
} | ||
|
||
get data(): SurveyStats[] { | ||
return this._data; | ||
} | ||
|
||
get aggregatedData(): { [key: string]: { [key: string]: SurveyAnswersByType } } { | ||
return this._aggregatedData; | ||
} | ||
} |
61 changes: 51 additions & 10 deletions
61
website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,58 @@ | ||
import { DefaultLayoutProps, DefaultPageProps } from '@/app/[lang]/[region]'; | ||
import { getMetadata } from '@/metadata'; | ||
import { firestoreAdmin } from '@/firebase-admin'; | ||
import { SurveyStatsCalculator } from '@socialincome/shared/src/utils/stats/SurveyStatsCalculator'; | ||
import { BaseContainer, Card, CardTitle, Typography } from '@socialincome/ui'; | ||
import { Translator } from '@socialincome/shared/src/utils/i18n'; | ||
import { DefaultPageProps } from '@/app/[lang]/[region]'; | ||
import { SurveyQuestionnaire } from '@socialincome/shared/src/types/survey'; | ||
|
||
export async function generateMetadata({ params }: DefaultLayoutProps) { | ||
return getMetadata(params.lang, 'website-login'); | ||
} | ||
export const revalidate = 3600; // update once an hour | ||
export default async function Page({ params: { lang } }: DefaultPageProps) { | ||
|
||
export default async function Page({ params }: DefaultPageProps) { | ||
const translator = await Translator.getInstance({ language: params.lang, namespaces: ['website-login'] }); | ||
const surveyStatsCalculator = await SurveyStatsCalculator.build(firestoreAdmin); | ||
const temp = surveyStatsCalculator.data; | ||
const allSurveyData = Object.values(SurveyQuestionnaire).map((it) => temp.find((survey) => survey.type == it)); | ||
const data = surveyStatsCalculator.aggregatedData; | ||
const translator = await Translator.getInstance({ | ||
language: lang, | ||
namespaces: ['website-responses'], | ||
}); | ||
let selectedSurvey = SurveyQuestionnaire.Onboarding; | ||
|
||
return ( | ||
<div className="min-h-screen-navbar mx-auto flex max-w-lg flex-col"> | ||
hello | ||
</div> | ||
<BaseContainer className="mx-auto flex max-w-3xl flex-col space-y-12"> | ||
<Typography size="4xl" weight="bold"> | ||
{translator.t('title')} | ||
</Typography> | ||
<div className="flex flex-col space-y-2 py-4"> | ||
<div className="mt-2 grid grid-cols-4 gap-2"> | ||
{allSurveyData.map( | ||
surveyData => | ||
surveyData && ( | ||
<Card key={surveyData.type} className="p-2"> | ||
<CardTitle className="text py-2">{translator.t(surveyData.type + '.title')}</CardTitle> | ||
|
||
<Typography className="mt-2">{translator.t(surveyData.type + '.description')}</Typography> | ||
<Typography className="mt-3">{surveyData.total} data points</Typography> | ||
</Card> | ||
), | ||
)} | ||
</div> | ||
</div> | ||
<div className="flex flex-col"> | ||
<div className="mt-2 grid grid-cols-1 gap-2"> | ||
{Object.keys(data[selectedSurvey]).map( | ||
(key) => | ||
( | ||
<Card key={key} className="p-2"> | ||
<CardTitle className="text py-2">{translator.t(key + '.title')}</CardTitle> | ||
|
||
<Typography className="mt-2">{JSON.stringify(data[selectedSurvey][key].answers)}</Typography> | ||
<Typography className="mt-3">{data[selectedSurvey][key].answers.length} answers</Typography> | ||
</Card> | ||
) | ||
)} | ||
</div> | ||
</div> | ||
</BaseContainer> | ||
); | ||
} |