@opexa/portal-components 0.0.181 → 0.0.182

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.
@@ -4,6 +4,10 @@ export interface PopupStore {
4
4
  setOpen: (open: boolean) => void;
5
5
  '~touched': boolean;
6
6
  }
7
+ export interface KYCPopupStore extends PopupStore {
8
+ step: number;
9
+ setStep: (step: number) => void;
10
+ }
7
11
  export type GameLaunchStore__Details = {
8
12
  status: 'WAITING';
9
13
  game?: null;
@@ -60,7 +64,7 @@ interface GlobalStore {
60
64
  message: MessageStore;
61
65
  sidebar: PopupStore;
62
66
  sidebar__mobile: PopupStore;
63
- kyc: PopupStore;
67
+ kyc: KYCPopupStore;
64
68
  spotBonus: PopupStore;
65
69
  }
66
70
  export declare const useGlobalStore: import("zustand").UseBoundStore<import("zustand").StoreApi<GlobalStore>>;
@@ -193,6 +193,16 @@ export const useGlobalStore = create((set) => ({
193
193
  },
194
194
  }));
195
195
  },
196
+ step: 1,
197
+ setStep(step) {
198
+ set((prev) => ({
199
+ kyc: {
200
+ ...prev.kyc,
201
+ step,
202
+ '~touched': true,
203
+ },
204
+ }));
205
+ },
196
206
  '~touched': false,
197
207
  },
198
208
  sidebar: {
@@ -22,6 +22,8 @@ export function AccountVerification(props) {
22
22
  const profileCompletion = profileCompletionQuery.data;
23
23
  const verificationQuery = useMemberVerificationQuery();
24
24
  const verification = verificationQuery.data;
25
+ const isKycCompleted = (verification && verification.status === 'VERIFIED') ||
26
+ verification?.status === 'PENDING';
25
27
  const empty = verification == null;
26
28
  const pending = verification?.status === 'PENDING';
27
29
  const success = verification?.status === 'VERIFIED' || verification?.status === 'APPROVED';
@@ -29,7 +31,7 @@ export function AccountVerification(props) {
29
31
  verification?.status === 'UNVERIFIED';
30
32
  return (_jsxs("div", { className: props.className, children: [_jsxs("div", { className: "flex items-start gap-8", children: [_jsxs("div", { className: "grow", children: [_jsx("h2", { className: "text-text-secondary-700 text-sm font-semibold", children: "Verify your identity" }), _jsx("p", { className: "text-text-tertiary-600 text-sm", children: "Complete personal verification to unlock all features and enhance account security." })] }), _jsx(ark.svg, { asChild: true, className: twMerge('size-6', profileCompletion?.accountVerification
31
33
  ? 'text-text-success-primary'
32
- : 'text-text-warning-primary'), children: profileCompletion?.accountVerification ? (_jsx(CheckVerified02Icon, {})) : (_jsx(AlertCircleIcon, {})) })] }), _jsxs("div", { className: "shadow-xs border-border-secondary bg-bg-primary-alt mt-5 rounded-xl border", children: [_jsx("div", { className: "px-4 py-5", children: _jsxs("ul", { className: "space-y-lg", children: [_jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(FileCheck02Icon, {}), "Basic Information"] }), _jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(CreditCard02Icon, {}), "Government ID"] }), _jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(FaceIdIcon, {}), "Facial Verification"] })] }) }), _jsx("div", { className: "border-border-secondary flex justify-end border-t px-4 py-3 lg:py-xl", children: (!verification || verification?.status === 'REJECTED') && (_jsx(Button, { size: "sm", className: "w-full lg:w-fit", onClick: () => globalStore.kyc.setOpen(true), children: "Verify Now" })) })] }), success && _jsx(VerificationSuccess, {}), pending && _jsx(VerificationInProgress, {}), failed && _jsx(VerificationFailed, {}), empty && _jsx(VerificationNotSubmitted, {})] }));
34
+ : 'text-text-warning-primary'), children: profileCompletion?.accountVerification ? (_jsx(CheckVerified02Icon, {})) : (_jsx(AlertCircleIcon, {})) })] }), _jsxs("div", { className: "shadow-xs border-border-secondary bg-bg-primary-alt mt-5 rounded-xl border", children: [_jsx("div", { className: "px-4 py-5", children: _jsxs("ul", { className: "space-y-lg", children: [_jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(FileCheck02Icon, {}), "Basic Information"] }), _jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(CreditCard02Icon, {}), "Government ID"] }), _jsxs("li", { className: "text-text-secondary-700 icon:size-6 icon:text-text-nav-item-button-icon flex items-center gap-lg", children: [_jsx(FaceIdIcon, {}), "Facial Verification"] })] }) }), _jsx("div", { className: "border-border-secondary flex justify-end border-t px-4 py-3 lg:py-xl", children: !isKycCompleted && (_jsx(Button, { size: "sm", className: "w-full lg:w-fit", onClick: () => globalStore.kyc.setOpen(true), children: "Verify Now" })) })] }), success && _jsx(VerificationSuccess, {}), pending && _jsx(VerificationInProgress, {}), failed && _jsx(VerificationFailed, {}), empty && _jsx(VerificationNotSubmitted, {})] }));
33
35
  }
