@oneblink/apps-react 6.10.0-beta.4 → 6.10.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ import { DialogProps } from '@mui/material';
3
+ type Props = {
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ onConfirm: (abortSignal: AbortSignal) => unknown;
7
+ children: React.ReactNode;
8
+ title: string;
9
+ confirmButtonText: string;
10
+ confirmButtonIcon: React.ReactNode;
11
+ cypress?: {
12
+ dialog?: string;
13
+ confirmButton?: string;
14
+ cancelButton?: string;
15
+ error?: string;
16
+ title?: string;
17
+ };
18
+ TransitionProps?: DialogProps['TransitionProps'];
19
+ disabled?: boolean;
20
+ };
21
+ export default function ConfirmDialog({ isOpen, onClose, children, onConfirm, title, confirmButtonText, confirmButtonIcon, cypress, TransitionProps, disabled, }: Props): React.JSX.Element;
22
+ export {};
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { Dialog, DialogActions, DialogTitle, DialogContent, Button, Portal, } from '@mui/material';
3
+ import { LoadingButton } from '@mui/lab';
4
+ import useIsMounted from '../hooks/useIsMounted';
5
+ import ErrorSnackbar from './ErrorSnackbar';
6
+ export default function ConfirmDialog({ isOpen, onClose, children, onConfirm, title, confirmButtonText, confirmButtonIcon, cypress, TransitionProps, disabled, }) {
7
+ const isMounted = useIsMounted();
8
+ const [isConfirming, setIsConfirming] = React.useState(false);
9
+ const [error, setError] = React.useState(null);
10
+ const handleConfirm = React.useCallback(async () => {
11
+ setIsConfirming(true);
12
+ setError(null);
13
+ let newError = null;
14
+ const abortController = new AbortController();
15
+ try {
16
+ await onConfirm(abortController.signal);
17
+ }
18
+ catch (error) {
19
+ newError = error;
20
+ }
21
+ if (isMounted.current && !abortController.signal.aborted) {
22
+ setIsConfirming(false);
23
+ setError(newError);
24
+ }
25
+ }, [isMounted, onConfirm]);
26
+ return (React.createElement(React.Fragment, null,
27
+ React.createElement(Dialog, { open: isOpen, maxWidth: "sm", fullWidth: true, onClose: !isConfirming ? onClose : undefined, "data-cypress": cypress === null || cypress === void 0 ? void 0 : cypress.dialog, TransitionProps: {
28
+ onExiting: () => setError(null),
29
+ ...(TransitionProps ? TransitionProps : {}),
30
+ } },
31
+ React.createElement(DialogTitle, { "data-cypress": cypress === null || cypress === void 0 ? void 0 : cypress.title }, title),
32
+ React.createElement(DialogContent, { dividers: true }, children),
33
+ React.createElement(DialogActions, null,
34
+ React.createElement(Button, { disabled: isConfirming, onClick: onClose, "data-cypress": cypress === null || cypress === void 0 ? void 0 : cypress.cancelButton }, "Cancel"),
35
+ React.createElement(LoadingButton, { variant: "contained", loading: isConfirming, autoFocus: true, startIcon: confirmButtonIcon, loadingPosition: "start", onClick: handleConfirm, "data-cypress": cypress === null || cypress === void 0 ? void 0 : cypress.confirmButton, disabled: disabled }, confirmButtonText))),
36
+ React.createElement(Portal, null,
37
+ React.createElement(ErrorSnackbar, { open: !!error, onClose: setError },
38
+ React.createElement("span", { "data-cypress": cypress === null || cypress === void 0 ? void 0 : cypress.error }, error === null || error === void 0 ? void 0 : error.message)))));
39
+ }
40
+ //# sourceMappingURL=ConfirmDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmDialog.js","sourceRoot":"","sources":["../../src/components/ConfirmDialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport {\n Dialog,\n DialogActions,\n DialogTitle,\n DialogContent,\n Button,\n Portal,\n DialogProps,\n} from '@mui/material'\nimport { LoadingButton } from '@mui/lab'\nimport useIsMounted from '../hooks/useIsMounted'\nimport ErrorSnackbar from './ErrorSnackbar'\n\ntype Props = {\n isOpen: boolean\n onClose: () => void\n onConfirm: (abortSignal: AbortSignal) => unknown\n children: React.ReactNode\n title: string\n confirmButtonText: string\n confirmButtonIcon: React.ReactNode\n cypress?: {\n dialog?: string\n confirmButton?: string\n cancelButton?: string\n error?: string\n title?: string\n }\n TransitionProps?: DialogProps['TransitionProps']\n disabled?: boolean\n}\n\nexport default function ConfirmDialog({\n isOpen,\n onClose,\n children,\n onConfirm,\n title,\n confirmButtonText,\n confirmButtonIcon,\n cypress,\n TransitionProps,\n disabled,\n}: Props) {\n const isMounted = useIsMounted()\n const [isConfirming, setIsConfirming] = React.useState(false)\n const [error, setError] = React.useState<Error | null>(null)\n const handleConfirm = React.useCallback(async () => {\n setIsConfirming(true)\n setError(null)\n let newError = null\n\n const abortController = new AbortController()\n try {\n await onConfirm(abortController.signal)\n } catch (error) {\n newError = error as Error\n }\n\n if (isMounted.current && !abortController.signal.aborted) {\n setIsConfirming(false)\n setError(newError)\n }\n }, [isMounted, onConfirm])\n return (\n <React.Fragment>\n <Dialog\n open={isOpen}\n maxWidth=\"sm\"\n fullWidth\n onClose={!isConfirming ? onClose : undefined}\n data-cypress={cypress?.dialog}\n TransitionProps={{\n onExiting: () => setError(null),\n ...(TransitionProps ? TransitionProps : {}),\n }}\n >\n <DialogTitle data-cypress={cypress?.title}>{title}</DialogTitle>\n <DialogContent dividers>{children}</DialogContent>\n <DialogActions>\n <Button\n disabled={isConfirming}\n onClick={onClose}\n data-cypress={cypress?.cancelButton}\n >\n Cancel\n </Button>\n\n <LoadingButton\n variant=\"contained\"\n loading={isConfirming}\n autoFocus\n startIcon={confirmButtonIcon}\n loadingPosition=\"start\"\n onClick={handleConfirm}\n data-cypress={cypress?.confirmButton}\n disabled={disabled}\n >\n {confirmButtonText}\n </LoadingButton>\n </DialogActions>\n </Dialog>\n <Portal>\n <ErrorSnackbar open={!!error} onClose={setError}>\n <span data-cypress={cypress?.error}>{error?.message}</span>\n </ErrorSnackbar>\n </Portal>\n </React.Fragment>\n )\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ interface Props {
3
+ onClear: () => void;
4
+ select?: boolean;
5
+ value: unknown;
6
+ }
7
+ declare const _default: React.NamedExoticComponent<Props>;
8
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ import { Box, IconButton, Icon } from '@mui/material';
3
+ const InputClear = ({ onClear, select, value }) => {
4
+ if (!value || (Array.isArray(value) && !value.length))
5
+ return null;
6
+ return (React.createElement(Box, { mr: select ? 2 : 0, alignItems: "center", display: "flex" },
7
+ React.createElement(IconButton, { size: "small", onClick: onClear },
8
+ React.createElement(Icon, null, "close"))));
9
+ };
10
+ export default React.memo(InputClear);
11
+ //# sourceMappingURL=InputClear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputClear.js","sourceRoot":"","sources":["../../src/components/InputClear.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AAOrD,MAAM,UAAU,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAS,EAAE,EAAE;IACvD,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAA;IAClE,OAAO,CACL,oBAAC,GAAG,IAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAC,QAAQ,EAAC,OAAO,EAAC,MAAM;QACzD,oBAAC,UAAU,IAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,OAAO;YACvC,oBAAC,IAAI,gBAAa,CACP,CACT,CACP,CAAA;AACH,CAAC,CAAA;AAED,eAAe,KAAK,CAAC,IAAI,CAAQ,UAAU,CAAC,CAAA","sourcesContent":["import * as React from 'react'\nimport { Box, IconButton, Icon } from '@mui/material'\ninterface Props {\n onClear: () => void\n select?: boolean\n value: unknown\n}\n\nconst InputClear = ({ onClear, select, value }: Props) => {\n if (!value || (Array.isArray(value) && !value.length)) return null\n return (\n <Box mr={select ? 2 : 0} alignItems=\"center\" display=\"flex\">\n <IconButton size=\"small\" onClick={onClear}>\n <Icon>close</Icon>\n </IconButton>\n </Box>\n )\n}\n\nexport default React.memo<Props>(InputClear)\n"]}
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import { TextField } from '@mui/material';
3
+ type Props = React.ComponentProps<typeof TextField> & {
4
+ onClear?: () => void;
5
+ };
6
+ declare const _default: React.NamedExoticComponent<Props>;
7
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+ import { TextField } from '@mui/material';
3
+ import InputClear from './InputClear';
4
+ const InputField = ({ margin = 'dense', variant = 'outlined', size = 'small', InputProps, onClear, ...props }) => {
5
+ const inputProps = React.useMemo(() => {
6
+ if (InputProps)
7
+ return InputProps;
8
+ if (onClear) {
9
+ return {
10
+ endAdornment: (React.createElement(InputClear, { select: props.select, value: props.value, onClear: onClear })),
11
+ };
12
+ }
13
+ }, [InputProps, onClear, props.select, props.value]);
14
+ return (React.createElement(TextField, { variant: variant, margin: margin, InputProps: inputProps, size: size, ...props }));
15
+ };
16
+ export default React.memo(InputField);
17
+ //# sourceMappingURL=InputField.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputField.js","sourceRoot":"","sources":["../../src/components/InputField.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,UAAU,MAAM,cAAc,CAAA;AAMrC,MAAM,UAAU,GAAG,CAAC,EAClB,MAAM,GAAG,OAAO,EAChB,OAAO,GAAG,UAAU,EACpB,IAAI,GAAG,OAAO,EACd,UAAU,EACV,OAAO,EACP,GAAG,KAAK,EACF,EAAE,EAAE;IACV,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACpC,IAAI,UAAU;YAAE,OAAO,UAAU,CAAA;QACjC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO;gBACL,YAAY,EAAE,CACZ,oBAAC,UAAU,IACT,MAAM,EAAE,KAAK,CAAC,MAAM,EACpB,KAAK,EAAE,KAAK,CAAC,KAAK,EAClB,OAAO,EAAE,OAAO,GAChB,CACH;aACF,CAAA;QACH,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA;IAEpD,OAAO,CACL,oBAAC,SAAS,IACR,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,IAAI,KACN,KAAK,GACT,CACH,CAAA;AACH,CAAC,CAAA;AAED,eAAe,KAAK,CAAC,IAAI,CAAQ,UAAU,CAAC,CAAA","sourcesContent":["import * as React from 'react'\nimport { TextField } from '@mui/material'\nimport InputClear from './InputClear'\n\ntype Props = React.ComponentProps<typeof TextField> & {\n onClear?: () => void\n}\n\nconst InputField = ({\n margin = 'dense',\n variant = 'outlined',\n size = 'small',\n InputProps,\n onClear,\n ...props\n}: Props) => {\n const inputProps = React.useMemo(() => {\n if (InputProps) return InputProps\n if (onClear) {\n return {\n endAdornment: (\n <InputClear\n select={props.select}\n value={props.value}\n onClear={onClear}\n />\n ),\n }\n }\n }, [InputProps, onClear, props.select, props.value])\n\n return (\n <TextField\n variant={variant}\n margin={margin}\n InputProps={inputProps}\n size={size}\n {...props}\n />\n )\n}\n\nexport default React.memo<Props>(InputField)\n"]}
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import { Button } from '@mui/material';
3
+ type Props = {
4
+ loading: boolean;
5
+ } & React.ComponentProps<typeof Button>;
6
+ declare const _default: React.MemoExoticComponent<({ loading, color, disabled, ...props }: Props) => React.JSX.Element>;
7
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import { CircularProgress, Button } from '@mui/material';
3
+ const LoadingButton = ({ loading, color, disabled, ...props }) => {
4
+ return (React.createElement(Button, { color: color, disabled: loading || disabled, ...props },
5
+ React.createElement(React.Fragment, null,
6
+ React.createElement("span", { style: { opacity: loading ? 0 : undefined } }, props.children),
7
+ loading && (React.createElement(CircularProgress, { color: color, style: {
8
+ position: 'absolute',
9
+ }, size: 24 })))));
10
+ };
11
+ export default React.memo(LoadingButton);
12
+ //# sourceMappingURL=LoadingButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoadingButton.js","sourceRoot":"","sources":["../../src/components/LoadingButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAKxD,MAAM,aAAa,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAS,EAAE,EAAE;IACtE,OAAO,CACL,oBAAC,MAAM,IAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,IAAI,QAAQ,KAAM,KAAK;QAC5D;YACE,8BAAM,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAC9C,KAAK,CAAC,QAAQ,CACV;YACN,OAAO,IAAI,CACV,oBAAC,gBAAgB,IACf,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE;oBACL,QAAQ,EAAE,UAAU;iBACrB,EACD,IAAI,EAAE,EAAE,GACU,CACrB,CACA,CACI,CACV,CAAA;AACH,CAAC,CAAA;AACD,eAAe,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA","sourcesContent":["import * as React from 'react'\nimport { CircularProgress, Button } from '@mui/material'\n\ntype Props = {\n loading: boolean\n} & React.ComponentProps<typeof Button>\nconst LoadingButton = ({ loading, color, disabled, ...props }: Props) => {\n return (\n <Button color={color} disabled={loading || disabled} {...props}>\n <>\n <span style={{ opacity: loading ? 0 : undefined }}>\n {props.children}\n </span>\n {loading && (\n <CircularProgress\n color={color}\n style={{\n position: 'absolute',\n }}\n size={24}\n ></CircularProgress>\n )}\n </>\n </Button>\n )\n}\nexport default React.memo(LoadingButton)\n"]}
@@ -0,0 +1,8 @@
1
+ import * as React from 'react';
2
+ declare function SuccessSnackbar({ open, onClose, children, }: {
3
+ open: boolean;
4
+ children: React.ReactNode;
5
+ onClose: (isOpen: false) => void;
6
+ }): React.JSX.Element;
7
+ declare const _default: React.MemoExoticComponent<typeof SuccessSnackbar>;
8
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ import { Grid, Icon, Snackbar, SnackbarContent } from '@mui/material';
3
+ function SuccessSnackbar({ open, onClose, children, }) {
4
+ return (React.createElement(Snackbar, { anchorOrigin: {
5
+ vertical: 'bottom',
6
+ horizontal: 'right',
7
+ }, open: open, onClose: (event, reason) => {
8
+ if (reason === 'clickaway') {
9
+ return;
10
+ }
11
+ onClose(false);
12
+ }, autoHideDuration: 3000 },
13
+ React.createElement(SnackbarContent, { sx: { bgcolor: 'success.main' }, message: React.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
14
+ React.createElement(Grid, { item: true, xs: "auto" },
15
+ React.createElement(Icon, null, "check_circle")),
16
+ React.createElement(Grid, { item: true, xs: true }, children)) })));
17
+ }
18
+ export default React.memo(SuccessSnackbar);
19
+ //# sourceMappingURL=SuccessSnackbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SuccessSnackbar.js","sourceRoot":"","sources":["../../src/components/SuccessSnackbar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAErE,SAAS,eAAe,CAAC,EACvB,IAAI,EACJ,OAAO,EACP,QAAQ,GAKT;IACC,OAAO,CACL,oBAAC,QAAQ,IACP,YAAY,EAAE;YACZ,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,OAAO;SACpB,EACD,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;YACzB,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;gBAC3B,OAAM;YACR,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,EACD,gBAAgB,EAAE,IAAI;QAEtB,oBAAC,eAAe,IACd,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,EAC/B,OAAO,EACL,oBAAC,IAAI,IAAC,SAAS,QAAC,OAAO,EAAE,CAAC,EAAE,UAAU,EAAC,QAAQ;gBAC7C,oBAAC,IAAI,IAAC,IAAI,QAAC,EAAE,EAAC,MAAM;oBAClB,oBAAC,IAAI,uBAAoB,CACpB;gBACP,oBAAC,IAAI,IAAC,IAAI,QAAC,EAAE,UACV,QAAQ,CACJ,CACF,GAET,CACO,CACZ,CAAA;AACH,CAAC;AAED,eAAe,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA","sourcesContent":["import * as React from 'react'\nimport { Grid, Icon, Snackbar, SnackbarContent } from '@mui/material'\n\nfunction SuccessSnackbar({\n open,\n onClose,\n children,\n}: {\n open: boolean\n children: React.ReactNode\n onClose: (isOpen: false) => void\n}) {\n return (\n <Snackbar\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'right',\n }}\n open={open}\n onClose={(event, reason) => {\n if (reason === 'clickaway') {\n return\n }\n\n onClose(false)\n }}\n autoHideDuration={3000}\n >\n <SnackbarContent\n sx={{ bgcolor: 'success.main' }}\n message={\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs=\"auto\">\n <Icon>check_circle</Icon>\n </Grid>\n <Grid item xs>\n {children}\n </Grid>\n </Grid>\n }\n />\n </Snackbar>\n )\n}\n\nexport default React.memo(SuccessSnackbar)\n"]}
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ import { authService } from '@oneblink/apps';
3
+ declare function MfaDialog({ onClose, onCompleted, mfaSetup, }: {
4
+ onClose: () => void;
5
+ onCompleted: () => void;
6
+ mfaSetup: Awaited<ReturnType<typeof authService.setupMfa>> | undefined;
7
+ }): React.JSX.Element;
8
+ declare const _default: React.MemoExoticComponent<typeof MfaDialog>;
9
+ export default _default;
@@ -0,0 +1,86 @@
1
+ import * as React from 'react';
2
+ import { QRCodeSVG } from 'qrcode.react';
3
+ import { Box, Collapse, Dialog, Grid, Link, Typography, DialogContent, DialogActions, Button, } from '@mui/material';
4
+ import { authService } from '@oneblink/apps';
5
+ import useBooleanState from '../../hooks/useBooleanState';
6
+ import { CopyToClipBoardIconButton } from '../CopyToClipboardIconButton';
7
+ import SuccessSnackbar from '../SuccessSnackbar';
8
+ import InputField from '../InputField';
9
+ import LoadingButton from '../LoadingButton';
10
+ function MfaDialog({ onClose, onCompleted, mfaSetup, }) {
11
+ const [code, setState] = React.useState('');
12
+ const [hasSuccessfullySaved, showSuccessfullySaved, hideSuccessfullySaved] = useBooleanState(false);
13
+ const [isShowingSecretCode, showSecretCode, hideSecretCode] = useBooleanState(false);
14
+ const qrcodeValue = React.useMemo(() => {
15
+ if (mfaSetup) {
16
+ return authService.generateMfaQrCodeUrl(mfaSetup);
17
+ }
18
+ }, [mfaSetup]);
19
+ const [isSaving, startSaving, stopSaving] = useBooleanState(false);
20
+ const handleSave = React.useCallback(async () => {
21
+ startSaving();
22
+ if (!code || !mfaSetup) {
23
+ return;
24
+ }
25
+ await mfaSetup.mfaCodeCallback(code);
26
+ onCompleted();
27
+ stopSaving();
28
+ showSuccessfullySaved();
29
+ }, [
30
+ code,
31
+ mfaSetup,
32
+ onCompleted,
33
+ showSuccessfullySaved,
34
+ startSaving,
35
+ stopSaving,
36
+ ]);
37
+ return (React.createElement(React.Fragment, null,
38
+ React.createElement(Dialog, { open: !!mfaSetup, onClose: onClose, title: "Complete MFA Setup" },
39
+ React.createElement(DialogContent, { dividers: true },
40
+ React.createElement(React.Fragment, null,
41
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Authenticator App"),
42
+ React.createElement(Typography, { variant: "body2", paragraph: true },
43
+ "Authenticator apps like",
44
+ ' ',
45
+ React.createElement(Link, { href: "https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2", target: "_blank", rel: "noopener noreferrer" }, "Google Authenticator"),
46
+ ' ',
47
+ "and",
48
+ ' ',
49
+ React.createElement(Link, { href: "https://www.microsoft.com/en-us/security/mobile-authenticator-app", target: "_blank", rel: "noopener noreferrer" }, "Microsoft Authenticator"),
50
+ ' ',
51
+ "generate one-time passwords that are used as a second factor to verify your identity when prompted during sign-in."),
52
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Scan the QR code"),
53
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Use an authenticator app or browser extension to scan the QR code below."),
54
+ React.createElement(Box, { marginBottom: 2 },
55
+ React.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
56
+ React.createElement(Grid, { item: true, xs: false },
57
+ React.createElement(Box, { border: 1, padding: 2, borderRadius: 1, borderColor: "divider", display: "inline-block" },
58
+ React.createElement(QRCodeSVG, { value: qrcodeValue || '' }))),
59
+ React.createElement(Grid, { item: true, xs: true },
60
+ React.createElement(Typography, { variant: "caption", color: "text.secondary" },
61
+ "Having trouble scanning the QR code?",
62
+ ' ',
63
+ React.createElement(Link, { onClick: showSecretCode, component: "button" }, "Click here"),
64
+ ' ',
65
+ "to display the setup key which can be manually entered in your authenticator app.")))),
66
+ React.createElement(Collapse, { in: isShowingSecretCode },
67
+ React.createElement(Box, { marginBottom: 2 },
68
+ React.createElement(InputField, { label: "Setup Key", value: (mfaSetup === null || mfaSetup === void 0 ? void 0 : mfaSetup.secretCode) || '', fullWidth: true, InputProps: {
69
+ endAdornment: (React.createElement(CopyToClipBoardIconButton, { text: (mfaSetup === null || mfaSetup === void 0 ? void 0 : mfaSetup.secretCode) || '' })),
70
+ }, helperText: React.createElement(React.Fragment, null,
71
+ React.createElement(Link, { onClick: hideSecretCode, component: "button" }, "Click here"),
72
+ ' ',
73
+ "to hide the setup key") }))),
74
+ React.createElement(Typography, { variant: "subtitle2", gutterBottom: true }, "Verify App"),
75
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Enter the 6-digit code found in your authenticator app."),
76
+ React.createElement(InputField, { autoFocus: true, margin: "none", name: "code", label: "Code", fullWidth: true, placeholder: "XXXXXX", variant: "outlined", value: code, onChange: (event) => {
77
+ const newValue = event.target.value;
78
+ setState(() => newValue);
79
+ }, disabled: isSaving, "data-cypress": "mfa-dialog-code" }))),
80
+ React.createElement(DialogActions, null,
81
+ React.createElement(Button, { onClick: onClose, disabled: isSaving }, "Cancel"),
82
+ React.createElement(LoadingButton, { variant: "contained", color: "primary", loading: isSaving, onClick: handleSave }, "Save"))),
83
+ React.createElement(SuccessSnackbar, { open: hasSuccessfullySaved, onClose: hideSuccessfullySaved }, "MFA has been successfully setup.")));
84
+ }
85
+ export default React.memo(MfaDialog);
86
+ //# sourceMappingURL=MfaDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MfaDialog.js","sourceRoot":"","sources":["../../../src/components/mfa/MfaDialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport { QRCodeSVG } from 'qrcode.react'\nimport {\n Box,\n Collapse,\n Dialog,\n Grid,\n Link,\n Typography,\n DialogContent,\n DialogActions,\n Button,\n} from '@mui/material'\nimport { authService } from '@oneblink/apps'\nimport useBooleanState from '../../hooks/useBooleanState'\nimport { CopyToClipBoardIconButton } from '../CopyToClipboardIconButton'\nimport SuccessSnackbar from '../SuccessSnackbar'\nimport InputField from '../InputField'\nimport LoadingButton from '../LoadingButton'\n\nfunction MfaDialog({\n onClose,\n onCompleted,\n mfaSetup,\n}: {\n onClose: () => void\n onCompleted: () => void\n mfaSetup: Awaited<ReturnType<typeof authService.setupMfa>> | undefined\n}) {\n const [code, setState] = React.useState('')\n const [hasSuccessfullySaved, showSuccessfullySaved, hideSuccessfullySaved] =\n useBooleanState(false)\n const [isShowingSecretCode, showSecretCode, hideSecretCode] =\n useBooleanState(false)\n\n const qrcodeValue = React.useMemo(() => {\n if (mfaSetup) {\n return authService.generateMfaQrCodeUrl(mfaSetup)\n }\n }, [mfaSetup])\n\n const [isSaving, startSaving, stopSaving] = useBooleanState(false)\n const handleSave = React.useCallback(async () => {\n startSaving()\n if (!code || !mfaSetup) {\n return\n }\n\n await mfaSetup.mfaCodeCallback(code)\n onCompleted()\n stopSaving()\n showSuccessfullySaved()\n }, [\n code,\n mfaSetup,\n onCompleted,\n showSuccessfullySaved,\n startSaving,\n stopSaving,\n ])\n\n return (\n <React.Fragment>\n <Dialog open={!!mfaSetup} onClose={onClose} title=\"Complete MFA Setup\">\n <DialogContent dividers>\n <>\n <Typography variant=\"subtitle2\" gutterBottom>\n Authenticator App\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Authenticator apps like{' '}\n <Link\n href=\"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Google Authenticator\n </Link>{' '}\n and{' '}\n <Link\n href=\"https://www.microsoft.com/en-us/security/mobile-authenticator-app\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Microsoft Authenticator\n </Link>{' '}\n generate one-time passwords that are used as a second factor to\n verify your identity when prompted during sign-in.\n </Typography>\n <Typography variant=\"subtitle2\" gutterBottom>\n Scan the QR code\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Use an authenticator app or browser extension to scan the QR code\n below.\n </Typography>\n\n <Box marginBottom={2}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs={false}>\n <Box\n border={1}\n padding={2}\n borderRadius={1}\n borderColor=\"divider\"\n display=\"inline-block\"\n >\n <QRCodeSVG value={qrcodeValue || ''} />\n </Box>\n </Grid>\n <Grid item xs>\n <Typography variant=\"caption\" color=\"text.secondary\">\n Having trouble scanning the QR code?{' '}\n <Link onClick={showSecretCode} component=\"button\">\n Click here\n </Link>{' '}\n to display the setup key which can be manually entered in\n your authenticator app.\n </Typography>\n </Grid>\n </Grid>\n </Box>\n\n <Collapse in={isShowingSecretCode}>\n <Box marginBottom={2}>\n <InputField\n label=\"Setup Key\"\n value={mfaSetup?.secretCode || ''}\n fullWidth\n InputProps={{\n endAdornment: (\n <CopyToClipBoardIconButton\n text={mfaSetup?.secretCode || ''}\n />\n ),\n }}\n helperText={\n <>\n <Link onClick={hideSecretCode} component=\"button\">\n Click here\n </Link>{' '}\n to hide the setup key\n </>\n }\n />\n </Box>\n </Collapse>\n\n <Typography variant=\"subtitle2\" gutterBottom>\n Verify App\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Enter the 6-digit code found in your authenticator app.\n </Typography>\n\n <InputField\n autoFocus\n margin=\"none\"\n name=\"code\"\n label=\"Code\"\n fullWidth\n placeholder=\"XXXXXX\"\n variant=\"outlined\"\n value={code}\n onChange={(event) => {\n const newValue = event.target.value\n setState(() => newValue)\n }}\n disabled={isSaving}\n data-cypress=\"mfa-dialog-code\"\n />\n </>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose} disabled={isSaving}>\n Cancel\n </Button>\n <LoadingButton\n variant=\"contained\"\n color=\"primary\"\n loading={isSaving}\n onClick={handleSave}\n >\n Save\n </LoadingButton>\n </DialogActions>\n </Dialog>\n\n <SuccessSnackbar\n open={hasSuccessfullySaved}\n onClose={hideSuccessfullySaved}\n >\n MFA has been successfully setup.\n </SuccessSnackbar>\n </React.Fragment>\n )\n}\n\nexport default React.memo(MfaDialog)\n"]}
@@ -0,0 +1,46 @@
1
+ import * as React from 'react';
2
+ export declare const LargeIcon: import("@emotion/styled").StyledComponent<import("@mui/material").IconOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
3
+ ref?: ((instance: HTMLSpanElement | null) => void) | React.RefObject<HTMLSpanElement> | null | undefined;
4
+ }, "className" | "style" | "children" | "classes" | "sx" | "color" | "fontSize" | "baseClassName"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, {}, {}>;
5
+ type Props = {
6
+ ssoSetupUrl?: string;
7
+ isExternalIdentityProviderUser?: boolean;
8
+ };
9
+ /**
10
+ * React Component that provides a mechanism for app users to configure Multi
11
+ * Factor Authentication. `<AppUserMfaProvider />` must be provided above this
12
+ * component in the component tree.
13
+ *
14
+ * #### Example
15
+ *
16
+ * ```js
17
+ * import * as React from 'react'
18
+ * import {
19
+ * AppUserMfaProvider,
20
+ * MultiFactorAuthentication,
21
+ * } from '@oneblink/apps-react'
22
+ *
23
+ * function Component() {
24
+ * return <MultiFactorAuthentication />
25
+ * }
26
+ *
27
+ * function App() {
28
+ * return (
29
+ * <AppUserMfaProvider>
30
+ * <Component />
31
+ * </AppUserMfaProvider>
32
+ * )
33
+ * }
34
+ *
35
+ * const root = document.getElementById('root')
36
+ * if (root) {
37
+ * ReactDOM.render(<App />, root)
38
+ * }
39
+ * ```
40
+ *
41
+ * @param props
42
+ * @returns
43
+ * @group Components
44
+ */
45
+ export default function MultiFactorAuthentication({ ssoSetupUrl, isExternalIdentityProviderUser, }: Props): React.JSX.Element;
46
+ export {};
@@ -0,0 +1,112 @@
1
+ import * as React from 'react';
2
+ import { Box, Button, Chip, CircularProgress, Divider, Grid, Icon, Paper, Tooltip, Typography, styled, } from '@mui/material';
3
+ import { LoadingButton } from '@mui/lab';
4
+ import ConfirmDialog from '../ConfirmDialog';
5
+ import MfaDialog from './MfaDialog';
6
+ import ErrorSnackbar from '../ErrorSnackbar';
7
+ import MaterialIcon from '../MaterialIcon';
8
+ import useAppUserMfa from '../../hooks/useAppUserMfa';
9
+ import ErrorMessage from '../messages/ErrorMessage';
10
+ export const LargeIcon = styled(Icon)(({ theme }) => ({
11
+ fontSize: `${theme.typography.h4.fontSize} !important`,
12
+ }));
13
+ function MfaStatus({ isExternalIdentityProviderUser, isLoading, loadingError, loadAppUserMfa, isMfaEnabled, }) {
14
+ if (isExternalIdentityProviderUser)
15
+ return null;
16
+ if (isLoading) {
17
+ return (React.createElement(Box, { padding: 3 },
18
+ React.createElement(Grid, { container: true, justifyContent: "center" },
19
+ React.createElement(CircularProgress, null))));
20
+ }
21
+ if (loadingError) {
22
+ return (React.createElement("div", null,
23
+ React.createElement(ErrorMessage, { title: "Error Loading Multi Factor Authentication Configuration", onTryAgain: loadAppUserMfa }, loadingError.message)));
24
+ }
25
+ if (isMfaEnabled) {
26
+ return (React.createElement(Chip, { label: "Enabled", icon: React.createElement(MaterialIcon, { color: "success" }, "verified_user") }));
27
+ }
28
+ return (React.createElement(Chip, { label: "Disabled", icon: React.createElement(MaterialIcon, { color: "warning" }, "remove_moderator") }));
29
+ }
30
+ function MfaSetup({ ssoSetupUrl, isExternalIdentityProviderUser, }) {
31
+ const { setupError, isMfaEnabled, isDisablingMfa, isSettingUpMfa, mfaSetup, beginMfaSetup, cancelMfaSetup, completeMfaSetup, clearMfaSetupError, beginDisablingMfa, completeDisablingMfa, cancelDisablingMfa, } = useAppUserMfa();
32
+ if (ssoSetupUrl) {
33
+ return (React.createElement(Grid, { item: true },
34
+ React.createElement(Button, { variant: "outlined", size: "small", component: "a", href: ssoSetupUrl, target: "_blank", rel: "noopener noreferrer", "data-cypress": "configure-mfa-button" }, "Configure MFA")));
35
+ }
36
+ if (isExternalIdentityProviderUser) {
37
+ return (React.createElement(Grid, { item: true },
38
+ React.createElement(Tooltip, { title: "MFA must be configured in your login provider." },
39
+ React.createElement("span", null,
40
+ React.createElement(Button, { variant: "outlined", size: "small", disabled: isExternalIdentityProviderUser, "data-cypress": "configure-mfa-button" }, "Configure MFA")))));
41
+ }
42
+ return (React.createElement(React.Fragment, null,
43
+ React.createElement(Grid, { item: true },
44
+ React.createElement(Button, { variant: "outlined", size: "small", disabled: !isMfaEnabled, "data-cypress": "disable-mfa-button", onClick: beginDisablingMfa }, "Disable MFA"),
45
+ React.createElement(ConfirmDialog, { isOpen: isDisablingMfa, onClose: cancelDisablingMfa, onConfirm: completeDisablingMfa, title: "Please Confirm", confirmButtonText: "Disable MFA", confirmButtonIcon: React.createElement(MaterialIcon, null, "remove_moderator"), cypress: {
46
+ dialog: 'disable-mfa-dialog',
47
+ confirmButton: 'disable-mfa-dialog-confirm-button',
48
+ cancelButton: 'disable-mfa-dialog-cancel-button',
49
+ error: 'disable-mfa-dialog-error-message',
50
+ } },
51
+ React.createElement(Typography, { variant: "body2" }, "Are you sure want to disable multi factor authentication (MFA)?"))),
52
+ React.createElement(Grid, { item: true },
53
+ React.createElement(LoadingButton, { variant: "contained", size: "small", loading: isSettingUpMfa, disabled: isMfaEnabled, onClick: beginMfaSetup, "data-cypress": "setup-mfa-button" }, "Setup MFA"),
54
+ React.createElement(MfaDialog, { mfaSetup: mfaSetup, onClose: cancelMfaSetup, onCompleted: completeMfaSetup })),
55
+ React.createElement(ErrorSnackbar, { open: !!setupError, onClose: clearMfaSetupError },
56
+ React.createElement("span", { "data-cypress": "mfa-setup-error-message" }, setupError === null || setupError === void 0 ? void 0 : setupError.message))));
57
+ }
58
+ /**
59
+ * React Component that provides a mechanism for app users to configure Multi
60
+ * Factor Authentication. `<AppUserMfaProvider />` must be provided above this
61
+ * component in the component tree.
62
+ *
63
+ * #### Example
64
+ *
65
+ * ```js
66
+ * import * as React from 'react'
67
+ * import {
68
+ * AppUserMfaProvider,
69
+ * MultiFactorAuthentication,
70
+ * } from '@oneblink/apps-react'
71
+ *
72
+ * function Component() {
73
+ * return <MultiFactorAuthentication />
74
+ * }
75
+ *
76
+ * function App() {
77
+ * return (
78
+ * <AppUserMfaProvider>
79
+ * <Component />
80
+ * </AppUserMfaProvider>
81
+ * )
82
+ * }
83
+ *
84
+ * const root = document.getElementById('root')
85
+ * if (root) {
86
+ * ReactDOM.render(<App />, root)
87
+ * }
88
+ * ```
89
+ *
90
+ * @param props
91
+ * @returns
92
+ * @group Components
93
+ */
94
+ export default function MultiFactorAuthentication({ ssoSetupUrl, isExternalIdentityProviderUser, }) {
95
+ const { loadingError, isLoading, isMfaEnabled, loadAppUserMfa } = useAppUserMfa();
96
+ return (React.createElement(Grid, { item: true, xs: true, lg: 8 },
97
+ React.createElement(Box, { padding: 3 },
98
+ React.createElement(Paper, null,
99
+ React.createElement(Box, { padding: 3 },
100
+ React.createElement(Grid, { container: true, spacing: 2, alignItems: "center" },
101
+ React.createElement(Grid, { item: true, xs: true },
102
+ React.createElement(Typography, { variant: "h4", fontWeight: "light" },
103
+ "Multi Factor Authentication",
104
+ ' ',
105
+ React.createElement(MfaStatus, { loadAppUserMfa: loadAppUserMfa, isLoading: isLoading, loadingError: loadingError, isMfaEnabled: isMfaEnabled, isExternalIdentityProviderUser: !!isExternalIdentityProviderUser })),
106
+ React.createElement(Box, { marginY: 1 },
107
+ React.createElement(Divider, null)),
108
+ React.createElement(Typography, { variant: "body2", paragraph: true }, "Multi factor authentication (MFA), also known as two factor authentication (2FA), is a best practice that requires a second authentication factor in addition to user name and password sign-in credentials. We strongly recommend enabling MFA to enhance your account security."),
109
+ React.createElement(Grid, { container: true, justifyContent: "flex-end", spacing: 1 },
110
+ React.createElement(MfaSetup, { isExternalIdentityProviderUser: !!isExternalIdentityProviderUser, ssoSetupUrl: ssoSetupUrl || '' })))))))));
111
+ }
112
+ //# sourceMappingURL=MultiFactorAuthentication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MultiFactorAuthentication.js","sourceRoot":"","sources":["../../../src/components/mfa/MultiFactorAuthentication.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport {\n Box,\n Button,\n Chip,\n CircularProgress,\n Divider,\n Grid,\n Icon,\n Paper,\n Tooltip,\n Typography,\n styled,\n} from '@mui/material'\nimport { LoadingButton } from '@mui/lab'\nimport ConfirmDialog from '../ConfirmDialog'\nimport MfaDialog from './MfaDialog'\nimport ErrorSnackbar from '../ErrorSnackbar'\nimport MaterialIcon from '../MaterialIcon'\nimport useAppUserMfa from '../../hooks/useAppUserMfa'\nimport ErrorMessage from '../messages/ErrorMessage'\n\nexport const LargeIcon = styled(Icon)(({ theme }) => ({\n fontSize: `${theme.typography.h4.fontSize} !important`,\n}))\n\ntype Props = {\n ssoSetupUrl?: string\n isExternalIdentityProviderUser?: boolean\n}\n\ntype MfaStatusProps = {\n isExternalIdentityProviderUser: boolean\n isLoading: boolean\n isMfaEnabled: boolean\n loadAppUserMfa: () => void\n loadingError?: Error\n}\n\nfunction MfaStatus({\n isExternalIdentityProviderUser,\n isLoading,\n loadingError,\n loadAppUserMfa,\n isMfaEnabled,\n}: MfaStatusProps) {\n if (isExternalIdentityProviderUser) return null\n\n if (isLoading) {\n return (\n <Box padding={3}>\n <Grid container justifyContent=\"center\">\n <CircularProgress />\n </Grid>\n </Box>\n )\n }\n\n if (loadingError) {\n return (\n <div>\n <ErrorMessage\n title=\"Error Loading Multi Factor Authentication Configuration\"\n onTryAgain={loadAppUserMfa}\n >\n {loadingError.message}\n </ErrorMessage>\n </div>\n )\n }\n\n if (isMfaEnabled) {\n return (\n <Chip\n label=\"Enabled\"\n icon={<MaterialIcon color=\"success\">verified_user</MaterialIcon>}\n />\n )\n }\n\n return (\n <Chip\n label=\"Disabled\"\n icon={<MaterialIcon color=\"warning\">remove_moderator</MaterialIcon>}\n />\n )\n}\n\nfunction MfaSetup({\n ssoSetupUrl,\n isExternalIdentityProviderUser,\n}: {\n ssoSetupUrl: string\n isExternalIdentityProviderUser: boolean\n}) {\n const {\n setupError,\n isMfaEnabled,\n isDisablingMfa,\n isSettingUpMfa,\n mfaSetup,\n beginMfaSetup,\n cancelMfaSetup,\n completeMfaSetup,\n clearMfaSetupError,\n beginDisablingMfa,\n completeDisablingMfa,\n cancelDisablingMfa,\n } = useAppUserMfa()\n\n if (ssoSetupUrl) {\n return (\n <Grid item>\n <Button\n variant=\"outlined\"\n size=\"small\"\n component=\"a\"\n href={ssoSetupUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </Grid>\n )\n }\n\n if (isExternalIdentityProviderUser) {\n return (\n <Grid item>\n <Tooltip title=\"MFA must be configured in your login provider.\">\n <span>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={isExternalIdentityProviderUser}\n data-cypress=\"configure-mfa-button\"\n >\n Configure MFA\n </Button>\n </span>\n </Tooltip>\n </Grid>\n )\n }\n\n return (\n <>\n <Grid item>\n <Button\n variant=\"outlined\"\n size=\"small\"\n disabled={!isMfaEnabled}\n data-cypress=\"disable-mfa-button\"\n onClick={beginDisablingMfa}\n >\n Disable MFA\n </Button>\n <ConfirmDialog\n isOpen={isDisablingMfa}\n onClose={cancelDisablingMfa}\n onConfirm={completeDisablingMfa}\n title=\"Please Confirm\"\n confirmButtonText=\"Disable MFA\"\n confirmButtonIcon={<MaterialIcon>remove_moderator</MaterialIcon>}\n cypress={{\n dialog: 'disable-mfa-dialog',\n confirmButton: 'disable-mfa-dialog-confirm-button',\n cancelButton: 'disable-mfa-dialog-cancel-button',\n error: 'disable-mfa-dialog-error-message',\n }}\n >\n <Typography variant=\"body2\">\n Are you sure want to disable multi factor authentication (MFA)?\n </Typography>\n </ConfirmDialog>\n </Grid>\n\n <Grid item>\n <LoadingButton\n variant=\"contained\"\n size=\"small\"\n loading={isSettingUpMfa}\n disabled={isMfaEnabled}\n onClick={beginMfaSetup}\n data-cypress=\"setup-mfa-button\"\n >\n Setup MFA\n </LoadingButton>\n <MfaDialog\n mfaSetup={mfaSetup}\n onClose={cancelMfaSetup}\n onCompleted={completeMfaSetup}\n />\n </Grid>\n <ErrorSnackbar open={!!setupError} onClose={clearMfaSetupError}>\n <span data-cypress=\"mfa-setup-error-message\">\n {setupError?.message}\n </span>\n </ErrorSnackbar>\n </>\n )\n}\n/**\n * React Component that provides a mechanism for app users to configure Multi\n * Factor Authentication. `<AppUserMfaProvider />` must be provided above this\n * component in the component tree.\n *\n * #### Example\n *\n * ```js\n * import * as React from 'react'\n * import {\n * AppUserMfaProvider,\n * MultiFactorAuthentication,\n * } from '@oneblink/apps-react'\n *\n * function Component() {\n * return <MultiFactorAuthentication />\n * }\n *\n * function App() {\n * return (\n * <AppUserMfaProvider>\n * <Component />\n * </AppUserMfaProvider>\n * )\n * }\n *\n * const root = document.getElementById('root')\n * if (root) {\n * ReactDOM.render(<App />, root)\n * }\n * ```\n *\n * @param props\n * @returns\n * @group Components\n */\nexport default function MultiFactorAuthentication({\n ssoSetupUrl,\n isExternalIdentityProviderUser,\n}: Props) {\n const { loadingError, isLoading, isMfaEnabled, loadAppUserMfa } =\n useAppUserMfa()\n\n return (\n <Grid item xs={true} lg={8}>\n <Box padding={3}>\n <Paper>\n <Box padding={3}>\n <Grid container spacing={2} alignItems=\"center\">\n <Grid item xs>\n <Typography variant=\"h4\" fontWeight=\"light\">\n Multi Factor Authentication{' '}\n <MfaStatus\n loadAppUserMfa={loadAppUserMfa}\n isLoading={isLoading}\n loadingError={loadingError}\n isMfaEnabled={isMfaEnabled}\n isExternalIdentityProviderUser={\n !!isExternalIdentityProviderUser\n }\n />\n </Typography>\n <Box marginY={1}>\n <Divider />\n </Box>\n <Typography variant=\"body2\" paragraph>\n Multi factor authentication (MFA), also known as two factor\n authentication (2FA), is a best practice that requires a\n second authentication factor in addition to user name and\n password sign-in credentials. We strongly recommend enabling\n MFA to enhance your account security.\n </Typography>\n <Grid container justifyContent=\"flex-end\" spacing={1}>\n <MfaSetup\n isExternalIdentityProviderUser={\n !!isExternalIdentityProviderUser\n }\n ssoSetupUrl={ssoSetupUrl || ''}\n />\n </Grid>\n </Grid>\n </Grid>\n </Box>\n </Paper>\n </Box>\n </Grid>\n )\n}\n"]}
@@ -0,0 +1,103 @@
1
+ import * as React from 'react';
2
+ import { authService } from '@oneblink/apps';
3
+ type AppUserMfaState = {
4
+ isLoading: boolean;
5
+ isMfaEnabled: boolean;
6
+ loadingError?: Error;
7
+ isSettingUpMfa: boolean;
8
+ isDisablingMfa: boolean;
9
+ setupError?: Error;
10
+ mfaSetup?: Awaited<ReturnType<typeof authService.setupMfa>>;
11
+ };
12
+ export declare const AppUserMfaContext: React.Context<AppUserMfaState & {
13
+ beginMfaSetup: () => void;
14
+ cancelMfaSetup: () => void;
15
+ completeMfaSetup: () => void;
16
+ beginDisablingMfa: () => void;
17
+ cancelDisablingMfa: () => void;
18
+ completeDisablingMfa: () => void;
19
+ clearMfaSetupError: () => void;
20
+ loadAppUserMfa: () => void;
21
+ }>;
22
+ /**
23
+ * React Component that provides the context for the
24
+ * `useUserMeetsMfaRequirement()` hook and `<MultiFactorAuthentication />`
25
+ * component, to be used by components further down your component tree. **It
26
+ * should only be included in your component tree once and ideally at the root
27
+ * of the application.**
28
+ *
29
+ * #### Example
30
+ *
31
+ * ```js
32
+ * import * as React from 'react'
33
+ * import {
34
+ * AppUserMfaProvider,
35
+ * useUserMeetsMfaRequirement,
36
+ * } from '@oneblink/apps-react'
37
+ *
38
+ * function Component() {
39
+ * const { isLoading, userMeetsMfaRequirement } =
40
+ * useUserMeetsMfaRequirement(true)
41
+ * // use App User MFA Requirement details here
42
+ * }
43
+ *
44
+ * function App() {
45
+ * return (
46
+ * <AppUserMfaProvider isExternalIdentityProviderUser={false}>
47
+ * <Component />
48
+ * </AppUserMfaProvider>
49
+ * )
50
+ * }
51
+ *
52
+ * const root = document.getElementById('root')
53
+ * if (root) {
54
+ * ReactDOM.render(<App />, root)
55
+ * }
56
+ * ```
57
+ *
58
+ * @param props
59
+ * @returns
60
+ * @group Components
61
+ */
62
+ export declare function AppUserMfaProvider({ children, isExternalIdentityProviderUser, }: {
63
+ children: React.ReactNode;
64
+ isExternalIdentityProviderUser: boolean;
65
+ }): React.JSX.Element;
66
+ export default function useAppUserMfa(): AppUserMfaState & {
67
+ beginMfaSetup: () => void;
68
+ cancelMfaSetup: () => void;
69
+ completeMfaSetup: () => void;
70
+ beginDisablingMfa: () => void;
71
+ cancelDisablingMfa: () => void;
72
+ completeDisablingMfa: () => void;
73
+ clearMfaSetupError: () => void;
74
+ loadAppUserMfa: () => void;
75
+ };
76
+ /**
77
+ * React hook to get the state associated to the logged in user's MFA status.
78
+ * Will throw an Error if used outside of the `<AppUserMfaProvider />`
79
+ * component.
80
+ *
81
+ * Example
82
+ *
83
+ * ```js
84
+ * import { useUserMeetsMfaRequirement } from '@oneblink/apps-react'
85
+ *
86
+ * const isAppUserMfaRequired = true
87
+ *
88
+ * function Component() {
89
+ * const { isLoading, userMeetsMfaRequirement } =
90
+ * useUserMeetsMfaRequirement(isAppUserMfaRequired)
91
+ * }
92
+ * ```
93
+ *
94
+ * @returns
95
+ * @group Hooks
96
+ */
97
+ export declare function useUserMeetsMfaRequirement(isAppUserMfaRequired: boolean): {
98
+ isLoading: boolean;
99
+ loadingError: Error | undefined;
100
+ loadAppUserMfa: () => void;
101
+ userMeetsMfaRequirement: boolean;
102
+ };
103
+ export {};
@@ -0,0 +1,228 @@
1
+ import * as React from 'react';
2
+ import { authService } from '@oneblink/apps';
3
+ export const AppUserMfaContext = React.createContext({
4
+ isLoading: true,
5
+ isMfaEnabled: false,
6
+ isSettingUpMfa: false,
7
+ isDisablingMfa: false,
8
+ beginMfaSetup: () => { },
9
+ cancelMfaSetup: () => { },
10
+ completeMfaSetup: () => { },
11
+ beginDisablingMfa: () => { },
12
+ cancelDisablingMfa: () => { },
13
+ completeDisablingMfa: () => { },
14
+ clearMfaSetupError: () => { },
15
+ loadAppUserMfa: () => { },
16
+ });
17
+ /**
18
+ * React Component that provides the context for the
19
+ * `useUserMeetsMfaRequirement()` hook and `<MultiFactorAuthentication />`
20
+ * component, to be used by components further down your component tree. **It
21
+ * should only be included in your component tree once and ideally at the root
22
+ * of the application.**
23
+ *
24
+ * #### Example
25
+ *
26
+ * ```js
27
+ * import * as React from 'react'
28
+ * import {
29
+ * AppUserMfaProvider,
30
+ * useUserMeetsMfaRequirement,
31
+ * } from '@oneblink/apps-react'
32
+ *
33
+ * function Component() {
34
+ * const { isLoading, userMeetsMfaRequirement } =
35
+ * useUserMeetsMfaRequirement(true)
36
+ * // use App User MFA Requirement details here
37
+ * }
38
+ *
39
+ * function App() {
40
+ * return (
41
+ * <AppUserMfaProvider isExternalIdentityProviderUser={false}>
42
+ * <Component />
43
+ * </AppUserMfaProvider>
44
+ * )
45
+ * }
46
+ *
47
+ * const root = document.getElementById('root')
48
+ * if (root) {
49
+ * ReactDOM.render(<App />, root)
50
+ * }
51
+ * ```
52
+ *
53
+ * @param props
54
+ * @returns
55
+ * @group Components
56
+ */
57
+ export function AppUserMfaProvider({ children, isExternalIdentityProviderUser, }) {
58
+ const [state, setState] = React.useState({
59
+ isLoading: !isExternalIdentityProviderUser,
60
+ isMfaEnabled: false,
61
+ isSettingUpMfa: false,
62
+ isDisablingMfa: false,
63
+ });
64
+ const loadAppUserMfa = React.useCallback(async (abortSignal) => {
65
+ setState((currentState) => ({
66
+ ...currentState,
67
+ isLoading: true,
68
+ isMfaEnabled: false,
69
+ loadingError: undefined,
70
+ }));
71
+ try {
72
+ const newIsMfaEnabled = await authService.checkIsMfaEnabled();
73
+ if (!(abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted)) {
74
+ setState((currentState) => ({
75
+ ...currentState,
76
+ isLoading: false,
77
+ isMfaEnabled: newIsMfaEnabled,
78
+ }));
79
+ }
80
+ }
81
+ catch (error) {
82
+ setState((currentState) => ({
83
+ ...currentState,
84
+ isLoading: false,
85
+ loadingError: error,
86
+ }));
87
+ }
88
+ }, []);
89
+ const clearMfaSetupError = React.useCallback(() => {
90
+ setState((currentState) => ({
91
+ ...currentState,
92
+ setupError: undefined,
93
+ }));
94
+ }, []);
95
+ const cancelMfaSetup = React.useCallback(() => {
96
+ setState((currentState) => ({
97
+ ...currentState,
98
+ mfaSetup: undefined,
99
+ }));
100
+ }, []);
101
+ const completeMfaSetup = React.useCallback(() => {
102
+ setState((currentState) => ({
103
+ ...currentState,
104
+ isMfaEnabled: true,
105
+ mfaSetup: undefined,
106
+ }));
107
+ }, []);
108
+ const beginMfaSetup = React.useCallback(async () => {
109
+ setState((currentState) => ({
110
+ ...currentState,
111
+ isSettingUpMfa: true,
112
+ mfaSetup: undefined,
113
+ setupError: undefined,
114
+ }));
115
+ try {
116
+ const newMfaSetup = await authService.setupMfa();
117
+ setState((currentState) => ({
118
+ ...currentState,
119
+ isSettingUpMfa: false,
120
+ mfaSetup: newMfaSetup,
121
+ }));
122
+ }
123
+ catch (error) {
124
+ setState((currentState) => ({
125
+ ...currentState,
126
+ isSettingUpMfa: false,
127
+ setupError: error,
128
+ }));
129
+ }
130
+ }, []);
131
+ const beginDisablingMfa = React.useCallback(() => {
132
+ setState((currentState) => ({
133
+ ...currentState,
134
+ isDisablingMfa: true,
135
+ }));
136
+ }, []);
137
+ const cancelDisablingMfa = React.useCallback(() => {
138
+ setState((currentState) => ({
139
+ ...currentState,
140
+ isDisablingMfa: false,
141
+ }));
142
+ }, []);
143
+ const completeDisablingMfa = React.useCallback(async () => {
144
+ await authService.disableMfa();
145
+ setState((currentState) => ({
146
+ ...currentState,
147
+ isDisablingMfa: false,
148
+ isMfaEnabled: false,
149
+ }));
150
+ }, []);
151
+ React.useEffect(() => {
152
+ if (isExternalIdentityProviderUser) {
153
+ return;
154
+ }
155
+ loadAppUserMfa();
156
+ return () => { };
157
+ }, [isExternalIdentityProviderUser, loadAppUserMfa]);
158
+ const value = React.useMemo(() => {
159
+ return {
160
+ ...state,
161
+ clearMfaSetupError,
162
+ loadAppUserMfa,
163
+ beginMfaSetup,
164
+ cancelMfaSetup,
165
+ completeMfaSetup,
166
+ beginDisablingMfa,
167
+ cancelDisablingMfa,
168
+ completeDisablingMfa,
169
+ };
170
+ }, [
171
+ state,
172
+ clearMfaSetupError,
173
+ loadAppUserMfa,
174
+ beginMfaSetup,
175
+ cancelMfaSetup,
176
+ completeMfaSetup,
177
+ beginDisablingMfa,
178
+ cancelDisablingMfa,
179
+ completeDisablingMfa,
180
+ ]);
181
+ return (React.createElement(AppUserMfaContext.Provider, { value: value }, children));
182
+ }
183
+ export default function useAppUserMfa() {
184
+ return React.useContext(AppUserMfaContext);
185
+ }
186
+ /**
187
+ * React hook to get the state associated to the logged in user's MFA status.
188
+ * Will throw an Error if used outside of the `<AppUserMfaProvider />`
189
+ * component.
190
+ *
191
+ * Example
192
+ *
193
+ * ```js
194
+ * import { useUserMeetsMfaRequirement } from '@oneblink/apps-react'
195
+ *
196
+ * const isAppUserMfaRequired = true
197
+ *
198
+ * function Component() {
199
+ * const { isLoading, userMeetsMfaRequirement } =
200
+ * useUserMeetsMfaRequirement(isAppUserMfaRequired)
201
+ * }
202
+ * ```
203
+ *
204
+ * @returns
205
+ * @group Hooks
206
+ */
207
+ export function useUserMeetsMfaRequirement(isAppUserMfaRequired) {
208
+ const context = React.useContext(AppUserMfaContext);
209
+ if (!context) {
210
+ throw new Error(`"useUserMeetsMfaRequirement" hook was used outside of the "<AppUserMfaProvider />" component's children.`);
211
+ }
212
+ const { isLoading, loadingError, loadAppUserMfa, isMfaEnabled } = context;
213
+ if (!isAppUserMfaRequired) {
214
+ return {
215
+ isLoading: false,
216
+ loadingError: undefined,
217
+ userMeetsMfaRequirement: true,
218
+ loadAppUserMfa,
219
+ };
220
+ }
221
+ return {
222
+ isLoading,
223
+ loadingError,
224
+ loadAppUserMfa,
225
+ userMeetsMfaRequirement: isMfaEnabled,
226
+ };
227
+ }
228
+ //# sourceMappingURL=useAppUserMfa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAppUserMfa.js","sourceRoot":"","sources":["../../src/hooks/useAppUserMfa.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react'\nimport { authService } from '@oneblink/apps'\n\ntype AppUserMfaState = {\n isLoading: boolean\n isMfaEnabled: boolean\n loadingError?: Error\n isSettingUpMfa: boolean\n isDisablingMfa: boolean\n setupError?: Error\n mfaSetup?: Awaited<ReturnType<typeof authService.setupMfa>>\n}\n\nexport const AppUserMfaContext = React.createContext<\n AppUserMfaState & {\n beginMfaSetup: () => void\n cancelMfaSetup: () => void\n completeMfaSetup: () => void\n beginDisablingMfa: () => void\n cancelDisablingMfa: () => void\n completeDisablingMfa: () => void\n clearMfaSetupError: () => void\n loadAppUserMfa: () => void\n }\n>({\n isLoading: true,\n isMfaEnabled: false,\n isSettingUpMfa: false,\n isDisablingMfa: false,\n beginMfaSetup: () => {},\n cancelMfaSetup: () => {},\n completeMfaSetup: () => {},\n beginDisablingMfa: () => {},\n cancelDisablingMfa: () => {},\n completeDisablingMfa: () => {},\n clearMfaSetupError: () => {},\n loadAppUserMfa: () => {},\n})\n\n/**\n * React Component that provides the context for the\n * `useUserMeetsMfaRequirement()` hook and `<MultiFactorAuthentication />`\n * component, to be used by components further down your component tree. **It\n * should only be included in your component tree once and ideally at the root\n * of the application.**\n *\n * #### Example\n *\n * ```js\n * import * as React from 'react'\n * import {\n * AppUserMfaProvider,\n * useUserMeetsMfaRequirement,\n * } from '@oneblink/apps-react'\n *\n * function Component() {\n * const { isLoading, userMeetsMfaRequirement } =\n * useUserMeetsMfaRequirement(true)\n * // use App User MFA Requirement details here\n * }\n *\n * function App() {\n * return (\n * <AppUserMfaProvider isExternalIdentityProviderUser={false}>\n * <Component />\n * </AppUserMfaProvider>\n * )\n * }\n *\n * const root = document.getElementById('root')\n * if (root) {\n * ReactDOM.render(<App />, root)\n * }\n * ```\n *\n * @param props\n * @returns\n * @group Components\n */\nexport function AppUserMfaProvider({\n children,\n isExternalIdentityProviderUser,\n}: {\n children: React.ReactNode\n isExternalIdentityProviderUser: boolean\n}) {\n const [state, setState] = React.useState<AppUserMfaState>({\n isLoading: !isExternalIdentityProviderUser,\n isMfaEnabled: false,\n isSettingUpMfa: false,\n isDisablingMfa: false,\n })\n\n const loadAppUserMfa = React.useCallback(\n async (abortSignal?: AbortSignal) => {\n setState((currentState) => ({\n ...currentState,\n isLoading: true,\n isMfaEnabled: false,\n loadingError: undefined,\n }))\n try {\n const newIsMfaEnabled = await authService.checkIsMfaEnabled()\n if (!abortSignal?.aborted) {\n setState((currentState) => ({\n ...currentState,\n isLoading: false,\n isMfaEnabled: newIsMfaEnabled,\n }))\n }\n } catch (error) {\n setState((currentState) => ({\n ...currentState,\n isLoading: false,\n loadingError: error as Error,\n }))\n }\n },\n [],\n )\n\n const clearMfaSetupError = React.useCallback(() => {\n setState((currentState) => ({\n ...currentState,\n setupError: undefined,\n }))\n }, [])\n\n const cancelMfaSetup = React.useCallback(() => {\n setState((currentState) => ({\n ...currentState,\n mfaSetup: undefined,\n }))\n }, [])\n\n const completeMfaSetup = React.useCallback(() => {\n setState((currentState) => ({\n ...currentState,\n isMfaEnabled: true,\n mfaSetup: undefined,\n }))\n }, [])\n\n const beginMfaSetup = React.useCallback(async () => {\n setState((currentState) => ({\n ...currentState,\n isSettingUpMfa: true,\n mfaSetup: undefined,\n setupError: undefined,\n }))\n try {\n const newMfaSetup = await authService.setupMfa()\n setState((currentState) => ({\n ...currentState,\n isSettingUpMfa: false,\n mfaSetup: newMfaSetup,\n }))\n } catch (error) {\n setState((currentState) => ({\n ...currentState,\n isSettingUpMfa: false,\n setupError: error as Error,\n }))\n }\n }, [])\n\n const beginDisablingMfa = React.useCallback(() => {\n setState((currentState) => ({\n ...currentState,\n isDisablingMfa: true,\n }))\n }, [])\n\n const cancelDisablingMfa = React.useCallback(() => {\n setState((currentState) => ({\n ...currentState,\n isDisablingMfa: false,\n }))\n }, [])\n\n const completeDisablingMfa = React.useCallback(async () => {\n await authService.disableMfa()\n setState((currentState) => ({\n ...currentState,\n isDisablingMfa: false,\n isMfaEnabled: false,\n }))\n }, [])\n\n React.useEffect(() => {\n if (isExternalIdentityProviderUser) {\n return\n }\n\n loadAppUserMfa()\n\n return () => {}\n }, [isExternalIdentityProviderUser, loadAppUserMfa])\n\n const value = React.useMemo(() => {\n return {\n ...state,\n clearMfaSetupError,\n loadAppUserMfa,\n beginMfaSetup,\n cancelMfaSetup,\n completeMfaSetup,\n beginDisablingMfa,\n cancelDisablingMfa,\n completeDisablingMfa,\n }\n }, [\n state,\n clearMfaSetupError,\n loadAppUserMfa,\n beginMfaSetup,\n cancelMfaSetup,\n completeMfaSetup,\n beginDisablingMfa,\n cancelDisablingMfa,\n completeDisablingMfa,\n ])\n\n return (\n <AppUserMfaContext.Provider value={value}>\n {children}\n </AppUserMfaContext.Provider>\n )\n}\n\nexport default function useAppUserMfa() {\n return React.useContext(AppUserMfaContext)\n}\n\n/**\n * React hook to get the state associated to the logged in user's MFA status.\n * Will throw an Error if used outside of the `<AppUserMfaProvider />`\n * component.\n *\n * Example\n *\n * ```js\n * import { useUserMeetsMfaRequirement } from '@oneblink/apps-react'\n *\n * const isAppUserMfaRequired = true\n *\n * function Component() {\n * const { isLoading, userMeetsMfaRequirement } =\n * useUserMeetsMfaRequirement(isAppUserMfaRequired)\n * }\n * ```\n *\n * @returns\n * @group Hooks\n */\nexport function useUserMeetsMfaRequirement(isAppUserMfaRequired: boolean) {\n const context = React.useContext(AppUserMfaContext)\n\n if (!context) {\n throw new Error(\n `\"useUserMeetsMfaRequirement\" hook was used outside of the \"<AppUserMfaProvider />\" component's children.`,\n )\n }\n\n const { isLoading, loadingError, loadAppUserMfa, isMfaEnabled } = context\n\n if (!isAppUserMfaRequired) {\n return {\n isLoading: false,\n loadingError: undefined,\n userMeetsMfaRequirement: true,\n loadAppUserMfa,\n }\n }\n\n return {\n isLoading,\n loadingError,\n loadAppUserMfa,\n userMeetsMfaRequirement: isMfaEnabled,\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -26,4 +26,6 @@ export { default as useGoogleJsApiLoader } from './hooks/useGoogleJsApiLoader';
26
26
  export { default as ProgressBar } from './components/renderer/ProgressBar';
