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/ocrvs 7978/qr reader #8196

Open
wants to merge 62 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
7330f51
feat: create a qr code scanner component
tahmidrahman-dsi Nov 12, 2024
4c01e21
chore: remove unnecessary comment
tahmidrahman-dsi Nov 12, 2024
2801173
chore: interim changes
tahmidrahman-dsi Nov 13, 2024
01718e5
chore: add a separate form field type for qr scanner
tahmidrahman-dsi Nov 14, 2024
322dc1f
Merge branch 'develop' into feat/ocrvs-7955/qr-code-scanner-poc
tahmidrahman-dsi Nov 14, 2024
d5cfb67
fix: remove testing related changes
tahmidrahman-dsi Nov 15, 2024
2a99bf9
feat: qr scanner with multiple variants
tahmidrahman-dsi Nov 19, 2024
4112f58
fix: minor fixes
tahmidrahman-dsi Nov 19, 2024
bda1f8f
Merge branch 'develop' into feat/ocrvs-7955/qr-code-scanner-poc
tahmidrahman-dsi Nov 20, 2024
952dbee
chore: style amends and add a validation
tahmidrahman-dsi Nov 20, 2024
29076fa
feat: create id reader UI component
tahmidrahman-dsi Dec 11, 2024
80f5d99
feat: allow divider component to take in children to show label
tahmidrahman-dsi Dec 11, 2024
5037cf3
fix: cleanup client
tahmidrahman-dsi Dec 11, 2024
c214ce8
feat: add scanner component
tahmidrahman-dsi Dec 11, 2024
8d60581
feat: add titleIcon to Dialog
tahmidrahman-dsi Dec 12, 2024
9879729
chore: add info component
tahmidrahman-dsi Dec 12, 2024
4ec4f56
fix: close modal on successful scan
tahmidrahman-dsi Dec 12, 2024
4350011
feat: add support for taking translatable labels
tahmidrahman-dsi Dec 13, 2024
e892be2
fix: organize code a bit
tahmidrahman-dsi Dec 13, 2024
d298fd9
fix: use `useWindowSize` hook
tahmidrahman-dsi Dec 13, 2024
9b41a69
chore: create a `ReaderGenerator` that renders different types of rea…
tahmidrahman-dsi Dec 17, 2024
4399fad
feat: add a story to divider component having a label
tahmidrahman-dsi Dec 17, 2024
f994875
fix: take `children` instead of prop `label`
tahmidrahman-dsi Dec 17, 2024
61fbc7e
fix: use `Button` component in place of `SecondaryButton`
tahmidrahman-dsi Dec 17, 2024
41bfb99
refactor(wip): re-organize `ReaderGenerator`
tahmidrahman-dsi Dec 17, 2024
91062cd
chore: internationalize qr-reader field object
tahmidrahman-dsi Dec 18, 2024
425ee00
refactor: redirect component to make it fetch on mount
tahmidrahman-dsi Dec 18, 2024
354bc8e
feat: add ability to use redirect component inside id reader
tahmidrahman-dsi Dec 18, 2024
5e9ecb7
refactor: UI improvements
tahmidrahman-dsi Dec 18, 2024
fb482ef
fix: amend config validations
tahmidrahman-dsi Dec 18, 2024
290228a
refactor: do not take unnecessary labels in prop
tahmidrahman-dsi Dec 23, 2024
a3d468b
fix: improve styles
tahmidrahman-dsi Dec 23, 2024
f134c23
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Dec 24, 2024
1bc9ea0
fix: put button inside link
tahmidrahman-dsi Dec 24, 2024
28edf58
chore: supported configuring icon to redirect button
tahmidrahman-dsi Dec 24, 2024
d2f7beb
fix: replace hardcoded breakpoint with theme defined breakpoint
tahmidrahman-dsi Dec 24, 2024
841dbad
fix: styles to align with the responsive design
tahmidrahman-dsi Dec 24, 2024
d4c1c57
fix: improve data handling
tahmidrahman-dsi Dec 24, 2024
400b303
feat: add new field `scannedFields` to gql input to track which field…
tahmidrahman-dsi Dec 24, 2024
70dd226
fix: minor amends
tahmidrahman-dsi Dec 30, 2024
892cc6a
Revert "fix: minor amends"
tahmidrahman-dsi Jan 1, 2025
7c55040
Revert "feat: add new field `scannedFields` to gql input to track whi…
tahmidrahman-dsi Jan 1, 2025
6589f9e
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 1, 2025
67fa6d5
wip: handle different status in reader component
tahmidrahman-dsi Jan 1, 2025
4f3e8e2
fix: remove usages of additional component
tahmidrahman-dsi Jan 2, 2025
3ce87c9
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 6, 2025
0723fb6
fix: remove status in id reader
tahmidrahman-dsi Jan 6, 2025
0996d0a
feat: introduce a `ID_VERIFICATION_BANNER` component
tahmidrahman-dsi Jan 6, 2025
745a2d9
fix: minor cleanup
tahmidrahman-dsi Jan 6, 2025
1b6b5f6
fix: remove hardcoded verified status modification
tahmidrahman-dsi Jan 7, 2025
271fdc9
refactor: REDIRECT -> LINK_BUTTON
tahmidrahman-dsi Jan 7, 2025
17145de
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 7, 2025
61c48f6
fix: forward params received from redirection to the callback request
tahmidrahman-dsi Jan 9, 2025
945cacd
refactor: interface names
tahmidrahman-dsi Jan 9, 2025
c7b1ce9
fix: misc amends
tahmidrahman-dsi Jan 9, 2025
633e8fd
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 9, 2025
57d0318
fix: typo and update code
tahmidrahman-dsi Jan 9, 2025
82b4601
fix: improve naming
tahmidrahman-dsi Jan 9, 2025
7e0dc81
fix: also eval params
tahmidrahman-dsi Jan 10, 2025
02c283f
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 14, 2025
f2368f5
fix: styles
tahmidrahman-dsi Jan 16, 2025
d6ea209
Merge branch 'develop' into feat/ocrvs-7978/qr-reader
tahmidrahman-dsi Jan 16, 2025
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
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@types/xregexp": "^3.0.29",
"@vitejs/plugin-react": "^4.2.1",
"apollo3-cache-persist": "^0.14.1",
"barcode-detector": "^2.3.1",
"bcryptjs": "^2.4.3",
"bowser": "^2.11.0",
"browser-image-compression": "^1.0.6",
Expand Down
57 changes: 50 additions & 7 deletions packages/client/src/components/form/FormFieldGenerator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,10 @@ import {
InitialValue,
DependencyInfo,
Ii18nButtonFormField,
REDIRECT,
IDocumentUploaderWithOptionsFormField
LINK_BUTTON,
IDocumentUploaderWithOptionsFormField,
ID_READER,
ID_VERIFICATION_BANNER
} from '@client/forms'
import { getValidationErrorsForForm, Errors } from '@client/forms/validation'
import { InputField } from '@client/components/form/InputField'
Expand Down Expand Up @@ -130,11 +132,19 @@ import { buttonMessages } from '@client/i18n/messages/buttons'
import { DateRangePickerForFormField } from '@client/components/DateRangePickerForFormField'
import { IAdvancedSearchFormState } from '@client/search/advancedSearch/utils'
import { UserDetails } from '@client/utils/userUtils'
import { BulletList, Divider, InputLabel, Stack } from '@opencrvs/components'
import {
BulletList,
Divider,
IDReader,
InputLabel,
Stack
} from '@opencrvs/components'
import { Heading2, Heading3 } from '@opencrvs/components/lib/Headings/Headings'
import { SignatureUploader } from './SignatureField/SignatureUploader'
import { ButtonField } from '@client/components/form/Button'
import { RedirectField } from '@client/components/form/Redirect'
import { LinkButtonField } from '@client/components/form/LinkButton'
import { ReaderGenerator } from './ReaderGenerator'
import { IDVerificationBanner } from './IDVerificationBanner'

