@opexa/portal-components 0.1.16 → 0.1.18

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.
Files changed (26) hide show
  1. package/dist/client/hooks/signInLoaderProgress.d.ts +10 -0
  2. package/dist/client/hooks/signInLoaderProgress.js +40 -0
  3. package/dist/components/BetDepositLimit/BetDepositLimitModal.d.ts +4 -0
  4. package/dist/components/BetDepositLimit/BetDepositLimitModal.js +49 -0
  5. package/dist/components/BetDepositLimit/DepositLimitReached.d.ts +8 -0
  6. package/dist/components/BetDepositLimit/DepositLimitReached.js +12 -0
  7. package/dist/components/BetDepositLimit/betDepositLimitStore.d.ts +24 -0
  8. package/dist/components/BetDepositLimit/betDepositLimitStore.js +51 -0
  9. package/dist/components/BetDepositLimit/index.d.ts +2 -0
  10. package/dist/components/BetDepositLimit/index.js +2 -0
  11. package/dist/components/KYC/KYCDefault/PersonalInformation.js +12 -0
  12. package/dist/components/KYC/KYCReminder.lazy.js +4 -1
  13. package/dist/components/KYC/KycOpenOnHomeMount.js +11 -1
  14. package/dist/components/Migrate/Migrate.d.ts +10 -0
  15. package/dist/components/Migrate/Migrate.js +6 -0
  16. package/dist/components/Migrate/MigrateInitializing.d.ts +17 -0
  17. package/dist/components/Migrate/MigrateInitializing.js +14 -0
  18. package/dist/components/Migrate/MigrateLoadingDialog.d.ts +18 -0
  19. package/dist/components/Migrate/MigrateLoadingDialog.js +26 -0
  20. package/dist/components/Migrate/index.d.ts +4 -0
  21. package/dist/components/Migrate/index.js +4 -0
  22. package/dist/components/Migrate/migrateCard.d.ts +7 -0
  23. package/dist/components/Migrate/migrateCard.js +10 -0
  24. package/dist/components/SignIn/SignIn.lazy.js +8 -1
  25. package/dist/components/UpdateMobilePhoneNumber/UpdateMobilePhoneNumber.js +1 -5
  26. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ export declare const SIGN_IN_LOADER_PHASE_LABEL = "Signing you in";