34
36
  function VerificationNotSubmitted() {
35
37
  return (_jsxs("div", { className: "shadow-xs border-border-primary bg-bg-primary-alt mt-6 flex flex-col gap-2 rounded-xl border p-xl lg:flex-row", children: [_jsx("div", { className: "border-text-warning-primary/10 flex size-[2.1875rem] items-center justify-center rounded-full border-2", children: _jsx("div", { className: "border-text-warning-primary/30 flex size-[1.666625rem] items-center justify-center rounded-full border-2", children: _jsx(AlertCircleIcon, { className: "text-text-warning-primary size-5" }) }) }), _jsx("p", { className: "text-text-tertiary-600 max-w-[800px] text-sm", children: "To serve you better and unlock all features, please provide valid identification documents. This helps protect your account and makes recovery easier if ever needed." })] }));
@@ -10,6 +10,18 @@ export function AutoOpen() {
10
10
  const { data: session } = useSessionQuery();
11
11
  const { data: account } = useAccountQuery();
12
12
  const { data: verification } = useMemberVerificationQuery();
13
+ const isBasicInfoCompleted = account?.realName !== null && account?.birthDay !== null;
14
+ const isUploadCompleted = verification &&
15
+ verification.idFrontImage !== null &&
16
+ verification.selfieImage !== null;
17
+ const isRejected = Boolean(verification?.status === 'REJECTED');
18
+ const step = !isBasicInfoCompleted
19
+ ? 1
20
+ : isRejected
21
+ ? 2
22
+ : isUploadCompleted
23
+ ? 3
24
+ : 2;
13
25
  const shouldOpen = !account?.realName ||
14
26
  !account?.birthDay ||
15
27
  !verification ||
@@ -24,5 +36,10 @@ export function AutoOpen() {
24
36
  return;
25
37
  }
26
38
  }, [kycStore, session?.status, shouldOpen]);
39
+ useEffect(() => {
40
+ if (step) {
41
+ kycStore.setStep(step);
42
+ }
43
+ }, [kycStore, step]);
27
44
  return null;
28
45
  }
@@ -5,7 +5,6 @@ import { isNil, omitBy, size } from 'lodash-es';
5
5
  import { useEffect } from 'react';
6
6
  import { useForm } from 'react-hook-form';
7
7
  import invariant from 'tiny-invariant';
8
- import { useInterval } from 'usehooks-ts';
9
8
  import { z } from 'zod';
10
9
  import { useAccountQuery } from '../../client/hooks/useAccountQuery.js';
11
10
  import { useUpdateAccountMutation } from '../../client/hooks/useUpdateAccountMutation.js';
