Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: charts #6790

Merged
merged 132 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
132 commits
Select commit Hold shift + click to select a range
c937aa1
Merge pull request #126 from opengovsg/release
arshadali172 Aug 12, 2020
d6550f5
Merge pull request #153 from opengovsg/release-4.30.4
tshuli Aug 18, 2020
7849419
Merge pull request #200 from opengovsg/release-v4.31.0
liangyuanruo Aug 25, 2020
49a64dd
Merge pull request #238 from opengovsg/release-v4.32.1
liangyuanruo Sep 1, 2020
4fa854c
Merge pull request #296 from opengovsg/release
arshadali172 Sep 8, 2020
9c40b5c
Merge pull request #319 from opengovsg/release-4.34.1
tshuli Sep 15, 2020
730f412
Merge pull request #351 from opengovsg/release-4.35.1-hotfix
mantariksh Sep 22, 2020
72f6b93
Merge pull request #380 from opengovsg/release-4.36.0
karrui Sep 29, 2020
b7b9240
Merge pull request #413 from opengovsg/release-v4.37.1
mantariksh Oct 6, 2020
5a880a9
Merge pull request #459 from opengovsg/release-v4.39.0
liangyuanruo Oct 13, 2020
42238be
Merge pull request #492 from opengovsg/release-v4.40.0
mantariksh Oct 20, 2020
cf80dc0
Merge pull request #517 from opengovsg/release-4.41.0
mantariksh Oct 27, 2020
6df2b0c
Merge pull request #528 from opengovsg/release-4.42.0
mantariksh Nov 2, 2020
5f657e8
Merge pull request #554 from opengovsg/release-4.43.2
mantariksh Nov 3, 2020
8ce4d29
Merge pull request #605 from opengovsg/release-4.44.0
mantariksh Nov 12, 2020
bdf95b7
Merge pull request #652 from opengovsg/release-4.45.1-hotfix
mantariksh Nov 17, 2020
e7cc6b0
Merge pull request #716 from opengovsg/release-v4.46.1-hotfix
mantariksh Nov 24, 2020
20a4fea
Merge pull request #761 from opengovsg/release-4.47.0
mantariksh Dec 1, 2020
773fd41
Merge pull request #843 from opengovsg/release-4.48.2
mantariksh Dec 11, 2020
3e5b510
Merge pull request #851 from opengovsg/release-4.49.1
karrui Dec 14, 2020
ab7c61a
Merge pull request #931 from opengovsg/release-4.50.3
liangyuanruo Dec 24, 2020
4507298
Merge pull request #976 from opengovsg/release-4.51.0
mantariksh Jan 6, 2021
b26267f
Merge pull request #1003 from opengovsg/release-4.52.1
liangyuanruo Jan 14, 2021
6c3e109
Merge pull request #1031 from opengovsg/release-4.53.0
liangyuanruo Jan 25, 2021
3947841
Merge pull request #1050 from opengovsg/release-4.54.0
liangyuanruo Jan 26, 2021
b7ad006
Merge pull request #1112 from opengovsg/release-4.56.0
mantariksh Feb 10, 2021
e28c87c
Merge pull request #1159 from opengovsg/release-4.57.0
mantariksh Feb 16, 2021
3d47f32
Merge pull request #1221 from opengovsg/release-4.58.2
liangyuanruo Feb 23, 2021
a145700
Merge pull request #1335 from opengovsg/release-v5.0.4
liangyuanruo Mar 10, 2021
bb2f4bd
Merge pull request #1389 from opengovsg/release-5.1.0
liangyuanruo Mar 17, 2021
fc6a3ee
Merge pull request #1431 from opengovsg/release-v5.2.0
liangyuanruo Mar 23, 2021
3006bdc
Merge pull request #1504 from opengovsg/release-v5.3.0
liangyuanruo Mar 31, 2021
4556b00
Merge pull request #1563 from opengovsg/release-5.4.1
liangyuanruo Apr 6, 2021
4f0194c
Merge pull request #1629 from opengovsg/release-v5.5.1
karrui Apr 13, 2021
cc19a8f
Merge pull request #1682 from opengovsg/release-v5.6.1
mantariksh Apr 20, 2021
17f2846
Merge pull request #1752 from opengovsg/release-v5.7.1
karrui Apr 28, 2021
6a89669
Merge pull request #1786 from opengovsg/release-v5.8.0
mantariksh May 5, 2021
79bda98
Merge pull request #1857 from opengovsg/release-5.9.0
karrui May 11, 2021
64b4bf0
Merge pull request #1970 from opengovsg/release-v5.10.1
mantariksh May 25, 2021
a2f8e1d
Merge pull request #2043 from opengovsg/release-v5.11.0
karrui Jun 1, 2021
65fff8d
Merge pull request #2103 from opengovsg/release-v5.12.1
mantariksh Jun 8, 2021
eccc0fc
Merge pull request #2175 from opengovsg/release-v5.13.1
mantariksh Jun 15, 2021
ef74218
Merge pull request #2231 from opengovsg/release-v5.15.0
mantariksh Jun 22, 2021
ea69b81
Merge pull request #2312 from opengovsg/release-v5.18.0
tshuli Jul 6, 2021
a1c9da4
Merge pull request #2514 from opengovsg/release-v5.21.2
mantariksh Aug 3, 2021
08e5c8d
Merge pull request #2568 from opengovsg/release-v5.22.0
mantariksh Aug 11, 2021
9cb73f9
Merge pull request #2638 from opengovsg/release-v5.23.0
karrui Aug 19, 2021
cdfe5a9
Merge pull request #2668 from opengovsg/release-v5.24.1
karrui Aug 24, 2021
cb4f6ca
Merge pull request #2713 from opengovsg/release-v5.25.0
karrui Aug 31, 2021
de612ba
Merge pull request #2805 from opengovsg/release-5.27.0
timotheeg Sep 14, 2021
086784b
Merge pull request #2852 from opengovsg/release-5.28.0
tshuli Sep 23, 2021
42ae160
Merge pull request #2877 from opengovsg/release-v5.29.0
tshuli Sep 29, 2021
0fac0e5
Merge pull request #3215 from opengovsg/release-5.38.1
tshuli Dec 21, 2021
b5c53a0
Merge pull request #4009 from opengovsg/release-5.61.0
karrui Jun 22, 2022
1cc9c50
chore: import react google charts
foochifa Oct 10, 2023
efdc933
feat: create skeleton insights page
foochifa Oct 10, 2023
0830d9d
fix: regex for route matching for insight page
foochifa Oct 10, 2023
bbb606f
fix: layout of insights page
foochifa Oct 10, 2023
808f712
feat: dummy endpoint for all encrypted data
foochifa Oct 10, 2023
b91b494
feat: queries to get all encrypted submission
foochifa Oct 10, 2023
580efe4
fix: encrypted model find params
foochifa Oct 10, 2023
1c20e97
feat: get and decrypted all submission data
foochifa Oct 10, 2023
a86e6d0
feat: get formfields
foochifa Oct 10, 2023
8d4d0fe
feat: generate charts
foochifa Oct 10, 2023
820910f
fix: typing of spacing
foochifa Oct 10, 2023
1cd41f0
feat: create mapping for field to charts
foochifa Oct 10, 2023
e978f67
feat: some upgrades to format charts
foochifa Oct 11, 2023
6d5373f
feat:wordcloud
foochifa Oct 11, 2023
611ebfa
fix: remove excess divider
foochifa Oct 11, 2023
8a3379e
fix: prefill rating values in bar chart
foochifa Oct 11, 2023
86dba56
feat: add question number to chart title
foochifa Oct 11, 2023
d4181d2
feat: skeleton for changing chart to table
foochifa Oct 11, 2023
98dbd93
chore: add gstatic charts to csp policy
foochifa Oct 11, 2023
a52203d
fix: rating average counting
foochifa Oct 11, 2023
962ae1d
feat: table toggle mode
foochifa Oct 11, 2023
e1ed170
feat: use icon button instead of toggle
foochifa Oct 11, 2023
012731b
feat: dummy date picker
foochifa Oct 11, 2023
ce1abbf
fix: better date range picker styling
foochifa Oct 11, 2023
7b071a2
feat: date range filtering
foochifa Oct 11, 2023
45e295d
fix: do not show rating if no values
foochifa Oct 12, 2023
05e1a03
fix: filtering by date and styling
foochifa Oct 12, 2023
ef62e44
fix: increase size of charts
foochifa Oct 12, 2023
1e3609c
fix: do not display wordcloud if no words
foochifa Oct 12, 2023
e4cdc91
fix: alignment of wordcloud title
foochifa Oct 12, 2023
3714da3
fix: typing for submission insights dto
foochifa Oct 12, 2023
623c626
refactor: make code more readable
foochifa Oct 13, 2023
4a7e0d9
fix: typing of render array
foochifa Oct 13, 2023
580dc5b
fix: do not randomize color for rating
foochifa Oct 16, 2023
44f7fe1
feat: fix bar graph colours changing on refocus
sebastianwzq Oct 25, 2023
d2d46d7
feat: fix random word cloud movements on re-render
sebastianwzq Oct 30, 2023
e1863ef
feat: address MR comments
sebastianwzq Nov 3, 2023
3d582a9
feat: corrected types and fixed lint comments
sebastianwzq Nov 3, 2023
d601794
feat: update average rating to account for division by 0
sebastianwzq Nov 3, 2023
117fa9e
feat: refactored filter function to remove redundant date parsing
sebastianwzq Nov 3, 2023
92fa974
feat: set max words for word cloud
sebastianwzq Nov 7, 2023
4d7916d
Merge remote-tracking branch 'origin/master' into hack/eliminate-data
sebastianwzq Nov 9, 2023
8c9c841
fix: merge with develop
sebastianwzq Nov 9, 2023
ed4d39d
fix: fe lint issue
KenLSM Nov 9, 2023
9cc8642
refactor: cleanup constants
KenLSM Nov 9, 2023
f38eaf5
feat: add growthbook toggle
KenLSM Nov 9, 2023
6bc247c
fix: flickering pie chart tooltip
KenLSM Nov 9, 2023
ac01deb
fix: ordering of frontend/package.json
KenLSM Nov 9, 2023
511d4c9
chore: add utils
KenLSM Nov 14, 2023
dc22568
feat: add empty insights field
KenLSM Nov 14, 2023
ec5bb2b
fix: typeerror on admin submission
KenLSM Nov 14, 2023
37897f8
refactor: rename insights to charts
KenLSM Nov 14, 2023
da548e9
feat: add beta badge
KenLSM Nov 14, 2023
a9964da
refactor: secretkeyverification to common component for results and c…
KenLSM Nov 14, 2023
b771a8b
fix: table charts ui
KenLSM Nov 14, 2023
ee8cfff
fix: charts secretkeyvewrification component
KenLSM Nov 14, 2023
eceeb22
Merge remote-tracking branch 'origin/develop' into hack/eliminate-data
KenLSM Nov 15, 2023
a6744f6
fix: remove stray space between charts and badge on tab title
KenLSM Nov 15, 2023
943432e
fix: remove testing flag
KenLSM Nov 19, 2023
a431ba4
chore: update copy for no charts generted
KenLSM Nov 19, 2023
e586558
fix: endday not calculated correctly
KenLSM Nov 19, 2023
a04084d
feat(be): add limit and reverse chrono sort for submissions query
KenLSM Nov 19, 2023
650e2b9
feat(fe): add forced redirect for email charts
KenLSM Nov 19, 2023
4e36103
Merge remote-tracking branch 'origin/develop' into hack/eliminate-data
KenLSM Nov 20, 2023
a24a24f
chore: update charts supported field for better visual alignment with…
KenLSM Nov 20, 2023
5dad95d
Merge branch 'develop' into hack/eliminate-data
tshuli Nov 20, 2023
879977b
Merge branch 'hack/eliminate-data' of https://github.com/opengovsg/Fo…
KenLSM Nov 20, 2023
80578c1
feat: add marketing prompts for charts
KenLSM Nov 20, 2023
e0fddc2
chore: add copy for 1000 chart limit
tshuli Nov 20, 2023
b01c8ef
chore: shorten copy
tshuli Nov 20, 2023
62bcf00
refactor: create daterangepicker helpers
KenLSM Nov 20, 2023
b8d676d
refactor: use helpers from daterangepicker
KenLSM Nov 20, 2023
b60d719
feat: add no charts prompt
KenLSM Nov 20, 2023
fe93acf
chore: update language to omit implication of uncertainty
KenLSM Nov 20, 2023
28669e8
fix: number typo
tshuli Nov 21, 2023
19dc00a
feat: correctly retrieve based on date range
tshuli Nov 21, 2023
9de7b16
fix: remove incorrect generic
KenLSM Nov 21, 2023
4184742
fix: remove unnecessary comment
KenLSM Nov 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
389 changes: 389 additions & 0 deletions frontend/package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@stablelib/base64": "^1.0.1",
"@stripe/react-stripe-js": "^1.15.0",
"@stripe/stripe-js": "^1.44.1",
"@types/stopword": "^2.0.1",
"axios": "^1.6.2",
"broadcast-channel": "^4.13.0",
"browser-image-compression": "^2.0.2",
Expand Down Expand Up @@ -50,6 +51,7 @@
"react-dom": "^17.0.2",
"react-dropzone": "^11.4.2",
"react-focus-lock": "^2.7.1",
"react-google-charts": "^4.0.1",
"react-helmet-async": "^1.2.3",
"react-hook-form": "^7.28.0",
"react-i18next": "^11.16.7",
Expand All @@ -67,11 +69,13 @@
"react-use-scrollspy": "^3.0.2",
"react-virtuoso": "^2.14.0",
"react-waypoint": "^10.1.0",
"react-wordcloud": "^1.2.7",
"remark-breaks": "^3.0.2",
"remark-gfm": "^3.0.1",
"rooks": "^5.11.0",
"simplur": "^3.0.1",
"spark-md5": "^3.0.2",
"stopword": "^2.0.8",
"stripe": "^11.1.0",
"timezone-mock": "^1.3.6",
"type-fest": "^2.8.0",
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/app/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
PAYMENT_PAGE_SUBROUTE,
PRIVACY_POLICY_ROUTE,
PUBLICFORM_ROUTE,
RESULTS_CHARTS_SUBROUTE,
RESULTS_FEEDBACK_SUBROUTE,
TOU_ROUTE,
USE_TEMPLATE_REDIRECT_SUBROUTE,
Expand All @@ -35,6 +36,7 @@ import {
ResponsesLayout,
ResponsesPage,
} from '~features/admin-form/responses'
import { ChartsPage } from '~features/admin-form/responses/ChartsPage/ChartsPage'
import { SettingsPage } from '~features/admin-form/settings/SettingsPage'
import { SelectProfilePage } from '~features/login'
import { FormPaymentPage } from '~features/public-form/components/FormPaymentPage/FormPaymentPage'
Expand Down Expand Up @@ -171,6 +173,9 @@ export const AppRouter = (): JSX.Element => {
path={RESULTS_FEEDBACK_SUBROUTE}
element={<FeedbackPage />}
/>
<Route path={RESULTS_CHARTS_SUBROUTE} element={<ResponsesLayout />}>
<Route index element={<ChartsPage />} />
</Route>
</Route>
</Route>
<Route
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/components/DateRangePicker/helpers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { format, isValid } from 'date-fns'

import { DateString } from '~shared/types'

import { DateRangeValue } from '~components/Calendar'

export const dateStringToDatePickerValue = (range: DateString[]) => {
const [start, end] = range
// Convert to Date objects
const startDate = new Date(start)
const endDate = new Date(end)
const result: (Date | null)[] = [null, null]
// Check if dates are valid
if (isValid(startDate)) {
result[0] = startDate
}
if (isValid(endDate)) {
result[1] = endDate
}
return result as DateRangeValue
}

export const datePickerValueToDateString = (range: DateRangeValue) => {
const [start, end] = range
const result: DateString[] = []
if (start) {
result.push(format(start, 'yyyy-MM-dd') as DateString)
}
if (end) {
result.push(format(end, 'yyyy-MM-dd') as DateString)
}
return result
}
1 change: 1 addition & 0 deletions frontend/src/components/DateRangePicker/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './DateRangePicker'
export * as dateRangePickerHelper from './helpers'
2 changes: 1 addition & 1 deletion frontend/src/constants/localStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const LOCAL_STORAGE_EVENT = 'local-storage'
* Key to store whether a user has seen the rollout announcements before.
*/
export const ROLLOUT_ANNOUNCEMENT_KEY_PREFIX =
'has-seen-rollout-announcement-20231116-'
'has-seen-rollout-announcement-20231121-'

/**
* Key to store whether the admin has seen the feature tour in localStorage.
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ export const ACTIVE_ADMINFORM_BUILDER_ROUTE_REGEX = new RegExp(
/** Responses tab has no subroute, its the index results route. */
export const RESULTS_RESPONSES_SUBROUTE = ''
export const RESULTS_FEEDBACK_SUBROUTE = 'feedback'
export const RESULTS_CHARTS_SUBROUTE = 'charts'

export const ACTIVE_ADMINFORM_RESULTS_ROUTE_REGEX = new RegExp(
`${ADMINFORM_ROUTE}/([a-fA-F0-9]{24})/${ADMINFORM_RESULTS_SUBROUTE}(/${RESULTS_FEEDBACK_SUBROUTE})?/?`,
`${ADMINFORM_ROUTE}/([a-fA-F0-9]{24})/${ADMINFORM_RESULTS_SUBROUTE}(/${RESULTS_FEEDBACK_SUBROUTE}|/${RESULTS_CHARTS_SUBROUTE})?/?`,
'i',
)
export const PAYMENT_PAGE_SUBROUTE = 'payment/:paymentId'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { DateString } from '~shared/types'
import {
FormSubmissionMetadataQueryDto,
StorageModeChartsDto,
StorageModeSubmissionDto,
StorageModeSubmissionMetadataList,
SubmissionCountQueryDto,
Expand Down Expand Up @@ -100,3 +102,61 @@ export const getDecryptedSubmissionById = async ({
responses: processedContent,
}
}

const getAllEncryptedSubmission = async ({
formId,
startDate,
endDate,
}: {
formId: string
startDate?: DateString
endDate?: DateString
}): Promise<StorageModeChartsDto[]> => {
const queryUrl = `${ADMIN_FORM_ENDPOINT}/${formId}/submissions`
if (startDate && endDate) {
return ApiService.get(queryUrl, {
params: {
startDate,
endDate,
},
}).then(({ data }) => data)
}
return ApiService.get(queryUrl).then(({ data }) => data)
}

type DecryptedContent = NonNullable<ReturnType<typeof formsgSdk.crypto.decrypt>>
export type DecryptedSubmission = DecryptedContent & {
submissionTime: string
}

export const getAllDecryptedSubmission = async ({
formId,
secretKey,
startDate,
endDate,
}: {
formId: string
secretKey?: string
startDate?: DateString
endDate?: DateString
}): Promise<DecryptedSubmission[]> => {
if (!secretKey) return []

const allEncryptedData = await getAllEncryptedSubmission({
formId,
startDate,
endDate,
})

return allEncryptedData.map((encryptedData) => {
KenLSM marked this conversation as resolved.
Show resolved Hide resolved
const decryptedContent = formsgSdk.crypto.decrypt(secretKey, {
encryptedContent: encryptedData.encryptedContent,
verifiedContent: encryptedData.verifiedContent,
version: encryptedData.version,
})

if (!decryptedContent) throw new Error('Could not decrypt the response')

return { ...decryptedContent, submissionTime: encryptedData.created }
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useLocation } from 'react-router-dom'
import { Box, Container, Divider, Stack } from '@chakra-ui/react'

import { FormResponseMode } from '~shared/types/form'

import { ACTIVE_ADMINFORM_RESULTS_ROUTE_REGEX } from '~constants/routes'
import { useToast } from '~hooks/useToast'

import { useAdminForm } from '~features/admin-form/common/queries'

import { SecretKeyVerification } from '../components/SecretKeyVerification'
import { ResponsesPageSkeleton } from '../ResponsesPage/ResponsesPageSkeleton'
import { useStorageResponsesContext } from '../ResponsesPage/storage'

import { ChartsSvgr } from './UnlockedCharts/assets/svgr/ChartsSvgr'
import { ChartsSupportedFieldsInfoBox } from './UnlockedCharts/components/ChartsSupportedFieldsInfoBox'
import { EmptyChartsContainer } from './UnlockedCharts/components/EmptyChartsContainer'
import UnlockedCharts from './UnlockedCharts'

export const ChartsPage = (): JSX.Element => {
const { data: form, isLoading } = useAdminForm()
const { totalResponsesCount, secretKey } = useStorageResponsesContext()
const { pathname } = useLocation()

const toast = useToast({ status: 'danger' })

if (isLoading) return <ResponsesPageSkeleton />

if (!form) {
toast({
description:
'There was an error retrieving your form. Please try again later.',
})
return <ResponsesPageSkeleton />
}

// Charts is not available for Email response
// Since there's no entry to the charts page for Email mode we should
// forcefully redirect the user to the responses page
// we need to redirect to one level up, i.e., '../'
if (form.responseMode === FormResponseMode.Email) {
/**
* 0: "/admin/form/<form_id>/results/charts"
* 1: "<form_id>"
* 2: "/charts"
*/
const match = pathname.match(ACTIVE_ADMINFORM_RESULTS_ROUTE_REGEX)
const subroute = match?.[2]
if (subroute) {
const pathnameWithoutSubroute = pathname.replace(subroute, '')
window.location.replace(pathnameWithoutSubroute)
}
return <></>
}

if (totalResponsesCount === 0) {
return (
<EmptyChartsContainer
title="No charts generated yet."
subtitle="Charts will be generated when you receive responses on your form."
/>
)
}

return secretKey ? (
<UnlockedCharts />
) : (
<>
<SecretKeyVerification
hideResponseCount
heroSvg={<ChartsSvgr />}
ctaText="View charts"
label="Enter or upload Secret Key to view charts"
/>
<Container p={0} maxW="42.5rem">
<Box mt="2rem" mb="0.5rem">
<Divider />
</Box>
<Stack>
<ChartsSupportedFieldsInfoBox />
</Stack>
</Container>
</>
)
}
Loading
Loading