2
+ export interface LoaderProgress {
3
+ barPct: number;
4
+ pctShown: number;
5
+ phaseLabel: string;
6
+ }
7
+ /** Drives loader bar while `active` is true. */
8
+ export declare function useLoaderProgress(active: boolean, phaseLabel: string): LoaderProgress;
9
+ /** Drives loader bar while a sign-in mutation is active. */
10
+ export declare function useSignInLoaderProgress(active: boolean): LoaderProgress;
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ import { useEffect, useState } from 'react';
3
+ export const SIGN_IN_LOADER_PHASE_LABEL = 'Signing you in';
4
+ const PROGRESS_INTERVAL_MS = 240;
5
+ const INITIAL_BAR_PCT = 6;
6
+ const BAR_CAP = 97;
7
+ function clamp(n, min, max) {
8
+ return Math.min(max, Math.max(min, n));
9
+ }
10
+ function advanceBar(p) {
11
+ const cap = BAR_CAP;
12
+ if (p >= cap)
13
+ return p;
14
+ const delta = (cap - p) * 0.055 + 0.45;
15
+ return clamp(p + delta, 0, cap);
16
+ }
17
+ /** Drives loader bar while `active` is true. */
18
+ export function useLoaderProgress(active, phaseLabel) {
19
+ const [barPct, setBarPct] = useState(INITIAL_BAR_PCT);
20
+ useEffect(() => {
21
+ if (!active) {
22
+ setBarPct(INITIAL_BAR_PCT);
23
+ return;
24
+ }
25
+ setBarPct(INITIAL_BAR_PCT);
26
+ const progressId = setInterval(() => {
27
+ setBarPct((p) => advanceBar(p));
28
+ }, PROGRESS_INTERVAL_MS);
29
+ return () => clearInterval(progressId);
30
+ }, [active]);
31
+ return {
32
+ barPct,
33
+ pctShown: Math.min(99, Math.round(barPct)),
34
+ phaseLabel,
35
+ };
36
+ }
37
+ /** Drives loader bar while a sign-in mutation is active. */
38
+ export function useSignInLoaderProgress(active) {
39
+ return useLoaderProgress(active, SIGN_IN_LOADER_PHASE_LABEL);
40
+ }
@@ -0,0 +1,4 @@
1
+ export interface BetDepositLimitModalProps {
2
+ onConfirm?: () => void;
3
+ }
4
+ export declare function BetDepositLimitModal({ onConfirm }: BetDepositLimitModalProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,49 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useShallow } from 'zustand/shallow';
4
+ import { useFeatureFlag } from '../../client/hooks/useFeatureFlag.js';
5
+ import { AlertTriangleIcon } from '../../icons/AlertTriangle.js';
6
+ import { XIcon } from '../../icons/XIcon.js';
7
+ import { Button } from '../../ui/Button/index.js';
8
+ import { Dialog } from '../../ui/Dialog/index.js';
9
+ import { Portal } from '../../ui/Portal/index.js';
10
+ import { useBetDepositLimitStore } from './betDepositLimitStore.js';
11
+ export function BetDepositLimitModal({ onConfirm }) {
12
+ const { enabled } = useFeatureFlag();
13
+ const { isOpen, type, period, currentLimit, requestedLimit, setIsOpen } = useBetDepositLimitStore(useShallow((state) => ({
14
+ isOpen: state.isOpen,
15
+ type: state.type,
16
+ period: state.period,
17
+ currentLimit: state.currentLimit,
18
+ requestedLimit: state.requestedLimit,
19
+ setIsOpen: state.setIsOpen,
20
+ })));
21
+ const isIncrease = requestedLimit > currentLimit;
22
+ const changeAmount = Math.abs(requestedLimit - currentLimit);
23
+ const formatPeso = (value) => {
24
+ return `₱${value.toLocaleString('en-PH')}`;
25
+ };
26
+ const capPeriod = period.charAt(0).toUpperCase() + period.slice(1);
27
+ const capType = type.charAt(0).toUpperCase() + type.slice(1);
28
+ const title = isIncrease
29
+ ? `${capPeriod} ${capType} Limit Increase Requested`
30
+ : `${capPeriod} ${capType} Limit Decrease Applied`;
31
+ const getDescription = () => {
32
+ if (isIncrease) {
33
+ if (type === 'bet') {
34
+ return `Your ${period} bet limit increase request has been submitted. Once approved, your new limit will take effect immediately.`;
35
+ }
36
+ return `Your request to increase your ${period} deposit limit has been submitted. The new limit will take effect once the request is approved.`;
37
+ }
38
+ return `Your ${period} ${type} limit has been lowered and is now active.`;
39
+ };
40
+ const handleConfirm = () => {
41
+ setIsOpen(false);
42
+ if (onConfirm) {
43
+ onConfirm();
44
+ }
45
+ };
46
+ return (_jsx(Dialog.Root, { open: isOpen && enabled, onOpenChange: (details) => {
47
+ setIsOpen(details.open);
48
+ }, closeOnInteractOutside: false, lazyMount: true, unmountOnExit: true, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, { className: "!z-[calc(var(--z-dialog)+3)]" }), _jsx(Dialog.Positioner, { className: "!z-[calc(var(--z-dialog)+4)] flex items-center justify-center", children: _jsxs(Dialog.Content, { className: "mx-auto max-h-[90vh] min-w-[21.438rem] max-w-[21.438rem] overflow-y-auto rounded-xl p-3xl lg:min-w-[25rem] lg:max-w-[25rem]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs("div", { className: "flex flex-col items-center", children: [_jsx("div", { className: "mx-auto flex size-12 items-center justify-center rounded-full bg-bg-brand-secondary", children: _jsx(AlertTriangleIcon, { className: "size-6 text-text-brand-700" }) }), _jsx("h2", { className: "mt-lg text-center font-semibold text-lg text-text-primary-900 xl:mt-xl", children: title }), _jsx("p", { className: "mt-xs text-center text-sm text-text-secondary-700 leading-relaxed", children: getDescription() }), _jsxs("div", { className: "mt-lg flex w-full select-none flex-col gap-y-2 rounded-xl border border-border-primary bg-bg-secondary px-5 py-4 text-sm", children: [_jsxs("div", { className: "flex justify-between py-0.5", children: [_jsx("span", { className: "text-text-tertiary-600", children: isIncrease ? 'Current limit:' : 'Previous limit:' }), _jsx("span", { className: "font-semibold text-text-primary-900", children: formatPeso(currentLimit) })] }), _jsxs("div", { className: "flex justify-between py-0.5", children: [_jsx("span", { className: "text-text-tertiary-600", children: isIncrease ? 'Requested limit:' : 'New limit:' }), _jsx("span", { className: "font-semibold text-text-primary-900", children: formatPeso(requestedLimit) })] }), _jsxs("div", { className: "flex justify-between py-0.5", children: [_jsx("span", { className: "text-text-tertiary-600", children: isIncrease ? 'Increase amount:' : 'Decrease amount:' }), _jsx("span", { className: "font-semibold text-text-primary-900", children: formatPeso(changeAmount) })] }), _jsxs("div", { className: "flex justify-between py-0.5", children: [_jsx("span", { className: "text-text-tertiary-600", children: isIncrease ? 'Status:' : 'Effective:' }), _jsx("span", { className: "font-semibold text-text-primary-900", children: isIncrease ? 'Pending Approval' : 'Immediately' })] })] }), _jsx("div", { className: "mt-3xl flex w-full flex-col gap-2 text-center lg:mt-4xl", children: _jsx(Button, { className: "w-full", onClick: handleConfirm, type: "button", children: "Confirm" }) })] })] }) })] }) }));
49
+ }
@@ -0,0 +1,8 @@
1
+ import type { LimitPeriod, LimitType } from './betDepositLimitStore';
2
+ export interface DepositLimitReachedProps {
3
+ type: LimitType;
4
+ period: LimitPeriod;
5
+ /** URL to the responsible gaming / limits page. Defaults to "/responsible-gaming" */
6
+ responsibleGamingUrl?: string;
7
+ }
8
+ export declare function DepositLimitReached({ type, period, responsibleGamingUrl, }: DepositLimitReachedProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AlertTriangleIcon } from '../../icons/AlertTriangle.js';
4
+ export function DepositLimitReached({ type, period, responsibleGamingUrl = '/responsible-gaming', }) {
5
+ const capPeriod = period.charAt(0).toUpperCase() + period.slice(1);
6
+ const capType = type.charAt(0).toUpperCase() + type.slice(1);
7
+ const title = `${capPeriod} ${capType} Limit Reached`;
8
+ const resetMessage = period === 'daily'
9
+ ? "You've reached your daily deposit limit, so you can't deposit right now. Your limit will reset tomorrow at midnight."
10
+ : "You've reached your monthly deposit limit, so you can't deposit right now. Your limit will reset on the 1st day of next month.";
11
+ return (_jsxs("div", { role: "alert", className: "flex items-start gap-md rounded-lg border border-utility-warning-200 bg-utility-warning-50 p-md", children: [_jsx("div", { className: "flex size-8 shrink-0 items-center justify-center rounded-full bg-bg-warning-secondary", children: _jsx(AlertTriangleIcon, { className: "size-4 text-text-featured-icon-light-warning" }) }), _jsxs("div", { className: "flex flex-col gap-xs", children: [_jsx("p", { className: "font-semibold text-sm text-text-primary-900", children: title }), _jsx("p", { className: "text-sm text-text-secondary-700 leading-relaxed", children: resetMessage }), _jsx("a", { href: `${responsibleGamingUrl}?from=limits`, className: "mt-xs w-fit text-sm font-medium text-text-warning-primary-600 hover:underline", children: "View Limits" })] })] }));
12
+ }
@@ -0,0 +1,24 @@
1
+ export type LimitType = 'bet' | 'deposit';
2
+ export type LimitPeriod = 'daily' | 'monthly';
3
+ export interface BetDepositLimitState {
4
+ isOpen: boolean;
5
+ type: LimitType;
6
+ period: LimitPeriod;
7
+ currentLimit: number;
8
+ requestedLimit: number;
9
+ setIsOpen: (isOpen: boolean) => void;
10
+ setType: (type: LimitType) => void;
11
+ setPeriod: (period: LimitPeriod) => void;
12
+ setCurrentLimit: (limit: number) => void;
13
+ setRequestedLimit: (limit: number) => void;
14
+ openModal: (type: LimitType, period: LimitPeriod, currentLimit: number, requestedLimit: number) => void;
15
+ }
16
+ export declare const useBetDepositLimitStore: import("zustand").UseBoundStore<import("zustand").StoreApi<BetDepositLimitState>>;
17
+ export declare const betDepositLimitStore: {
18
+ isOpen: boolean;
19
+ type: LimitType;
20
+ period: LimitPeriod;
21
+ currentLimit: number;
22
+ requestedLimit: number;
23
+ openModal(newType: LimitType, newPeriod: LimitPeriod, currentLimit: number, requestedLimit: number): void;
24
+ };
@@ -0,0 +1,51 @@
1
+ import { create } from 'zustand';
2
+ export const useBetDepositLimitStore = create((set) => ({
3
+ isOpen: false,
4
+ type: 'bet',
5
+ period: 'daily',
6
+ currentLimit: 0,
7
+ requestedLimit: 0,
8
+ setIsOpen: (isOpen) => set({ isOpen }),
9
+ setType: (type) => set({ type }),
10
+ setPeriod: (period) => set({ period }),
11
+ setCurrentLimit: (currentLimit) => set({ currentLimit }),
12
+ setRequestedLimit: (requestedLimit) => set({ requestedLimit }),
13
+ openModal: (type, period, currentLimit, requestedLimit) => set({ type, period, currentLimit, requestedLimit, isOpen: true }),
14
+ }));
15
+ export const betDepositLimitStore = {
16
+ get isOpen() {
17
+ return useBetDepositLimitStore.getState().isOpen;
18
+ },
19
+ set isOpen(value) {
20
+ useBetDepositLimitStore.setState({ isOpen: value });
21
+ },
22
+ get type() {
23
+ return useBetDepositLimitStore.getState().type;
24
+ },
25
+ set type(value) {
26
+ useBetDepositLimitStore.setState({ type: value });
27
+ },
28
+ get period() {
29
+ return useBetDepositLimitStore.getState().period;
30
+ },
31
+ set period(value) {
32
+ useBetDepositLimitStore.setState({ period: value });
33
+ },
34
+ get currentLimit() {
35
+ return useBetDepositLimitStore.getState().currentLimit;
36
+ },
37
+ set currentLimit(value) {
38
+ useBetDepositLimitStore.setState({ currentLimit: value });
39
+ },
40
+ get requestedLimit() {
41
+ return useBetDepositLimitStore.getState().requestedLimit;
42
+ },
43
+ set requestedLimit(value) {
44
+ useBetDepositLimitStore.setState({ requestedLimit: value });
45
+ },
46
+ openModal(newType, newPeriod, currentLimit, requestedLimit) {
47
+ useBetDepositLimitStore
48
+ .getState()
49
+ .openModal(newType, newPeriod, currentLimit, requestedLimit);
50
+ },
51
+ };
@@ -0,0 +1,2 @@
1
+ export * from './BetDepositLimitModal';
2
+ export * from './betDepositLimitStore';
@@ -0,0 +1,2 @@
1
+ export * from './BetDepositLimitModal.js';
2
+ export * from './betDepositLimitStore.js';
@@ -161,6 +161,7 @@ export function PersonalInformation() {
161
161
  verification: {
162
162
  status: 'PENDING',
163
163
  },
164
+ verificationStatus: 'PENDING',
164
165
  };
165
166
  });
166
167
  kyc.setDone(true);
@@ -193,6 +194,17 @@ export function PersonalInformation() {
193
194
  status: 'PENDING',
194
195
  };
195
196
  });
197
+ queryClient.setQueryData(getAccountQueryKey(), (prev) => {
198
+ if (!prev)
199
+ return prev;
200
+ return {
201
+ ...prev,
202
+ verification: {
203
+ status: 'PENDING',
204
+ },
205
+ verificationStatus: 'PENDING',
206
+ };
207
+ });
196
208
  kyc.setDone(true);
197
209
  globalStore.kyc.setOpen(false);
198
210
  globalStore.kycAccountVerificationRequired.setOpen(false);
@@ -55,8 +55,9 @@ export function KYCReminder({ enablePendingKycReminder = false, ...props }) {
55
55
  const { data: account, isLoading: accountLoading } = useAccountQuery();
56
56
  const isPending = account?.verification?.status === 'PENDING' ||
57
57
  account?.verificationStatus === 'PENDING';
58
- const isRejected = account?.verification?.status === 'REJECTED';
58
+ const isRejected = account?.verification?.status === 'REJECTED' && !isPending;
59
59
  const isNotVerified = !!account &&
60
+ !isPending &&
60
61
  (!account.verification ||
61
62
  account.verification === null ||
62
63
  account.verification.status === 'UNVERIFIED' ||
@@ -118,6 +119,7 @@ export function KYCReminder({ enablePendingKycReminder = false, ...props }) {
118
119
  globalStore.account__mobile.setOpen(false);
119
120
  router.push('/');
120
121
  }, children: "Log Out" })] })) })] })), isNotVerified &&