27
27
  export { default as PaymentReceipt } from './PaymentReceipt';
28
28
  export { default as PaymentForm } from './components/payments/PaymentForm';
29
+ export { default as MultiFactorAuthentication } from './components/mfa/MultiFactorAuthentication';
30
+ export { AppUserMfaProvider, useUserMeetsMfaRequirement, } from './hooks/useAppUserMfa';
29
31
  export * from './types/form';
package/dist/index.js CHANGED
@@ -26,5 +26,7 @@ export { default as useGoogleJsApiLoader } from './hooks/useGoogleJsApiLoader';
26
26
  export { default as ProgressBar } from './components/renderer/ProgressBar';
27
27
  export { default as PaymentReceipt } from './PaymentReceipt';
28
28
  export { default as PaymentForm } from './components/payments/PaymentForm';
29
+ export { default as MultiFactorAuthentication } from './components/mfa/MultiFactorAuthentication';
30
+ export { AppUserMfaProvider, useUserMeetsMfaRequirement, } from './hooks/useAppUserMfa';
29
31
  export * from './types/form';
30
32
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,wBAAwB,IAAI,YAAY,EACxC,sBAAsB,GACvB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAA;AAC5F,OAAO,EAAE,OAAO,IAAI,mCAAmC,EAAE,MAAM,4DAA4D,CAAA;AAC3H,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,uDAAuD,CAAA;AACjH,OAAO,EAAE,OAAO,IAAI,+BAA+B,EAAE,MAAM,wDAAwD,CAAA;AACnH,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,uDAAuD,CAAA;AACjH,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,+CAA+C,CAAA;AAEjG,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAClF,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,wBAAwB,GACzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAiB,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EACL,OAAO,IAAI,OAAO,EAClB,mBAAmB,GAEpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,OAAO,IAAI,qBAAqB,EAChC,iCAAiC,GAElC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,OAAO,IAAI,SAAS,EACpB,qBAAqB,GAEtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,OAAO,IAAI,gBAAgB,GAE5B,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAClF,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,wCAAwC,CAAA;AAClG,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAE9E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mCAAmC,CAAA;AAE1E,cAAc,cAAc,CAAA","sourcesContent":["export {\n OneBlinkFormBaseProps,\n OneBlinkFormControlledProps,\n OneBlinkFormUncontrolled as OneBlinkForm,\n OneBlinkFormControlled,\n} from './OneBlinkForm'\nexport { default as OneBlinkAutoSaveForm } from './OneBlinkAutoSaveForm'\nexport { default as OneBlinkReadOnlyForm } from './OneBlinkReadOnlyForm'\nexport { OneBlinkFormStoreProvider } from './components/formStore/OneBlinkFormStoreProvider'\nexport { default as OneBlinkFormStoreClearFiltersButton } from './components/formStore/OneBlinkFormStoreClearFiltersButton'\nexport { default as OneBlinkFormStoreColumnsButton } from './components/formStore/OneBlinkFormStoreColumnsButton'\nexport { default as OneBlinkFormStoreDownloadButton } from './components/formStore/OneBlinkFormStoreDownloadButton'\nexport { default as OneBlinkFormStoreRefreshButton } from './components/formStore/OneBlinkFormStoreRefreshButton'\nexport { default as OneBlinkFormStoreTable } from './components/formStore/OneBlinkFormStoreTable'\n\nexport { default as useIsMounted } from './hooks/useIsMounted'\nexport { default as useBooleanState } from './hooks/useBooleanState'\nexport { default as useNullableState } from './hooks/useNullableState'\nexport { default as useClickOutsideElement } from './hooks/useClickOutsideElement'\nexport {\n default as useIsOffline,\n IsOfflineContextProvider,\n} from './hooks/useIsOffline'\nexport { default as useLogin, UseLoginValue } from './hooks/useLogin'\nexport { default as useSignUp } from './hooks/useSignUp'\nexport {\n default as useAuth,\n AuthContextProvider,\n AuthContextValue,\n} from './hooks/useAuth'\nexport {\n default as usePendingSubmissions,\n PendingSubmissionsContextProvider,\n PendingSubmissionsContextValue,\n} from './hooks/usePendingSubmissions'\nexport {\n default as useDrafts,\n DraftsContextProvider,\n DraftsContextValue,\n} from './hooks/useDrafts'\nexport {\n default as useLoadDataState,\n LoadDataState,\n} from './hooks/useLoadDataState'\nexport { default as useLoadResourcesState } from './hooks/useLoadResourcesState'\nexport { default as useLoadDataEffect } from './hooks/useLoadDataEffect'\nexport { default as useFormSubmissionState } from './hooks/useFormSubmissionState'\nexport { default as useFormSubmissionAutoSaveState } from './hooks/useFormSubmissionAutoSaveState'\nexport { default as useGoogleJsApiLoader } from './hooks/useGoogleJsApiLoader'\n\nexport { default as ProgressBar } from './components/renderer/ProgressBar'\nexport { default as PaymentReceipt } from './PaymentReceipt'\nexport { default as PaymentForm } from './components/payments/PaymentForm'\n\nexport * from './types/form'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,wBAAwB,IAAI,YAAY,EACxC,sBAAsB,GACvB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAA;AAC5F,OAAO,EAAE,OAAO,IAAI,mCAAmC,EAAE,MAAM,4DAA4D,CAAA;AAC3H,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,uDAAuD,CAAA;AACjH,OAAO,EAAE,OAAO,IAAI,+BAA+B,EAAE,MAAM,wDAAwD,CAAA;AACnH,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,uDAAuD,CAAA;AACjH,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,+CAA+C,CAAA;AAEjG,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACpE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACtE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAClF,OAAO,EACL,OAAO,IAAI,YAAY,EACvB,wBAAwB,GACzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAiB,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EACL,OAAO,IAAI,OAAO,EAClB,mBAAmB,GAEpB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,OAAO,IAAI,qBAAqB,EAChC,iCAAiC,GAElC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,OAAO,IAAI,SAAS,EACpB,qBAAqB,GAEtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,OAAO,IAAI,gBAAgB,GAE5B,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAChF,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AACxE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAClF,OAAO,EAAE,OAAO,IAAI,8BAA8B,EAAE,MAAM,wCAAwC,CAAA;AAClG,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAE9E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,mCAAmC,CAAA;AAE1E,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,4CAA4C,CAAA;AAEjG,OAAO,EACL,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,cAAc,cAAc,CAAA","sourcesContent":["export {\n OneBlinkFormBaseProps,\n OneBlinkFormControlledProps,\n OneBlinkFormUncontrolled as OneBlinkForm,\n OneBlinkFormControlled,\n} from './OneBlinkForm'\nexport { default as OneBlinkAutoSaveForm } from './OneBlinkAutoSaveForm'\nexport { default as OneBlinkReadOnlyForm } from './OneBlinkReadOnlyForm'\nexport { OneBlinkFormStoreProvider } from './components/formStore/OneBlinkFormStoreProvider'\nexport { default as OneBlinkFormStoreClearFiltersButton } from './components/formStore/OneBlinkFormStoreClearFiltersButton'\nexport { default as OneBlinkFormStoreColumnsButton } from './components/formStore/OneBlinkFormStoreColumnsButton'\nexport { default as OneBlinkFormStoreDownloadButton } from './components/formStore/OneBlinkFormStoreDownloadButton'\nexport { default as OneBlinkFormStoreRefreshButton } from './components/formStore/OneBlinkFormStoreRefreshButton'\nexport { default as OneBlinkFormStoreTable } from './components/formStore/OneBlinkFormStoreTable'\n\nexport { default as useIsMounted } from './hooks/useIsMounted'\nexport { default as useBooleanState } from './hooks/useBooleanState'\nexport { default as useNullableState } from './hooks/useNullableState'\nexport { default as useClickOutsideElement } from './hooks/useClickOutsideElement'\nexport {\n default as useIsOffline,\n IsOfflineContextProvider,\n} from './hooks/useIsOffline'\nexport { default as useLogin, UseLoginValue } from './hooks/useLogin'\nexport { default as useSignUp } from './hooks/useSignUp'\nexport {\n default as useAuth,\n AuthContextProvider,\n AuthContextValue,\n} from './hooks/useAuth'\nexport {\n default as usePendingSubmissions,\n PendingSubmissionsContextProvider,\n PendingSubmissionsContextValue,\n} from './hooks/usePendingSubmissions'\nexport {\n default as useDrafts,\n DraftsContextProvider,\n DraftsContextValue,\n} from './hooks/useDrafts'\nexport {\n default as useLoadDataState,\n LoadDataState,\n} from './hooks/useLoadDataState'\nexport { default as useLoadResourcesState } from './hooks/useLoadResourcesState'\nexport { default as useLoadDataEffect } from './hooks/useLoadDataEffect'\nexport { default as useFormSubmissionState } from './hooks/useFormSubmissionState'\nexport { default as useFormSubmissionAutoSaveState } from './hooks/useFormSubmissionAutoSaveState'\nexport { default as useGoogleJsApiLoader } from './hooks/useGoogleJsApiLoader'\n\nexport { default as ProgressBar } from './components/renderer/ProgressBar'\nexport { default as PaymentReceipt } from './PaymentReceipt'\nexport { default as PaymentForm } from './components/payments/PaymentForm'\n\nexport { default as MultiFactorAuthentication } from './components/mfa/MultiFactorAuthentication'\n\nexport {\n AppUserMfaProvider,\n useUserMeetsMfaRequirement,\n} from './hooks/useAppUserMfa'\n\nexport * from './types/form'\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oneblink/apps-react",
3
3
  "description": "Helper functions for OneBlink apps in ReactJS.",