@@ -56,7 +55,6 @@ export function BasicInformation() {
56
55
  const accountQuery = useAccountQuery();
57
56
  const account = accountQuery.data;
58
57
  const completed = account != null && account.realName != null && account.birthDay != null;
59
- useInterval(() => kyc.setStep(2), completed ? 1000 : null);
60
58
  const updateAccountMutation = useUpdateAccountMutation({
61
59
  onError(error) {
62
60
  toaster.error({
@@ -11,7 +11,7 @@ import { Button } from '../../ui/Button/index.js';
11
11
  import { Dialog } from '../../ui/Dialog/index.js';
12
12
  import { useKYCContext } from './KYCContext.js';
13
13
  import NoCameraError from './NoCameraError.js';
14
- import { dataURLtoBlob, resizeFile, showPermissionInstructions } from './utils.js';
14
+ import { cameraErrorDescriptions, dataURLtoBlob, resizeFile, showPermissionInstructions, } from './utils.js';
15
15
  export function CaptureIdDocument() {
16
16
  const kyc = useKYCContext();
17
17
  const { mutate, isPending } = useUploadImageFileMutation();
@@ -210,5 +210,10 @@ export function CaptureIdDocument() {
210
210
  return (_jsx(_Fragment, { children: _jsxs("div", { className: "w-full p-4 lg:w-[640px] lg:px-12 lg:py-4xl", children: [_jsxs("button", { type: "button", className: "flex items-center gap-lg lg:absolute lg:right-5 lg:top-5 lg:p-md", onClick: () => {
211
211
  stopCamera();
212
212
  kyc.setCapturing(null);
213
- }, children: [_jsx(XIcon, { className: "text-text-senary-300 hidden size-5 lg:block" }), _jsx(ArrowLeftIcon, { className: "text-text-senary-300 size-5 lg:hidden" }), _jsx("span", { className: "text-lg font-semibold lg:hidden", children: "Back" })] }), _jsx(Dialog.Title, { className: "mt-6 text-center text-lg font-semibold lg:mt-0", children: "Take a Picture of Your Front ID" }), _jsxs(Dialog.Description, { className: "lg:max-w-auto text-text-tertiary-600 mx-auto mt-sm max-w-[19rem] text-center text-sm lg:max-w-full", children: ["Make sure your ID is clearly visible, well-lit, and not blurry.", _jsx("br", { className: "hidden lg:block" }), " Avoid glare or reflections, and ensure all corners are within the frame."] }), _jsx("div", { id: "parent", className: "border-border-disabled mt-6 aspect-video rounded-xl border bg-black lg:mt-8", children: ['NotAllowedError', 'NotReadableError', 'NotFoundError'].includes(cameraError) ? (_jsx(NoCameraError, { onRetry: checkCameraPermission, title: "Camera Access Required", description: "We were unable to detect a camera on your device. Please ensure it is properly connected and\r\n accessible to this application." })) : captureInProgress ? (_jsxs("div", { className: "relative flex h-fit w-full flex-col items-center justify-center", children: [_jsx("video", { ref: videoElement, autoPlay: true, muted: true, playsInline: true, className: "relative h-[60vh] w-full rounded-lg object-cover md:aspect-video md:max-h-[333px]", width: "100%", height: "100%" }), _jsxs("div", { className: "pointer-events-none absolute inset-0 z-50", children: [_jsx("div", { className: twMerge('absolute inset-0 w-full bg-black/60 backdrop-blur-sm [clip-path:polygon(0%_0%,0%_100%,calc(50%-158px)_100%,calc(50%-158px)_calc(50%-100px),calc(50%+158px)_calc(50%-100px),calc(50%+158px)_calc(50%+100px),calc(50%-158px)_calc(50%+100px),calc(50%-158px)_100%,100%_100%,100%_0%)] md:[clip-path:polygon(0%_0%,0%_100%,calc(50%-225px)_100%,calc(50%-225px)_calc(50%-142px),calc(50%+225px)_calc(50%-142px),calc(50%+225px)_calc(50%+142px),calc(50%-225px)_calc(50%+142px),calc(50%-225px)_100%,100%_100%,100%_0%)]') }), _jsx("div", { className: "absolute inset-0 z-[60] mx-auto flex items-center justify-center", children: _jsx("div", { className: "relative aspect-[1011/638] h-[200px] md:h-[295px] md:w-[450px]", children: _jsx("div", { className: "h-full w-full rounded-lg border-4 border-dashed border-[#B54708]" }) }) })] })] })) : (_jsx("div", { ref: containerRef, className: "relative grid h-full w-full place-items-center rounded-lg object-contain md:max-h-[333px]" })) }), captureInProgress ? (_jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-6 px-xl disabled:opacity-100 lg:mt-8 lg:w-fit", onClick: captureAndProcessImage, children: ["Capture Image", _jsx(Camera01Icon, { className: "size-5" })] })) : selectedImage && selectedImage.src ? (_jsxs("div", { className: "mt-4 flex flex-col items-center justify-center gap-2 md:flex-row", children: [_jsxs(Button, { className: "lg:w-fit", type: "button", onClick: retakePhoto, children: ["Retake Photo ", _jsx(Camera01Icon, { className: "ml-1" })] }), _jsxs(Button, { className: "lg:w-fit", type: "button", onClick: uploadPhoto, disabled: isPending, children: ["Use Photo ", _jsx(CheckCircleIcon, { className: "ml-1" })] })] })) : (_jsxs(Button, { className: twMerge('disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-6 px-xl disabled:opacity-100 lg:mt-8 lg:w-fit', cameraError && 'hidden'), onClick: startCamera, disabled: isLoading || !!videoStream, children: [isLoading ? 'Starting Camera...' : 'Start Camera', _jsx(Camera01Icon, { className: "size-5" })] }))] }) }));
213
+ }, children: [_jsx(XIcon, { className: "text-text-senary-300 hidden size-5 lg:block" }), _jsx(ArrowLeftIcon, { className: "text-text-senary-300 size-5 lg:hidden" }), _jsx("span", { className: "text-lg font-semibold lg:hidden", children: "Back" })] }), _jsx(Dialog.Title, { className: "mt-6 text-center text-lg font-semibold lg:mt-0", children: "Take a Picture of Your Front ID" }), _jsxs(Dialog.Description, { className: "lg:max-w-auto text-text-tertiary-600 mx-auto mt-sm max-w-[19rem] text-center text-sm lg:max-w-full", children: ["Make sure your ID is clearly visible, well-lit, and not blurry.", _jsx("br", { className: "hidden lg:block" }), " Avoid glare or reflections, and ensure all corners are within the frame."] }), _jsx("div", { id: "parent", className: "border-border-disabled mt-6 aspect-video rounded-xl border bg-black lg:mt-8", children: [
214
+ 'NotAllowedError',
215
+ 'NotReadableError',
216
+ 'NotFoundError',
217
+ 'NoIdDetectedError',
218
+ ].includes(cameraError) ? (_jsx(NoCameraError, { onRetry: checkCameraPermission, title: "Camera Access Required", description: cameraErrorDescriptions[cameraError] })) : captureInProgress ? (_jsxs("div", { className: "relative flex h-fit w-full flex-col items-center justify-center", children: [_jsx("video", { ref: videoElement, autoPlay: true, muted: true, playsInline: true, className: "relative h-[50vh] w-full rounded-lg object-cover md:aspect-video md:max-h-[333px]", width: "100%", height: "100%" }), _jsxs("div", { className: "pointer-events-none absolute inset-0 z-50", children: [_jsx("div", { className: twMerge('absolute inset-0 w-full bg-black/60 backdrop-blur-sm [clip-path:polygon(0%_0%,0%_100%,calc(50%-158px)_100%,calc(50%-158px)_calc(50%-100px),calc(50%+158px)_calc(50%-100px),calc(50%+158px)_calc(50%+100px),calc(50%-158px)_calc(50%+100px),calc(50%-158px)_100%,100%_100%,100%_0%)] md:[clip-path:polygon(0%_0%,0%_100%,calc(50%-225px)_100%,calc(50%-225px)_calc(50%-142px),calc(50%+225px)_calc(50%-142px),calc(50%+225px)_calc(50%+142px),calc(50%-225px)_calc(50%+142px),calc(50%-225px)_100%,100%_100%,100%_0%)]') }), _jsx("div", { className: "absolute inset-0 z-[60] mx-auto flex items-center justify-center", children: _jsx("div", { className: "relative aspect-[1011/638] h-[200px] md:h-[295px] md:w-[450px]", children: _jsx("div", { className: "h-full w-full rounded-lg border-4 border-dashed border-[#B54708]" }) }) })] })] })) : (_jsx("div", { ref: containerRef, className: "relative grid h-full w-full place-items-center rounded-lg object-contain md:max-h-[333px]" })) }), captureInProgress ? (_jsxs(Button, { className: "disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-6 px-xl disabled:opacity-100 lg:mt-8 lg:w-fit", onClick: captureAndProcessImage, children: ["Capture Image", _jsx(Camera01Icon, { className: "size-5" })] })) : selectedImage && selectedImage.src ? (_jsxs("div", { className: "mt-4 flex flex-col items-center justify-center gap-2 md:flex-row", children: [_jsxs(Button, { className: "lg:w-fit", type: "button", onClick: retakePhoto, children: ["Retake Photo ", _jsx(Camera01Icon, { className: "ml-1" })] }), _jsxs(Button, { className: "lg:w-fit", type: "button", onClick: uploadPhoto, disabled: isPending, children: ["Use Photo ", _jsx(CheckCircleIcon, { className: "ml-1" })] })] })) : (_jsxs(Button, { className: twMerge('disabled:bg-bg-disabled disabled:text-text-disabled mx-auto mt-6 px-xl disabled:opacity-100 lg:mt-8 lg:w-fit', cameraError && 'hidden'), onClick: startCamera, disabled: isLoading || !!videoStream, children: [isLoading ? 'Starting Camera...' : 'Start Camera', _jsx(Camera01Icon, { className: "size-5" })] }))] }) }));
214
219
  }
@@ -15,10 +15,12 @@ import { validateFile } from './utils.js';
15
15
  function FileUpload({ value, name, hasDescription, onChange, captureObject, }) {
16
16
  const kyc = useKYCContext();
17
17
  const { mutate, isPending } = useUploadImageFileMutation();
18
+ const [isLoading, setIsLoading] = useState(false);
18
19
  const [uploadedImageUrl, setUploadedImageUrl] = useState(null);
19
20
  async function pollImage(id) {
20
21
  if (!id)
21
22
  return;
23
+ setIsLoading(true);
22
24
  const poll = createPoll(async () => {
23
25
  const session = await getSession();
24
26
  invariant(session.status === 'authenticated');
@@ -34,11 +36,12 @@ function FileUpload({ value, name, hasDescription, onChange, captureObject, }) {
34
36
  console.error('Image not found or failed to load');
35
37
  }
36
38
  setUploadedImageUrl(image?.url ?? null);
39
+ setIsLoading(false);
37
40
  }
38
41
  useEffect(() => {
39
42
  pollImage(value);
40
43
  }, [value]);
41
- if (isPending) {
44
+ if (isPending || isLoading) {
42
45
  return (_jsx("div", { className: "border-border-primary bg-bg-primary flex min-h-[11.25rem] flex-col justify-center gap-lg rounded-xl border px-3xl py-xl", children: _jsx("div", { className: "flex items-center justify-center", children: _jsx("span", { className: "text-text-secondary-700", children: "Uploading..." }) }) }));
43
46
  }
44
47
  function handleFileChange(event) {
@@ -84,7 +84,7 @@ export function IdentityVerification() {
84
84
  placeOfBirth: '',
85
85
  sourceOfIncome: '',
86
86
  });
87
- kyc.setStep(3);
87
+ globalStore.kyc.setStep(3);
88
88
  }
89
89
  else {
90
90
  updateMemberVerification({
@@ -94,7 +94,7 @@ export function IdentityVerification() {
94
94
  idFrontImage: values.idFrontImage,
95
95
  },
96
96
  });
97
- kyc.setStep(3);
97
+ globalStore.kyc.setStep(3);
98
98
  }
99
99
  }
100
100
  useEffect(() => {
@@ -1,8 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { noop } from 'lodash-es';
3
+ import { useShallow } from 'zustand/shallow';
4
+ import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
3
5
  import { Progress } from '../../ui/Progress/index.js';
4
- import { useKYCContext } from './KYCContext.js';
5
6
  export function Indicator() {
6
- const kyc = useKYCContext();
7
- return (_jsxs("div", { className: "flex items-center justify-center gap-1.5 py-3", children: [_jsx(Progress.Root, { className: "w-10 shrink-0", value: kyc.step > 1 ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) }), _jsx(Progress.Root, { className: "w-10 shrink-0", value: kyc.step > 2 ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) }), _jsx(Progress.Root, { className: "w-10 shrink-0", value: kyc.step === 3 && kyc.done ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) })] }));
7
+ const globalStore = useGlobalStore(useShallow((ctx) => ({
8
+ kyc: ctx.kyc,
9
+ })));
10
+ return (_jsxs("div", { className: "flex items-center justify-center gap-1.5 py-3", children: [_jsx(Progress.Root, { className: "w-10 shrink-0", value: globalStore.kyc.step > 1 ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) }), _jsx(Progress.Root, { className: "w-10 shrink-0", value: globalStore.kyc.step > 2 ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) }), _jsx(Progress.Root, { className: "w-10 shrink-0", value: globalStore.kyc.step === 3 ? 100 : 0, onValueChange: noop, children: _jsx(Progress.Track, { className: "bg-bg-primary-hover", children: _jsx(Progress.Range, {}) }) })] }));
8
11
  }
@@ -24,5 +24,5 @@ export function KYC(props) {
24
24
  if (!details.open) {
25
25
  localStorage.setItem(KYC_PROMPT_LOCALSTORAGE_KEY, '');
26
26
  }
27
- }, lazyMount: true, unmountOnExit: true, onExitComplete: kyc.reset, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: _jsxs(Dialog.Content, { className: "bg-bg-primary-alt lg:min-h-auto mx-auto min-h-full w-full lg:w-fit", children: [kyc.capturing === 'SELFIE' && _jsx(CaptureSelfie, {}), kyc.capturing === 'ID_DOCUMENT' && _jsx(CaptureIdDocument, {}), !kyc.capturing && (_jsxs("div", { className: "w-full p-3xl lg:w-[400px]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: props.logo, alt: "", width: 200, height: 100, className: "mx-auto mb-5 block h-7.5 w-auto", draggable: false }), _jsx(Indicator, {}), kyc.step === 1 && _jsx(BasicInformation, {}), kyc.step === 2 && _jsx(IdentityVerification, {}), kyc.step === 3 && _jsx(PersonalInformation, {})] }))] }) })] }) }) }));
27
+ }, lazyMount: true, unmountOnExit: true, onExitComplete: kyc.reset, closeOnEscape: true, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: _jsxs(Dialog.Content, { className: "bg-bg-primary-alt lg:min-h-auto mx-auto min-h-full w-full lg:w-fit", children: [kyc.capturing === 'SELFIE' && _jsx(CaptureSelfie, {}), kyc.capturing === 'ID_DOCUMENT' && _jsx(CaptureIdDocument, {}), !kyc.capturing && (_jsxs("div", { className: "w-full p-3xl lg:w-[400px]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: props.logo, alt: "", width: 200, height: 100, className: "mx-auto mb-5 block h-7.5 w-auto", draggable: false }), _jsx(Indicator, {}), globalStore.kyc.step === 1 && _jsx(BasicInformation, {}), globalStore.kyc.step === 2 && _jsx(IdentityVerification, {}), globalStore.kyc.step === 3 && _jsx(PersonalInformation, {})] }))] }) })] }) }) }));
28
28
  }
@@ -6,3 +6,4 @@ export declare const MAX_NAME_LENGTH = 52;
6
6
  export declare const SUPPORTED_FILES: string[];
7
7
  export declare function validateFile(file: File): "File too large" | "File name too long" | "Unsupported file" | undefined;
8
8
  export declare function showPermissionInstructions(): void;
9
+ export declare const cameraErrorDescriptions: Record<string, string>;
@@ -70,3 +70,9 @@ export function showPermissionInstructions() {
70
70
  `;
71
71
  alert(instructions);
72
72
  }
73
+ export const cameraErrorDescriptions = {
74
+ NotAllowedError: 'We were unable to detect a camera on your device. Please ensure it is properly connected and accessible to this application.',
75
+ NotReadableError: 'We were unable to access your camera. Please check if another application is using it or try reconnecting your device.',
76
+ NotFoundError: 'No camera was found. Please connect a camera and try again.',
77
+ NoIdDetectedError: 'No ID document was detected in the frame. Please ensure your ID is clearly visible and try again.',
78
+ };
@@ -84,7 +84,7 @@ function RemainingTimeTilTomorrow() {
84
84
  });
85
85
  },
86
86
  });
87
- return (_jsxs("div", { className: "flex items-center space-x-0.5", children: [_jsx(CheckIcon, { className: "size-5" }), _jsx("p", { children: remainingTime.toString() })] }));
87
+ return (_jsxs("div", { className: "flex items-center space-x-0.5", children: [_jsx(CheckIcon, { className: "size-5" }), _jsxs("p", { children: ["Available in ", remainingTime.toString()] })] }));
88
88
  }
89
89
  function Rules(props) {
90
90
  const [open, setOpen] = useControllableState({
@@ -4,6 +4,7 @@ import { useShallow } from 'zustand/shallow';
4
4
  import { useControllableState } from '../../client/hooks/useControllableState.js';
5
5
  import { useDisclosure } from '../../client/hooks/useDisclosure.js';
6
6
  import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
7
+ import { useMemberVerificationQuery } from '../../client/hooks/useMemberVerificationQuery.js';
7
8
  import { useRemainingTime } from '../../client/hooks/useRemainingTime.js';
8
9
  import { CheckIcon } from '../../icons/CheckIcon.js';
9
10
  import { CoinsHandIcon } from '../../icons/CoinsHandIcon.js';
@@ -19,17 +20,20 @@ import { RemainingTime } from './RemainingTime.js';
19
20
  export function OnboardingQuest() {
20
21
  const quest = useQuestContext();
21
22
  const disclosure = useDisclosure();
23
+ const { data: verification } = useMemberVerificationQuery();
22
24
  const globalStore = useGlobalStore(useShallow((ctx) => ({
23
25
  kyc: ctx.kyc,
24
26
  depositWithdrawal: ctx.depositWithdrawal,
25
27
  })));
26
28
  const remainingTime = useRemainingTime(quest.endDateTime);
27
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "border-border-primary bg-bg-tertiary flex h-full w-full flex-col space-y-6 rounded-2xl border px-4 pb-6 pt-5", children: [_jsxs("div", { className: "flex flex-col space-y-2", children: [_jsxs("div", { className: "flex max-h-7.5 items-start justify-between", children: [_jsx(RemainingTime, {}), _jsx("button", { type: "button", className: "bg-bg-primary border-border-secondary flex size-9 cursor-pointer items-center justify-center rounded-md border", onClick: () => disclosure.setOpen(true), "aria-label": "View Rules", children: _jsx(InfoCircleIcon, { className: "text-text-disabled size-5" }) })] }), _jsx("h2", { className: "text-xl font-semibold", children: quest.name }), _jsx("div", { className: "text-text-secondary-700 text-wrap text-sm font-normal", dangerouslySetInnerHTML: { __html: quest.description } })] }), _jsxs("div", { className: "w-full grow space-y-2", children: [_jsxs(Checkbox.Root, { checked: quest.accountVerificationCompleted, onCheckedChange: noop, children: [_jsx(Checkbox.Control, { className: "bg-transparent", children: _jsx(Checkbox.Indicator, { asChild: true, children: _jsx(CheckIcon, {}) }) }), _jsx(Checkbox.Label, { className: "text-sm font-medium leading-tight", children: "Complete personal verification" }), _jsx(Checkbox.HiddenInput, {})] }), _jsxs(Checkbox.Root, { checked: quest.firstDepositCompleted, onCheckedChange: noop, children: [_jsx(Checkbox.Control, { className: "bg-transparent", children: _jsx(Checkbox.Indicator, { asChild: true, children: _jsx(CheckIcon, {}) }) }), _jsx(Checkbox.Label, { className: "text-sm font-medium leading-tight", children: "Make your first deposit" }), _jsx(Checkbox.HiddenInput, {})] })] }), _jsxs("div", { className: "flex gap-lg", children: [_jsxs(Button, { size: "sm", onClick: () => {
29
+ const isKycCompleted = Boolean(verification?.status === 'VERIFIED') ||
30
+ Boolean(verification?.status === 'PENDING');
31
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "border-border-primary bg-bg-tertiary flex h-full w-full flex-col space-y-6 rounded-2xl border px-4 pb-6 pt-5", children: [_jsxs("div", { className: "flex flex-col space-y-2", children: [_jsxs("div", { className: "flex max-h-7.5 items-start justify-between", children: [_jsx(RemainingTime, {}), _jsx("button", { type: "button", className: "bg-bg-primary border-border-secondary flex size-9 cursor-pointer items-center justify-center rounded-md border", onClick: () => disclosure.setOpen(true), "aria-label": "View Rules", children: _jsx(InfoCircleIcon, { className: "text-text-disabled size-5" }) })] }), _jsx("h2", { className: "text-xl font-semibold", children: quest.name }), _jsx("div", { className: "text-text-secondary-700 text-wrap text-sm font-normal", dangerouslySetInnerHTML: { __html: quest.description } })] }), _jsxs("div", { className: "w-full grow space-y-2", children: [_jsxs(Checkbox.Root, { checked: quest.accountVerificationCompleted, onCheckedChange: noop, children: [_jsx(Checkbox.Control, { className: "bg-transparent", children: _jsx(Checkbox.Indicator, { asChild: true, children: _jsx(CheckIcon, {}) }) }), _jsx(Checkbox.Label, { className: "text-sm font-medium leading-tight", children: "Complete personal verification" }), _jsx(Checkbox.HiddenInput, {})] }), _jsxs(Checkbox.Root, { checked: quest.firstDepositCompleted, onCheckedChange: noop, children: [_jsx(Checkbox.Control, { className: "bg-transparent", children: _jsx(Checkbox.Indicator, { asChild: true, children: _jsx(CheckIcon, {}) }) }), _jsx(Checkbox.Label, { className: "text-sm font-medium leading-tight", children: "Make your first deposit" }), _jsx(Checkbox.HiddenInput, {})] })] }), _jsxs("div", { className: "flex gap-lg", children: [!isKycCompleted && (_jsxs(Button, { size: "sm", onClick: () => {
28
32
  globalStore.kyc.setOpen(true);
29
33
  }, disabled: quest.accountVerificationCompleted ||
30
34
  quest.status === 'COMPLETED' ||
31
35
  quest.status === 'FAILED' ||
32
- remainingTime.expired, className: "disabled:bg-bg-primary disabled:text-text-disabled", children: [quest.accountVerificationCompleted ? (_jsx(CheckIcon, { className: "size-5" })) : (_jsx(FileCheck02Icon, { className: "size-5" })), "Verify"] }), _jsxs(Button, { size: "sm", onClick: () => {
36
+ remainingTime.expired, className: "disabled:bg-bg-primary disabled:text-text-disabled", children: [quest.accountVerificationCompleted ? (_jsx(CheckIcon, { className: "size-5" })) : (_jsx(FileCheck02Icon, { className: "size-5" })), "Verify"] })), _jsxs(Button, { size: "sm", onClick: () => {
33
37
  globalStore.depositWithdrawal.setTab('DEPOSIT');
34
38
  globalStore.depositWithdrawal.setOpen(true);
35
39
  }, disabled: quest.firstDepositCompleted ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.0.181",
3
+ "version": "0.0.182",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",