122
+ !isPending &&
121
123
  daysFromCreationToNow <= 3 &&
122
124
  daysFromCreationToNow > 1 &&
123
125
  !isVerificationLocked && (_jsxs(_Fragment, { children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs("div", { className: "p-3xl text-center", children: [_jsx("div", { className: "mx-auto mb-4 w-fit rounded-full bg-bg-primary p-2", children: _jsx(Image, { src: props.logo, alt: `${props.siteName} logo`, width: 200, height: 100, className: "mx-auto h-auto w-[120px]", draggable: false }) }), _jsx("h2", { className: `mt-4 font-semibold text-lg ${props.titleBgColor}`, children: "JUST A FRIENDLY REMINDER" }), _jsxs("p", { className: `mt-xs text-sm text-text-tertiary-600 ${props.descriptionBgColor}`, children: ["Please complete your KYC information within", ' ', _jsx("span", { className: "font-semibold text-[#FF0000]", children: "3 days" }), ' ', "to avoid temporary lock on your account."] }), _jsxs("div", { className: "mt-6 flex w-full items-center justify-center gap-3xl", children: [_jsx(Image, { src: props.pagcorLogo?.logo ?? pagcorLogo, alt: "PAGCOR logo", height: 88, width: 88, className: `h-[88px] w-auto shrink-0 ${props.pagcorLogo?.styles ?? ''}`, draggable: false, unoptimized: true }), _jsx(Image, { src: props.responsibleGamingLogo?.logo ??
@@ -145,6 +147,7 @@ export function KYCReminder({ enablePendingKycReminder = false, ...props }) {
145
147
  globalStore.account__mobile.setOpen(false);
146
148
  router.push('/');
147
149
  }, children: "Log Out" })] })) })] })] })), (isNotVerified || isRejected) &&