const SignatureField = styled(Stack)`
margin-top: 8px;
Expand Down Expand Up @@ -263,6 +273,36 @@ const GeneratedInputField = React.memo<GeneratedInputFieldProps>(
</InputField>
)
}

if (fieldDefinition.type === ID_READER) {
return (
<IDReader
dividerLabel={fieldDefinition.dividerLabel}
manualInputInstructionLabel={
fieldDefinition.manualInputInstructionLabel
}
>
<ReaderGenerator
readers={fieldDefinition.readers}
form={values}
field={fieldDefinition}
draft={draftData}
fields={fields}
setFieldValue={setFieldValue}
/>
</IDReader>
)
}
if (fieldDefinition.type === ID_VERIFICATION_BANNER) {
return (
<IDVerificationBanner
type={fieldDefinition.bannerType}
idFieldName={fieldDefinition.idFieldName}
setFieldValue={setFieldValue}
/>
)
}

Comment on lines +296 to +305
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is form field component

if (fieldDefinition.type === DOCUMENT_UPLOADER_WITH_OPTION) {
return (
<InputField {...inputFieldProps}>
Expand Down Expand Up @@ -619,12 +659,15 @@ const GeneratedInputField = React.memo<GeneratedInputFieldProps>(
)
}

if (fieldDefinition.type === REDIRECT) {
if (fieldDefinition.type === LINK_BUTTON) {
return (
<RedirectField
to={fieldDefinition.options.url}
<LinkButtonField
form={values}
draft={draftData}
fieldDefinition={fieldDefinition}
fields={fields}
setFieldValue={setFieldValue}
isDisabled={disabled}
/>
)
}
Expand Down
118 changes: 118 additions & 0 deletions packages/client/src/components/form/IDVerificationBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* OpenCRVS is also distributed under the terms of the Civil Registration
* & Healthcare Disclaimer located at http://opencrvs.org/license.
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import React from 'react'
import { BannerType, IFormFieldValue } from '@client/forms'
import { useIntl } from 'react-intl'
import { Pill, Banner, Icon, Button, Text } from '@opencrvs/components'
import { messages } from '@client/i18n/messages/views/id-verification-banner'

export const IDVerificationBanner = ({
type,
idFieldName,
setFieldValue
}: {
type: BannerType
setFieldValue: (name: string, value: IFormFieldValue) => void
idFieldName: string
}) => {
const intl = useIntl()
const handleReset = () => {
setFieldValue(idFieldName, '')
}
if (type === 'pending') {
return (
<Banner.Container>
<Banner.Header type="pending">
<Pill
type="pending"
size="small"
pillTheme="dark"
label={
<>
<Icon name="QrCode" size="small" />
{intl.formatMessage(messages.pending.title)}
</>
}
/>
<Icon name="Clock" size="large" />
</Banner.Header>
<Banner.Body>
<Text variant="reg16" element="span">
{intl.formatMessage(messages.pending.description)}
</Text>
</Banner.Body>
<Banner.Footer justifyContent="flex-end">
<Button type="secondary" onClick={handleReset}>
{intl.formatMessage(messages.actions.reset)}
</Button>
</Banner.Footer>
</Banner.Container>
)
} else if (type === 'verified') {
return (
<Banner.Container>
<Banner.Header type="default">
<Pill
type="default"
size="small"
pillTheme="dark"
label={
<>
<Icon name="CircleWavyCheck" size="small" />
{intl.formatMessage(messages.success.title)}
</>
}
/>
<Icon name="FilledCheck" size="large" />
</Banner.Header>
<Banner.Body>
<Text variant="reg16" element="span">
{intl.formatMessage(messages.success.description)}
</Text>
</Banner.Body>
<Banner.Footer justifyContent="flex-end">
<Button type="secondary" onClick={handleReset}>
{intl.formatMessage(messages.actions.revoke)}
</Button>
</Banner.Footer>
</Banner.Container>
)
} else if (type === 'failed') {
return (
<Banner.Container>
<Banner.Header type="inactive">
<Pill
type="inactive"
size="small"
pillTheme="dark"
label={
<>
<Icon name="X" size="small" />
{intl.formatMessage(messages.failed.title)}
</>
}
/>
<Icon name="Close" size="large" />
</Banner.Header>
<Banner.Body>
<Text variant="reg16" element="span">
{intl.formatMessage(messages.failed.description)}
</Text>
</Banner.Body>
<Banner.Footer justifyContent="flex-end">
<Button type="secondary" onClick={handleReset}>
{intl.formatMessage(messages.actions.reset)}
</Button>
</Banner.Footer>
</Banner.Container>
)
} else return null
}
114 changes: 114 additions & 0 deletions packages/client/src/components/form/LinkButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* OpenCRVS is also distributed under the terms of the Civil Registration
* & Healthcare Disclaimer located at http://opencrvs.org/license.
*
* Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS.
*/
import {
IFormData,
IFormField,
IFormFieldValue,
IFormSectionData,
IHttpFormField,
Ii18nLinkButtonFormField
} from '@client/forms'
import { evalExpressionInFieldDefinition } from '@client/forms/utils'
import { getOfflineData } from '@client/offline/selectors'
import { getUserDetails } from '@client/profile/profileSelectors'
import React, { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useHttp } from './http'
import { Button, getTheme, Icon } from '@opencrvs/components'
import { useWindowSize } from '@opencrvs/components/src/hooks'

export const LinkButtonField = ({
fields,
form,
draft,
fieldDefinition,
setFieldValue,
isDisabled
}: {
fields: IFormField[]
form: IFormSectionData
draft: IFormData
fieldDefinition: Ii18nLinkButtonFormField
setFieldValue: (name: string, value: IFormFieldValue) => void
isDisabled?: boolean
}) => {
const config = useSelector(getOfflineData)
const user = useSelector(getUserDetails)
const windowSize = useWindowSize()
const theme = getTheme()
const {
options: {
url: to,
callback: { params }
}
} = fieldDefinition
const trigger = fields.find(
(f) => f.name === fieldDefinition.options.callback.trigger
)!
const onChange: Parameters<typeof useHttp>[1] = ({ data, error, loading }) =>
setFieldValue(trigger.name, { loading, data, error } as IFormFieldValue)
const [hasCallbackRequestBeenMade, setCallbackRequestBeenMade] =
useState(false)

const { call } = useHttp<string>(
trigger as IHttpFormField,
onChange,
form,
config,
draft,
user
)

useEffect(() => {
const urlParams = new URLSearchParams(window.location.search)
function checkParamsPresentInURL() {
for (const [key, value] of Object.entries(params)) {
if (urlParams.get(key) !== value) {
return false
}
}
return true
}
if (checkParamsPresentInURL() && !hasCallbackRequestBeenMade) {
call({
// forward params which are received after redirection to the callback request
params: Object.fromEntries(urlParams)
})
setCallbackRequestBeenMade(true)
}
}, [call, params, form, trigger, hasCallbackRequestBeenMade])
Comment on lines +72 to +87
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tahmidrahman-dsi how / where is this call/params functionality documented? It's a good method, but can be confusing for someone wanting to use the component.

return (
<Button
type="secondary"
size="large"
element="a"
fullWidth
disabled={isDisabled}
onClick={() => {
window.location.href = evalExpressionInFieldDefinition(
'`' + to + '`',
form,
config,
draft,
user
)
}}
>
{fieldDefinition.icon &&
(windowSize.width <= theme.grid.breakpoints.md ? (
<Icon name={fieldDefinition.icon.mobile} size="medium" />
) : (
<Icon name={fieldDefinition.icon.desktop} size="medium" />
))}
{fieldDefinition.label}
</Button>
)
}
Loading
Loading