4
- "version": "6.10.0-beta.4",
4
+ "version": "6.10.0-beta.5",
5
5
  "author": "OneBlink <developers@oneblink.io> (https://oneblink.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/oneblink/apps-react/issues"
@@ -29,6 +29,7 @@
29
29
  "lodash.debounce": "^4.0.8",
30
30
  "lodash.throttle": "^4.1.1",
31
31
  "morph-expressions": "^1.1.1",
32
+ "qrcode.react": "^4.1.0",
32
33
  "query-string": "^8.1.0",
33
34
  "react-google-recaptcha": "^3.1.0",
34
35
  "react-input-mask": "^2.0.4",
@@ -46,8 +47,8 @@
46
47
  "@mui/lab": "^5.0.0-alpha.152",
47
48
  "@mui/material": "^5.15.6",
48
49
  "@mui/x-date-pickers": "^6.19.2",
49
- "@oneblink/apps": "^14.1.0-beta.1",
50
- "@oneblink/release-cli": "^3.3.0",
50
+ "@oneblink/apps": "17.2.0-beta.3",
51
+ "@oneblink/release-cli": "^3.2.2",
51
52
  "@oneblink/types": "github:oneblink/types",
52
53
  "@types/blueimp-load-image": "^5.16.6",
53
54
  "@types/color": "^3.0.6",