150
+ !isPending &&
148
151
  !isVerificationLocked &&
149
152
  !(daysFromCreationToNow <= 3 && daysFromCreationToNow > 1) && (_jsxs(_Fragment, { children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsxs("div", { className: "p-3xl text-center", children: [_jsx("div", { className: "mx-auto w-fit rounded-full bg-bg-primary p-2", children: _jsx(FileCheck02Icon, { className: "text-[#FEDF89]" }) }), _jsx("h2", { className: `mt-4 font-semibold text-lg ${props.titleBgColor}`, children: "Personal Verification" }), _jsxs("p", { className: `mt-xs text-sm text-text-tertiary-600 ${props.descriptionBgColor}`, children: ["All new users are required to complete identity verification to access the full range of ", props.siteName, ' ', "services, including withdrawals."] }), _jsx(Dialog.Context, { children: (api) => (_jsxs("div", { className: "mt-3xl space-y-lg", children: [_jsx(Button, { onClick: () => {
150
153
  api.setOpen(false);
@@ -8,6 +8,8 @@ export function KycOpenOnHomeMount({ isSkippable, bypassKycCheck, enablePendingK
8
8
  const setkycOpen = useGlobalStore((s) => s.kyc.setOpen);
9
9
  const setkycAccountVerificationRequired = useGlobalStore((s) => s.kycAccountVerificationRequired.setOpen);
10
10
  const isSignUpOpen = useGlobalStore((s) => s.signUp.open);
11
+ const isKycOpen = useGlobalStore((s) => s.kyc.open);
12
+ const isKycReminderOpen = useGlobalStore((s) => s.kycReminder.open);
11
13
  const { data: verification, isLoading: verificationLoading } = useMemberVerificationQuery();
12
14
  const { data: account, isLoading: accountLoading } = useAccountQuery();
13
15
  const isVerificationLocked = account?.status === 'VERIFICATION_LOCKED';
@@ -46,7 +48,13 @@ export function KycOpenOnHomeMount({ isSkippable, bypassKycCheck, enablePendingK
46
48
  if (isPending) {
47
49
  setkycOpen(false);
48
50
  setkycAccountVerificationRequired(false);
49
- setkycReminderOpen(isVerificationLocked || enablePendingKycReminder);
51
+ setkycReminderOpen(isVerificationLocked ||
52
+ enablePendingKycReminder ||
53
+ useGlobalStore.getState().kycReminder.open);
54
+ return;
55
+ }
56
+ // User opened the KYC flow explicitly — don't override with the reminder.
57
+ if (isKycOpen && !isKycReminderOpen) {
50
58
  return;
51
59
  }
52
60
  // Handle rejected verification status
@@ -94,6 +102,8 @@ export function KycOpenOnHomeMount({ isSkippable, bypassKycCheck, enablePendingK
94
102
  verificationLoading,
95
103
  accountLoading,
96
104
  isSignUpOpen,
105
+ isKycOpen,
106
+ isKycReminderOpen,
97
107
  isVerificationLocked,
98
108
  isPending,
99
109
  isRejected,
@@ -0,0 +1,10 @@
1
+ import type { ImageProps } from 'next/image';
2
+ export interface MigrateProps {
3
+ /** Controls loader dialog visibility and progress animation. */
4
+ open: boolean;
5
+ /** Shown inside the loading spinner; omit for default user icon. */
6
+ logo?: ImageProps['src'];
7
+ /** Extra classes merged onto the inner card. */
8
+ cardClassName?: string;
9
+ }
10
+ export declare function Migrate(props: MigrateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { MigrateLoadingDialog } from './MigrateLoadingDialog.js';
4
+ export function Migrate(props) {
5
+ return (_jsx(MigrateLoadingDialog, { open: props.open, active: props.open, logo: props.logo, variant: "migrate", cardClassName: props.cardClassName }));
6
+ }
@@ -0,0 +1,17 @@
1
+ import { type ImageProps } from 'next/image';
2
+ export declare const LOADER_HEADLINE = "Please wait while we finish setting things up for your account.";
3
+ export declare const LOADER_FOOTER = "You'll be redirected automatically once everything is ready.";
4
+ export declare const MIGRATE_LOADER_PHASE_LABEL = "Setting things up";
5
+ export declare const MIGRATE_LOADER_TITLE = "Account migration";
6
+ export declare const SIGN_IN_LOADER_TITLE = "Signing in";
7
+ export interface MigrateInitializingProps {
8
+ title: string;
9
+ phaseLabel: string;
10
+ barPct: number;
11
+ pctShown: number;
12
+ /** Brand mark in the spinner; omit to show the default user icon. */
13
+ logo?: ImageProps['src'];
14
+ footerText?: string;
15
+ className?: string;
16
+ }
17
+ export declare function MigrateInitializing(props: MigrateInitializingProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import Image, {} from 'next/image';
4
+ import { User01Icon } from '../../icons/User01Icon.js';
5
+ import { mergeMigrateCardClassName } from './migrateCard.js';
6
+ export const LOADER_HEADLINE = 'Please wait while we finish setting things up for your account.';
7
+ export const LOADER_FOOTER = "You'll be redirected automatically once everything is ready.";
8
+ export const MIGRATE_LOADER_PHASE_LABEL = 'Setting things up';
9
+ export const MIGRATE_LOADER_TITLE = 'Account migration';
10
+ export const SIGN_IN_LOADER_TITLE = 'Signing in';
11
+ export function MigrateInitializing(props) {
12
+ const footerText = props.footerText ?? LOADER_FOOTER;
13
+ return (_jsx("div", { className: mergeMigrateCardClassName(props.className), children: _jsxs("div", { className: "flex flex-col items-stretch gap-5 px-0.5 py-1 sm:px-1", children: [_jsx("div", { className: "flex justify-center", children: _jsxs("div", { className: "relative h-20 w-20 shrink-0", "aria-hidden": true, children: [_jsx("div", { className: "absolute inset-0 animate-spin rounded-full border-4 border-bg-tertiary border-t-button-tertiary-fg [animation-duration:1s]" }), _jsx("div", { className: "absolute inset-0 flex items-center justify-center text-button-tertiary-fg", children: props.logo ? (_jsx(Image, { src: props.logo, alt: "", width: 20, height: 20, className: "h-12 w-12 object-contain" })) : (_jsx(User01Icon, { className: "h-7 w-7", "aria-hidden": true })) })] }) }), _jsxs("div", { className: "flex w-full min-w-0 flex-col gap-1.5", children: [_jsx("h1", { className: "font-semibold text-text-primary-900 text-lg tracking-tight", children: props.title }), _jsx("p", { className: "min-w-0 max-w-full text-balance text-center text-sm text-text-secondary-700 leading-relaxed", children: LOADER_HEADLINE })] }), _jsxs("div", { className: "w-full min-w-0 space-y-1.5", children: [_jsxs("div", { className: "flex w-full min-w-0 items-center justify-between gap-3 text-text-tertiary-600 text-xs", children: [_jsx("span", { className: "min-w-0 truncate text-left", children: props.phaseLabel }), _jsxs("span", { className: "shrink-0 text-text-tertiary-600/90 tabular-nums", children: [props.pctShown, "%"] })] }), _jsx("div", { className: "h-2 w-full min-w-0 overflow-hidden rounded-full bg-bg-tertiary", children: _jsx("div", { className: "h-full rounded-full bg-button-primary-bg transition-[width] duration-1000 ease-[cubic-bezier(0.33,1,0.68,1)]", style: { width: `${props.barPct}%` } }) })] }), _jsx("p", { className: "min-w-0 max-w-full text-balance text-center text-text-tertiary-600/80 text-xs leading-relaxed", children: footerText })] }) }));
14
+ }
@@ -0,0 +1,18 @@
1
+ import type { ImageProps } from 'next/image';
2
+ export interface MigrateLoadingDialogProps {
3
+ /**
4
+ * When true, renders only `Dialog.Content` (for use inside an existing
5
+ * `Dialog.Root`, e.g. SignIn).
6
+ */
7
+ contentOnly?: boolean;
8
+ /** Controls dialog visibility when not `contentOnly`. */
9
+ open?: boolean;
10
+ /** Drives progress bar animation. */
11
+ active: boolean;
12
+ logo?: ImageProps['src'];
13
+ variant?: 'signIn' | 'migrate';
14
+ cardClassName?: string;
15
+ contentClassName?: string;
16
+ }
17
+ /** Loading dialog with spinner, static copy, and mutation-driven progress. */
18
+ export declare function MigrateLoadingDialog(props: MigrateLoadingDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { useLoaderProgress, useSignInLoaderProgress, } from '../../client/hooks/signInLoaderProgress.js';
5
+ import { Dialog } from '../../ui/Dialog/index.js';
6
+ import { Portal } from '../../ui/Portal/index.js';
7
+ import { MIGRATE_LOADER_PHASE_LABEL, MIGRATE_LOADER_TITLE, MigrateInitializing, SIGN_IN_LOADER_TITLE, } from './MigrateInitializing.js';
8
+ function useVariantLoaderProgress(active, variant) {
9
+ const signInProgress = useSignInLoaderProgress(variant === 'signIn' ? active : false);
10
+ const migrateProgress = useLoaderProgress(variant === 'migrate' ? active : false, MIGRATE_LOADER_PHASE_LABEL);
11
+ return variant === 'signIn' ? signInProgress : migrateProgress;
12
+ }
13
+ function LoaderCard(props) {
14
+ const variant = props.variant ?? 'signIn';
15
+ const progress = useVariantLoaderProgress(props.active, variant);
16
+ const title = variant === 'signIn' ? SIGN_IN_LOADER_TITLE : MIGRATE_LOADER_TITLE;
17
+ return (_jsx(MigrateInitializing, { title: title, phaseLabel: progress.phaseLabel, barPct: progress.barPct, pctShown: progress.pctShown, logo: props.logo, className: props.cardClassName }));
18
+ }
19
+ /** Loading dialog with spinner, static copy, and mutation-driven progress. */
20
+ export function MigrateLoadingDialog(props) {
21
+ const content = (_jsx(Dialog.Content, { className: twMerge('relative mx-auto bg-transparent shadow-none lg:rounded-xl', props.contentClassName), children: _jsx(LoaderCard, { ...props }) }));
22
+ if (props.contentOnly) {
23
+ return content;
24
+ }
25
+ return (_jsx(Dialog.Root, { open: props.open, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: content })] }) }));
26
+ }
@@ -0,0 +1,4 @@
1
+ export * from './Migrate';
2
+ export * from './MigrateInitializing';
3
+ export * from './MigrateLoadingDialog';
4
+ export * from './migrateCard';
@@ -0,0 +1,4 @@
1
+ export * from './Migrate.js';
2
+ export * from './MigrateInitializing.js';
3
+ export * from './MigrateLoadingDialog.js';
4
+ export * from './migrateCard.js';
@@ -0,0 +1,7 @@
1
+ /** Shared shell for MigrateInitializing / MigrateLoadingDialog. */
2
+ export declare const migrateCardClassName = "box-border w-full min-w-0 max-w-full self-stretch rounded-2xl border border-border-primary bg-bg-primary-alt p-5 text-center shadow-black/20 shadow-lg sm:p-6 md:w-[30dvw] md:max-w-[30dvw] md:shrink-0 md:self-center";
3
+ /**
4
+ * Merges the default migrate card shell with optional classes (e.g. `Migrate`’s
5
+ * `cardClassName` or a subcomponent’s `className`).
6
+ */
7
+ export declare function mergeMigrateCardClassName(className?: string): string;
@@ -0,0 +1,10 @@
1
+ import { twMerge } from 'tailwind-merge';
2
+ /** Shared shell for MigrateInitializing / MigrateLoadingDialog. */
3
+ export const migrateCardClassName = 'box-border w-full min-w-0 max-w-full self-stretch rounded-2xl border border-border-primary bg-bg-primary-alt p-5 text-center shadow-black/20 shadow-lg sm:p-6 md:w-[30dvw] md:max-w-[30dvw] md:shrink-0 md:self-center';
4
+ /**
5
+ * Merges the default migrate card shell with optional classes (e.g. `Migrate`’s
6
+ * `cardClassName` or a subcomponent’s `className`).
7
+ */
8
+ export function mergeMigrateCardClassName(className) {
9
+ return twMerge(migrateCardClassName, className);
10
+ }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useIsMutating } from '@tanstack/react-query';
3
4
  import Image, {} from 'next/image';
4
5
  import { twMerge } from 'tailwind-merge';
5
6
  import { useShallow } from 'zustand/shallow';
@@ -7,6 +8,8 @@ import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
7
8
  import { XIcon } from '../../icons/XIcon.js';
8
9
  import { Dialog } from '../../ui/Dialog/index.js';
9
10
  import { Portal } from '../../ui/Portal/index.js';
11
+ import { MigrateLoadingDialog } from '../Migrate/MigrateLoadingDialog.js';
12
+ import { getSignInMutationKey } from '../../utils/mutationKeys.js';
10
13
  import { SignInForm as CWSignInForm } from './CrazyWin/SignInForm.js';
11
14
  import { SignInForm as HBSignInForm } from './HappyBingo/SignInForm.js';
12
15
  import { SignInPropsProvider, SignInProvider } from './SignInContext.js';
@@ -19,11 +22,15 @@ export function SignIn(props) {
19
22
  termsAndConditions: ctx.termsAndConditions,
20
23
  responsibleGaming: ctx.responsibleGaming,
21
24
  })));
25
+ const mutating = useIsMutating({ mutationKey: getSignInMutationKey() }) > 0;
26
+ const showLoader = mutating &&
27
+ props.versionSession === 'Inplay' &&
28
+ signIn.type === 'NAME_AND_PASSWORD';
22
29
  return (_jsx(SignInPropsProvider, { value: props, children: _jsx(SignInProvider, { value: signIn, children: _jsx(Dialog.Root, { open: signInStore.open, onOpenChange: (details) => {
23
30
  signInStore.setOpen(details.open);
24
31
  }, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, onExitComplete: () => {
25
32
  // Reset checkbox state when dialog closes
26
33
  globalStore.termsAndConditions.setAccepted(false);
27
34
  globalStore.responsibleGaming.setAccepted(false);
28
- }, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: props.variant === 'crazywin' ? (_jsxs(Dialog.Content, { className: twMerge(`relative w-[20.6875rem] bg-transparent`, props.className?.root), children: [_jsxs("div", { children: [_jsx(Dialog.CloseTrigger, { className: "md:-right-5 absolute top-0 right-2 z-10 size-5.5 rounded-md bg-gradient-to-t from-[#FFE5AF] to-[#EAC467] p-0.5", children: _jsx(XIcon, { className: "h-4.5 w-4.5 text-[#6C5200]" }) }), _jsx(Image, { src: "https://assets.opexacms.atalos.io/hdQJS7VHEaDvb3ZtV", alt: "", width: 200, height: 100, className: "mx-auto h-auto w-[8rem] object-contain md:mt-10 md:w-[12rem]", draggable: false })] }), _jsx("div", { className: "rounded-[1.25em] border-white/10 bg-[radial-gradient(103.87%_100%_at_50.15%_0%,_#072b37_20.3%,_#051125_100%)] p-[1.25rem] shadow-[0_4px_14px_6px_rgba(0,0,0,0.6)] backdrop-blur-[25px]", children: _jsx(CWSignInForm, {}) }), _jsxs("div", { className: "mt-5 flex items-center justify-center gap-1 text-xs md:mt-10", children: [_jsx("span", { children: "Don't have an account?" }), _jsx("button", { type: "button", className: "font-semibold text-[#FFE595]", children: "Register Here" })] })] })) : props.variant === 'happybingo' ? (_jsxs(Dialog.Content, { className: twMerge('h-full w-full lg:mx-auto lg:grid lg:h-[50rem] lg:w-[55rem] lg:grid-cols-2', props.className?.root), style: { msOverflowStyle: 'none', scrollbarWidth: 'none' }, children: [_jsx("div", { className: "hidden h-full overflow-hidden rounded-l-xl lg:block", children: _jsx(Image, { src: "https://assets.opexacms.atalos.io/hd48QiKmacXs1XWnY", alt: "login banner", width: 1000, height: 1000, className: "h-full w-full", draggable: false }) }), _jsxs("div", { className: "relative flex min-h-full flex-col bg-white p-5 lg:rounded-r-xl lg:p-3xl dark:bg-[#0C111D]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: "https://assets.opexacms.atalos.io/hdKuiVnx6WePuJy6C", alt: "happy bingo logo", width: 433, height: 211, className: "mx-auto mb-xl h-auto w-[7.125rem]", draggable: false }), _jsx(HBSignInForm, {})] })] })) : (_jsxs(Dialog.Content, { className: twMerge('mx-auto h-full w-full items-start overflow-y-auto bg-bg-primary-alt p-3xl pb-4xl lg:h-auto lg:max-h-[80vh] lg:w-[400px] lg:rounded-xl', props.className?.root), style: { msOverflowStyle: 'none', scrollbarWidth: 'none' }, children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: props.logo, alt: "", width: 200, height: 100, className: "mx-auto h-7.5 w-auto", draggable: false }), _jsx("div", { children: _jsx(SignInForm, {}) })] })) })] }) }) }) }));
35
+ }, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsxs(Dialog.Positioner, { children: [showLoader && (_jsx(MigrateLoadingDialog, { contentOnly: true, active: mutating, logo: props.logo, variant: "signIn" })), _jsx(Dialog.Content, { className: twMerge(showLoader ? 'hidden' : ''), children: props.variant === 'crazywin' ? (_jsxs("div", { className: twMerge(`relative w-[20.6875rem] bg-transparent`, props.className?.root), children: [_jsxs("div", { children: [_jsx(Dialog.CloseTrigger, { className: "md:-right-5 absolute top-0 right-2 z-10 size-5.5 rounded-md bg-gradient-to-t from-[#FFE5AF] to-[#EAC467] p-0.5", children: _jsx(XIcon, { className: "h-4.5 w-4.5 text-[#6C5200]" }) }), _jsx(Image, { src: "https://assets.opexacms.atalos.io/hdQJS7VHEaDvb3ZtV", alt: "", width: 200, height: 100, className: "mx-auto h-auto w-[8rem] object-contain md:mt-10 md:w-[12rem]", draggable: false })] }), _jsx("div", { className: "rounded-[1.25em] border-white/10 bg-[radial-gradient(103.87%_100%_at_50.15%_0%,_#072b37_20.3%,_#051125_100%)] p-[1.25rem] shadow-[0_4px_14px_6px_rgba(0,0,0,0.6)] backdrop-blur-[25px]", children: _jsx(CWSignInForm, {}) }), _jsxs("div", { className: "mt-5 flex items-center justify-center gap-1 text-xs md:mt-10", children: [_jsx("span", { children: "Don't have an account?" }), _jsx("button", { type: "button", className: "font-semibold text-[#FFE595]", children: "Register Here" })] })] })) : props.variant === 'happybingo' ? (_jsxs("div", { className: twMerge('relative h-full w-full lg:mx-auto lg:grid lg:h-[50rem] lg:w-[55rem] lg:grid-cols-2', props.className?.root), style: { msOverflowStyle: 'none', scrollbarWidth: 'none' }, children: [_jsx("div", { className: "hidden h-full overflow-hidden rounded-l-xl lg:block", children: _jsx(Image, { src: "https://assets.opexacms.atalos.io/hd48QiKmacXs1XWnY", alt: "login banner", width: 1000, height: 1000, className: "h-full w-full", draggable: false }) }), _jsxs("div", { className: "relative flex min-h-full flex-col bg-white p-5 lg:rounded-r-xl lg:p-3xl dark:bg-[#0C111D]", children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: "https://assets.opexacms.atalos.io/hdKuiVnx6WePuJy6C", alt: "happy bingo logo", width: 433, height: 211, className: "mx-auto mb-xl h-auto w-[7.125rem]", draggable: false }), _jsx(HBSignInForm, {})] })] })) : (_jsxs("div", { className: twMerge('relative mx-auto h-full w-full items-start overflow-y-auto bg-bg-primary-alt p-3xl pb-4xl lg:h-auto lg:max-h-[80vh] lg:w-[400px] lg:rounded-xl', props.className?.root), style: { msOverflowStyle: 'none', scrollbarWidth: 'none' }, children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: props.logo, alt: "", width: 200, height: 100, className: "mx-auto h-7.5 w-auto", draggable: false }), _jsx("div", { children: _jsx(SignInForm, {}) })] })) })] })] }) }) }) }));
29
36
  }
@@ -2,7 +2,6 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import Image from 'next/image';
4
4
  import { useShallow } from 'zustand/shallow';
5
- import { useFeatureFlag } from '../../client/hooks/useFeatureFlag.js';
6
5
  import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
7
6
  import inplayLogo from '../../images/inplay-logo.png';
8
7
  import lightBg from '../../images/light-bg.png';
@@ -14,15 +13,12 @@ import { useAutoOpen, } from './hooks/useAutoOpen.js';
14
13
  import { useUpdateMobileFlow } from './hooks/useUpdateMobileFlow.js';
15
14
  export function UpdateMobilePhoneNumber({ shouldOnlyShow = 'MobileVerified', } = {}) {
16
15
  useAutoOpen(shouldOnlyShow);
17
- const featureFlag = useFeatureFlag();
18
16
  const globalStore = useGlobalStore(useShallow((ctx) => ({
19
17
  updateMobilePhoneNumber: ctx.updateMobilePhoneNumber,
20
18
  kyc: ctx.kyc,
21
19
  })));
22
20
  const { step, step1Form, step2Form, cooldown, formRef, mobileNumberParser, submitStep1, submitStep2, resend, goBackToStep1, } = useUpdateMobileFlow();
23
- const isOpen = globalStore.updateMobilePhoneNumber.open &&
24
- featureFlag.enabled &&
25
- !globalStore.kyc.open;
21
+ const isOpen = globalStore.updateMobilePhoneNumber.open && !globalStore.kyc.open;
26
22
  return (_jsx(Dialog.Root, { open: isOpen, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { className: "flex items-center", children: _jsxs(Dialog.Content, { className: "flex w-[375px] flex-col items-center space-y-4 rounded-xl bg-[#111827] p-6 text-center", style: {
27
23
  backgroundImage: `url(${lightBg.src})`,
28
24
  }, children: [_jsx(Image, { src: inplayLogo, alt: "inplay logo", width: 82, height: 34, className: "h-auto w-[82px]" }), _jsxs("div", { children: [step === 1 && (_jsx(Step1MobileNumberForm, { step1Form: step1Form, submitStep1: submitStep1 })), step === 2 && (_jsx(Step2VerificationForm, { step1Form: step1Form, step2Form: step2Form, cooldown: cooldown, formRef: formRef, mobileNumberParser: mobileNumberParser, submitStep2: submitStep2, resend: resend, goBackToStep1: goBackToStep1 }))] })] }) })] }) }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",