@opexa/portal-components 0.0.903 → 0.0.905

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 (65) hide show
  1. package/dist/client/hooks/useSignInSSOMutation.d.ts +3 -6
  2. package/dist/client/hooks/useSignInSSOMutation.js +7 -13
  3. package/dist/client/hooks/useVerifyMobileNumber.d.ts +3 -0
  4. package/dist/client/hooks/useVerifyMobileNumber.js +20 -0
  5. package/dist/client/services/signIn.d.ts +0 -1
  6. package/dist/client/services/signIn.js +0 -19
  7. package/dist/components/AccountInfo/AccountInfo.js +13 -2
  8. package/dist/components/AccountSecurity/AccountSecurity.js +16 -2
  9. package/dist/components/GamesSearch/GamesSearch.d.ts +2 -0
  10. package/dist/components/GamesSearch/GamesSearch.js +6 -3
  11. package/dist/components/Search/Search.lazy.d.ts +2 -0
  12. package/dist/components/Search/Search.lazy.js +2 -2
  13. package/dist/components/SingleSignOn/SingleSignOn.d.ts +2 -0
  14. package/dist/components/SingleSignOn/SingleSignOn.js +173 -0
  15. package/dist/components/SingleSignOn/index.d.ts +1 -0
  16. package/dist/components/SingleSignOn/index.js +1 -0
  17. package/dist/constants/GameProvider.js +12 -0
  18. package/dist/schemas/forgotPasswordSchema.d.ts +4 -4
  19. package/dist/services/account.d.ts +3 -0
  20. package/dist/services/auth.d.ts +5 -0
  21. package/dist/services/auth.js +26 -0
  22. package/dist/types/index.d.ts +1 -1
  23. package/dist/ui/AlertDialog/AlertDialog.d.ts +88 -88
  24. package/dist/ui/AlertDialog/alertDialog.recipe.d.ts +8 -8
  25. package/dist/ui/Badge/Badge.d.ts +12 -12
  26. package/dist/ui/Badge/badge.anatomy.d.ts +1 -1
  27. package/dist/ui/Badge/badge.recipe.d.ts +3 -3
  28. package/dist/ui/Carousel/Carousel.d.ts +72 -72
  29. package/dist/ui/Carousel/carousel.recipe.d.ts +8 -8
  30. package/dist/ui/Checkbox/Checkbox.d.ts +23 -23
  31. package/dist/ui/Checkbox/checkbox.recipe.d.ts +3 -3
  32. package/dist/ui/Clipboard/Clipboard.d.ts +18 -18
  33. package/dist/ui/Clipboard/clipboard.recipe.d.ts +3 -3
  34. package/dist/ui/Collapsible/Collapsible.d.ts +20 -20
  35. package/dist/ui/Collapsible/collapsible.recipe.d.ts +5 -5
  36. package/dist/ui/Combobox/Combobox.d.ts +42 -42
  37. package/dist/ui/Combobox/combobox.recipe.d.ts +3 -3
  38. package/dist/ui/DatePicker/DatePicker.d.ts +72 -72
  39. package/dist/ui/DatePicker/datePicker.recipe.d.ts +3 -3
  40. package/dist/ui/Dialog/Dialog.d.ts +33 -33
  41. package/dist/ui/Dialog/dialog.recipe.d.ts +3 -3
  42. package/dist/ui/Drawer/Drawer.d.ts +33 -33
  43. package/dist/ui/Drawer/drawer.recipe.d.ts +3 -3
  44. package/dist/ui/Field/Field.d.ts +21 -21
  45. package/dist/ui/Field/field.recipe.d.ts +3 -3
  46. package/dist/ui/Menu/Menu.d.ts +360 -360
  47. package/dist/ui/Menu/menu.recipe.d.ts +20 -20
  48. package/dist/ui/Popover/Popover.d.ts +88 -88
  49. package/dist/ui/Popover/popover.recipe.d.ts +8 -8
  50. package/dist/ui/Progress/Progress.d.ts +27 -27
  51. package/dist/ui/Progress/progress.recipe.d.ts +3 -3
  52. package/dist/ui/SegmentGroup/SegmentGroup.d.ts +18 -18
  53. package/dist/ui/SegmentGroup/segmentGroup.recipe.d.ts +3 -3
  54. package/dist/ui/Select/Select.d.ts +45 -45
  55. package/dist/ui/Select/select.recipe.d.ts +3 -3
  56. package/dist/ui/Table/Table.d.ts +21 -21
  57. package/dist/ui/Table/table.anatomy.d.ts +1 -1
  58. package/dist/ui/Table/table.recipe.d.ts +3 -3
  59. package/dist/ui/Tabs/Tabs.d.ts +15 -15
  60. package/dist/ui/Tabs/tabs.recipe.d.ts +3 -3
  61. package/dist/ui/Tooltip/Tooltip.d.ts +30 -30
  62. package/dist/ui/Tooltip/tooltip.recipe.d.ts +5 -5
  63. package/dist/utils/mutationKeys.d.ts +1 -0
  64. package/dist/utils/mutationKeys.js +4 -0
  65. package/package.json +1 -1
@@ -1,6 +1,3 @@
1
- import type { Authenticator, Mutation } from '../../types';
2
- export interface SignInSSOInput {
3
- partnerId: string;
4
- token: string;
5
- }
6
- export declare const useSignInSSOMutation: Mutation<Authenticator | null, SignInSSOInput>;
1
+ import { type CreateSessionMutation_next, type SignInSSOInput } from '../../services/auth';
2
+ import type { Mutation } from '../../types';
3
+ export declare const useSignInSSOMutation: Mutation<CreateSessionMutation_next, SignInSSOInput>;
@@ -1,27 +1,21 @@
1
1
  import { useMutation } from '@tanstack/react-query';
2
+ import { signInSSO, } from '../../services/auth.js';
2
3
  import { getQueryClient } from '../../utils/getQueryClient.js';
3
4
  import { getSignInSSOMutationKey } from '../../utils/mutationKeys.js';
4
5
  import { getSessionQueryKey } from '../../utils/queryKeys.js';
5
- import { signInSSO } from '../services/signIn.js';
6
6
  export const useSignInSSOMutation = (config) => {
7
7
  const queryClient = getQueryClient();
8
8
  return useMutation({
9
9
  ...config,
10
10
  mutationKey: getSignInSSOMutationKey(),
11
11
  mutationFn: async (input) => {
12
- try {
13
- const { partnerId, token } = input;
14
- const authenticator = await signInSSO(partnerId, token);
15
- if (!authenticator) {
16
- await queryClient.invalidateQueries({
17
- queryKey: getSessionQueryKey(),
18
- });
19
- }
20
- return authenticator;
21
- }
22
- catch (error) {
23
- throw error;
12
+ const authenticator = await signInSSO(input);
13
+ if (!authenticator) {
14
+ await queryClient.invalidateQueries({
15
+ queryKey: getSessionQueryKey(),
16
+ });
24
17
  }
18
+ return authenticator;
25
19
  },
26
20
  });
27
21
  };
@@ -0,0 +1,3 @@
1
+ import { type VerifyMobileNumberInput } from '../../services/account';
2
+ import type { Mutation } from '../../types';
3
+ export declare const useVerifyMobileNumber: Mutation<void, VerifyMobileNumberInput>;
@@ -0,0 +1,20 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import invariant from 'tiny-invariant';
3
+ import { verifyMobileNumber, } from '../../services/account.js';
4
+ import { getVerifyMobileNumberMutationKey } from '../../utils/mutationKeys.js';
5
+ import { getSession } from '../services/getSession.js';
6
+ export const useVerifyMobileNumber = (config) => {
7
+ return useMutation({
8
+ ...config,
9
+ mutationKey: getVerifyMobileNumberMutationKey(),
10
+ mutationFn: async (input) => {
11
+ const session = await getSession();
12
+ invariant(session.status === 'authenticated');
13
+ await verifyMobileNumber(input.verificationCode, {
14
+ headers: {
15
+ Authorization: `Bearer ${session.token}`,
16
+ },
17
+ });
18
+ },
19
+ });
20
+ };
@@ -3,4 +3,3 @@ import { type HttpRequestOptions } from '../../services/httpRequest';
3
3
  import type { Authenticator } from '../../types';
4
4
  export type SignInInput = CreateSessionInput;
5
5
  export declare function signIn(input: SignInInput, options?: HttpRequestOptions): Promise<Authenticator | null>;
6
- export declare function signInSSO(partnerId: string, token: string, options?: HttpRequestOptions): Promise<Authenticator | null>;
@@ -21,22 +21,3 @@ export async function signIn(input, options) {
21
21
  }
22
22
  return res.data ?? null;
23
23
  }
24
- export async function signInSSO(partnerId, token, options) {
25
- // Create base64 encoded authorization
26
- const authString = btoa(`${partnerId}:${token}`);
27
- const res = await httpRequest.json('/api/v3/sessions', {
28
- ...options,
29
- method: 'POST',
30
- headers: {
31
- Authorization: `OPEXA_SINGLE_SIGN_ON ${authString}`,
32
- 'Platform-Code': process.env.NEXT_PUBLIC_PLATFORM_CODE || '',
33
- },
34
- });
35
- if (!res.ok) {
36
- const error = new Error();
37
- error.name = 'SSOSignInError';
38
- error.message = 'Single sign-on authentication failed';
39
- throw error;
40
- }
41
- return res.data ?? null;
42
- }
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ark } from '@ark-ui/react/factory';
3
4
  import FacebookLogin from '@greatsumini/react-facebook-login';
4
5
  import { zodResolver } from '@hookform/resolvers/zod';
5
6
  import { GoogleOAuthProvider, useGoogleLogin } from '@react-oauth/google';
@@ -15,12 +16,15 @@ import { useFacebookClientQuery } from '../../client/hooks/useFacebookClientQuer
15
16
  import { useGoogleClientIdQuery } from '../../client/hooks/useGoogleClientIdQuery.js';
16
17
  import { useLocaleInfo } from '../../client/hooks/useLocaleInfo.js';
17
18
  import { useMobileNumberParser } from '../../client/hooks/useMobileNumberParser.js';
19
+ import { useProfileCompletionQuery } from '../../client/hooks/useProfileCompletionQuery.js';
18
20
  import { useSessionQuery } from '../../client/hooks/useSessionQuery.js';
19
21
  import { useUnlinkFacebookMutation } from '../../client/hooks/useUnlinkFacebookMutation.js';
20
22
  import { useUnlinkGoogleMutation } from '../../client/hooks/useUnlinkGoogleMutation.js';
21
23
  import { useUpdateAccountMutation } from '../../client/hooks/useUpdateAccountMutation.js';
22
24
  import { toaster } from '../../client/utils/toaster.js';
23
25
  import { AUTH_ENDPOINT, PLATFORM_CODE } from '../../constants/index.js';
26
+ import { AlertCircleIcon } from '../../icons/AlertCircleIcon.js';
27
+ import { CheckVerified02Icon } from '../../icons/CheckVerified02Icon.js';
24
28
  import { FacebookIcon } from '../../icons/FacebookIcon.js';
25
29
  import { GoogleIcon } from '../../icons/GoogleIcon.js';
26
30
  import { LinkBroken02Icon } from '../../icons/LinkBroken02Icon.js';
@@ -32,7 +36,7 @@ import { Field } from '../../ui/Field/index.js';
32
36
  import { IconButton } from '../../ui/IconButton/index.js';
33
37
  import { Portal } from '../../ui/Portal/index.js';
34
38
  import { getQueryClient } from '../../utils/getQueryClient.js';
35
- import { getAccountQueryKey } from '../../utils/queryKeys.js';
39
+ import { getAccountQueryKey, getProfileCompletionQueryKey, } from '../../utils/queryKeys.js';
36
40
  const accountSchema = z.object({
37
41
  name: z
38
42
  .string()
@@ -154,6 +158,8 @@ function PersonalInfo(props) {
154
158
  function ContactInfo() {
155
159
  const localeInfo = useLocaleInfo();
156
160
  const accountQuery = useAccountQuery();
161
+ const profileCompletionQuery = useProfileCompletionQuery();
162
+ const profileCompletion = profileCompletionQuery.data;
157
163
  const account = accountQuery.data;
158
164
  const { parse, format, equals } = useMobileNumberParser();
159
165
  const updateAccountMutation = useUpdateAccountMutation({
@@ -161,6 +167,9 @@ function ContactInfo() {
161
167
  toaster.success({
162
168
  description: 'Changes saved!',
163
169
  });
170
+ getQueryClient().invalidateQueries({
171
+ queryKey: getProfileCompletionQueryKey(),
172
+ });
164
173
  },
165
174
  onError(error) {
166
175
  toaster.error({
@@ -194,7 +203,9 @@ function ContactInfo() {
194
203
  });
195
204
  }
196
205
  }, [account, form, parse]);
197
- return (_jsxs("div", { children: [_jsx("h2", { className: "font-semibold text-sm text-text-secondary-700", children: "Contact Info" }), _jsxs("form", { className: "mt-5 rounded-xl border border-border-secondary bg-bg-primary-alt shadow-xs", onSubmit: form.handleSubmit((data) => {
206
+ return (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h2", { className: "grow font-semibold text-sm text-text-secondary-700", children: "Contact Info" }), _jsx(ark.svg, { asChild: true, className: twMerge('size-6', profileCompletion?.personalInformation
207
+ ? 'text-text-success-primary'
208
+ : 'text-text-warning-primary'), children: profileCompletion?.personalInformation ? (_jsx(CheckVerified02Icon, {})) : (_jsx(AlertCircleIcon, {})) })] }), _jsxs("form", { className: "mt-5 rounded-xl border border-border-secondary bg-bg-primary-alt shadow-xs", onSubmit: form.handleSubmit((data) => {
198
209
  updateAccountMutation.mutate({
199
210
  emailAddress: data.emailAddress && account?.emailAddress !== data.emailAddress
200
211
  ? data.emailAddress
@@ -21,6 +21,8 @@ import { Field } from '../../ui/Field/index.js';
21
21
  import { PasswordInput } from '../../ui/PasswordInput/index.js';
22
22
  import { Portal } from '../../ui/Portal/index.js';
23
23
  import { Select } from '../../ui/Select/index.js';
24
+ import { getQueryClient } from '../../utils/getQueryClient.js';
25
+ import { getProfileCompletionQueryKey } from '../../utils/queryKeys.js';
24
26
  export function AccountSecurity(props) {
25
27
  return (_jsxs("div", { className: twMerge('space-y-3xl', props.className), children: [_jsx(LoginPassword, {}), _jsx(TransactionPassword, {})] }));
26
28
  }
@@ -33,6 +35,9 @@ function LoginPassword() {
33
35
  toaster.success({
34
36
  description: 'Changes saved!',
35
37
  });
38
+ getQueryClient().invalidateQueries({
39
+ queryKey: getProfileCompletionQueryKey(),
40
+ });
36
41
  },
37
42
  onError(error) {
38
43
  toaster.error({
@@ -42,7 +47,10 @@ function LoginPassword() {
42
47
  });
43
48
  const schema = z
44
49
  .object({
45
- password: z.string().min(8, 'Password must be at least 8 characters').max(150, 'Password must be at most 150 characters'),
50
+ password: z
51
+ .string()
52
+ .min(8, 'Password must be at least 8 characters')
53
+ .max(150, 'Password must be at most 150 characters'),
46
54
  confirmPassword: z.string().min(1, 'Please confirm your password'),
47
55
  secretQuestion: z.enum(SECRET_QUESTIONS).nullable().optional(),
48
56
  secretAnswer: z
@@ -115,6 +123,9 @@ function TransactionPassword() {
115
123
  toaster.success({
116
124
  description: 'Changes saved!',
117
125
  });
126
+ getQueryClient().invalidateQueries({
127
+ queryKey: getProfileCompletionQueryKey(),
128
+ });
118
129
  },
119
130
  onError(error) {
120
131
  toaster.error({
@@ -124,7 +135,10 @@ function TransactionPassword() {
124
135
  });
125
136
  const schema = z
126
137
  .object({
127
- password: z.string().min(8, 'Password must be at least 8 characters').max(150, 'Password must be at most 150 characters'),
138
+ password: z
139
+ .string()
140
+ .min(8, 'Password must be at least 8 characters')
141
+ .max(150, 'Password must be at most 150 characters'),
128
142
  confirmPassword: z.string().min(1, 'Please confirm your password'),
129
143
  })
130
144
  .superRefine((data, ctx) => {
@@ -5,6 +5,7 @@ export interface ClassNameEntries {
5
5
  thumbnailRoot?: string;
6
6
  thumbnailTitle?: string;
7
7
  loadMoreButton?: string;
8
+ gameSearchResult?: string;
8
9
  }
9
10
  interface GamesSearchProps {
10
11
  first?: number;
@@ -13,6 +14,7 @@ interface GamesSearchProps {
13
14
  className?: string | ClassNameEntries;
14
15
  placeholder?: string;
15
16
  bypassDomains?: BypassDomainConfig[];
17
+ variant?: string;
16
18
  }
17
19
  export declare function GamesSearch(props: GamesSearchProps): import("react/jsx-runtime").JSX.Element;
18
20
  export {};
@@ -20,7 +20,9 @@ export function GamesSearch(props) {
20
20
  const [searchInput, setSearchInput] = useState('');
21
21
  const sanitizedSearch = sanitizeGamesSearch(searchInput);
22
22
  const trimmedInputLength = searchInput.trim().length;
23
- const showMinLengthWarning = trimmedInputLength > 0 && trimmedInputLength < 3 && sanitizedSearch.length < 3;
23
+ const showMinLengthWarning = trimmedInputLength > 0 &&
24
+ trimmedInputLength < 3 &&
25
+ sanitizedSearch.length < 3;
24
26
  const gamesQuery = useGamesQuery({
25
27
  first: props.first ?? 18,
26
28
  filter: props.filter,
@@ -50,13 +52,14 @@ export function GamesSearch(props) {
50
52
  api.setInputValue('');
51
53
  api.focus();
52
54
  }, children: "Clear" }));
53
- } })] }), _jsx(Portal, { children: _jsx(Combobox.Context, { children: (api) => (_jsxs(_Fragment, { children: [_jsx(Presence, { present: api.open, children: _jsx("div", { className: "fixed inset-0 z-40 bg-black/50 backdrop-blur-sm", onClick: () => api.setOpen(false) }) }), _jsx(Combobox.Positioner, { children: searchInput.trim().length > 0 && (_jsx(Combobox.Content, { className: "z-50 max-h-[33.25rem] overflow-y-auto p-0", children: showMinLengthWarning ? (_jsx(Alert, { message: "Search requires at least 3 characters." })) : (_jsxs(_Fragment, { children: [games.length <= 0 && (_jsx(Alert, { message: "No results found" })), games.length > 0 && (_jsxs("div", { className: "p-xl", children: [_jsx(Combobox.Context, { children: (api) => (_jsx("div", { className: "grid grid-cols-3 gap-1.5 lg:grid-cols-9 lg:gap-3.5", children: games.map((game) => (_jsxs(GameLaunchTrigger, { bypassKycCheck: isBypass, game: game, onClick: () => {
55
+ } })] }), _jsx(Portal, { children: _jsx(Combobox.Context, { children: (api) => (_jsxs(_Fragment, { children: [_jsx(Presence, { present: api.open, children: _jsx("div", { className: "fixed inset-0 z-40 bg-black/50 backdrop-blur-sm", onClick: () => api.setOpen(false) }) }), _jsx(Combobox.Positioner, { children: searchInput.trim().length > 0 && (_jsx(Combobox.Content, { className: "z-50 max-h-[33.25rem] overflow-y-auto p-0", children: showMinLengthWarning ? (_jsx(Alert, { message: "Search requires at least 3 characters." })) : (_jsxs(_Fragment, { children: [games.length <= 0 && (_jsx(Alert, { message: "No results found" })), games.length > 0 && (_jsxs("div", { className: "p-xl", children: [_jsx(Combobox.Context, { children: (api) => (_jsx("div", { className: twMerge('grid grid-cols-3 gap-1.5 lg:grid-cols-9 lg:gap-3.5', classNames.gameSearchResult), children: games.map((game) => (_jsxs(GameLaunchTrigger, { bypassKycCheck: isBypass, game: game, onClick: () => {
54
56
  api.setOpen(false);
55
57
  }, className: twMerge('block w-full shadow-sm', classNames.thumbnailRoot), children: [_jsx(Image, { src: getGameImageUrl({
56
58
  reference: game.reference,
57
59
  provider: game.provider,
58
60
  image: game.image,
59
- }), alt: "", width: 200, height: 200, loading: "lazy", unoptimized: true, className: "aspect-square w-full rounded-t-md object-cover" }), _jsx("span", { className: twMerge('block w-full truncate rounded-b-md bg-bg-tertiary px-2 py-2.5 text-center font-semibold text-text-primary-brand text-xs', classNames.thumbnailTitle), children: game.name })] }, game.id))) })) }), _jsx(Presence, { present: gamesQuery.hasNextPage, children: _jsx(Button, { variant: "outline", className: twMerge('mx-auto mt-4xl w-fit', classNames.loadMoreButton), onClick: () => gamesQuery.fetchNextPage(), children: "Load More" }) })] }))] })) })) })] })) }) })] }) }));
61
+ }), alt: "", width: 200, height: 200, loading: "lazy", unoptimized: true, className: "aspect-square w-full rounded-t-md object-cover" }), _jsx("span", { className: twMerge('block w-full rounded-b-md bg-bg-tertiary px-2 py-2.5 text-center font-semibold text-text-primary-brand text-xs', props.variant !== '88play' &&
62
+ 'truncate', classNames.thumbnailTitle), children: game.name })] }, game.id))) })) }), _jsx(Presence, { present: gamesQuery.hasNextPage, children: _jsx(Button, { variant: "outline", className: twMerge('mx-auto mt-4xl w-fit', classNames.loadMoreButton), onClick: () => gamesQuery.fetchNextPage(), children: "Load More" }) })] }))] })) })) })] })) }) })] }) }));
60
63
  }
61
64
  function Alert({ message }) {
62
65
  return (_jsxs("div", { className: "py-lg", role: "alert", "aria-live": "polite", children: [_jsx("div", { className: "mx-auto flex size-12 items-center justify-center rounded-lg border border-border-primary bg-bg-secondary shadow-xs", children: _jsx(SearchLgIcon, { className: "size-6 text-text-secondary-700" }) }), _jsx("p", { className: "mt-4 text-center text-text-secondary-700", children: message })] }));
@@ -5,6 +5,7 @@ export interface ClassNameEntries {
5
5
  root?: string;
6
6
  gameThumbnailRoot?: string;
7
7
  gameThumbnailTitle?: string;
8
+ gameSearchResult?: string;
8
9
  providerThumbnailRoot?: string;
9
10
  providerThumbnailImage?: string;
10
11
  loadMoreButton?: string;
@@ -28,5 +29,6 @@ export interface SearchProps {
28
29
  gameProviderImages?: Partial<Record<GameProvider, ImageProps['src']>>;
29
30
  placeholder?: string;
30
31
  bypassDomains?: BypassDomainConfig[];
32
+ variant?: string;
31
33
  }
32
34
  export declare function Search(props: SearchProps): import("react/jsx-runtime").JSX.Element;
@@ -79,13 +79,13 @@ export function Search(props) {
79
79
  }, children: "Clear" }) })] }) }), _jsx("div", { className: "flex max-h-[85dvh] min-h-0 flex-1 flex-col", children: _jsx("div", { className: "mt-2xl p-xl pb-3xl lg:mt-2 lg:max-h-[41rem] lg:overflow-y-auto lg:rounded-xl lg:border lg:border-border-primary lg:bg-bg-primary", children: search.length <= 0 ? (_jsx(Alert, { message: "Search for your favorite game or provider." })) : search.length === 1 ? (_jsx(Alert, { message: "Search requires at least 2 characters." })) : (_jsxs(_Fragment, { children: [empty && _jsx(Alert, { message: "No results found" }), !empty && (_jsxs(_Fragment, { children: [gameProviders.length > 0 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "font-semibold text-lg", children: "Providers" }), _jsx("div", { className: "mt-3.5 mb-3xl grid grid-cols-3 gap-1.5 lg:grid-cols-9 lg:gap-3.5", children: gameProviders.map((provider) => (_jsx(Link, { href: viewGamesUrl(provider), "aria-label": `View ${provider.name} games`, className: twMerge('flex h-14 w-full items-center rounded-md bg-brand-800', classNames.providerThumbnailRoot), onClick: () => {
80
80
  globalStore.search.setOpen(false);
81
81
  }, children: _jsx(Image, { src: props.gameProviderImages?.[provider.id] ??
82
- provider.logo, alt: "", width: 100, height: 50, className: twMerge('mx-auto h-auto w-full', classNames.providerThumbnailImage) }) }, provider.id))) })] })), games.length > 0 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "font-semibold text-lg", children: "Games" }), _jsx("div", { className: "mt-3.5 grid grid-cols-3 gap-1.5 lg:grid-cols-9 lg:gap-3.5", children: games.map((game) => (_jsxs(GameLaunchTrigger, { bypassKycCheck: isBypass, game: game, className: twMerge('block w-full shadow-sm', classNames.gameThumbnailRoot), onClick: () => {
82
+ provider.logo, alt: "", width: 100, height: 50, className: twMerge('mx-auto h-auto w-full', classNames.providerThumbnailImage) }) }, provider.id))) })] })), games.length > 0 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "font-semibold text-lg", children: "Games" }), _jsx("div", { className: twMerge('mt-3.5 grid grid-cols-3 gap-1.5 lg:grid-cols-9 lg:gap-3.5', classNames.gameSearchResult), children: games.map((game) => (_jsxs(GameLaunchTrigger, { bypassKycCheck: isBypass, game: game, className: twMerge('block w-full shadow-sm', classNames.gameThumbnailRoot), onClick: () => {
83
83
  globalStore.search.setOpen(false);
84
84
  }, children: [_jsx(Image, { src: getGameImageUrl({
85
85
  reference: game.reference,
86
86
  provider: game.provider,
87
87
  image: game.image,
88
- }), alt: "", width: 200, height: 200, loading: "lazy", unoptimized: true, className: "aspect-square w-full rounded-t-md object-cover" }), _jsx("span", { className: twMerge('block w-full truncate rounded-b-md bg-bg-tertiary px-2 py-2.5 text-center font-semibold text-text-primary-brand text-xs', classNames.gameThumbnailTitle), children: game.name })] }, game.id))) })] })), _jsx(Presence, { present: gamesQuery.hasNextPage, children: _jsx(Button, { variant: "outline", className: twMerge('mx-auto mt-12 w-fit', classNames.loadMoreButton), onClick: () => gamesQuery.fetchNextPage(), children: "Load More" }) })] }))] })) }) })] }) })] }) }));
88
+ }), alt: "", width: 200, height: 200, loading: "lazy", unoptimized: true, className: "aspect-square w-full rounded-t-md object-cover" }), _jsx("span", { className: twMerge('block w-full rounded-b-md bg-bg-tertiary px-2 py-2.5 text-center font-semibold text-text-primary-brand text-xs', props.variant !== '88play' && 'truncate', classNames.gameThumbnailTitle), children: game.name })] }, game.id))) })] })), _jsx(Presence, { present: gamesQuery.hasNextPage, children: _jsx(Button, { variant: "outline", className: twMerge('mx-auto mt-12 w-fit', classNames.loadMoreButton), onClick: () => gamesQuery.fetchNextPage(), children: "Load More" }) })] }))] })) }) })] }) })] }) }));
89
89
  }
90
90
  function DebouncedInput(props) {
91
91
  const [value, setValue] = useControllableState({
@@ -0,0 +1,2 @@
1
+ export declare function SingleSignOn(): import("react/jsx-runtime").JSX.Element | undefined;
2
+ export declare function VerifyMobileNumber(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,173 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { zodResolver } from '@hookform/resolvers/zod';
4
+ import Image from 'next/image';
5
+ import { useParams, useRouter, useSearchParams } from 'next/navigation';
6
+ import { useEffect, useRef, useState } from 'react';
7
+ import { Controller, useForm } from 'react-hook-form';
8
+ import z from 'zod';
9
+ import cinePopLogo from '../../../lib/images/cinepop-logo.png';
10
+ import dearUtolLogo from '../../../lib/images/dear-utol-logo.png';
11
+ import inplayLogo from '../../../lib/images/inplay-logo.png';
12
+ import lightBg from '../../../lib/images/light-bg.png';
13
+ import mariasDiary from '../../../lib/images/marias-diary-logo.png';
14
+ import secretConfessionsLogo from '../../../lib/images/secret-confessions-logo.png';
15
+ import { useCooldown } from '../../client/hooks/useCooldown.js';
16
+ import { useLocaleInfo } from '../../client/hooks/useLocaleInfo.js';
17
+ import { useMobileNumberParser } from '../../client/hooks/useMobileNumberParser.js';
18
+ import { useSendVerificationCodeMutation } from '../../client/hooks/useSendVerificationCodeMutation.js';
19
+ import { useSignInSSOMutation } from '../../client/hooks/useSignInSSOMutation.js';
20
+ import { useVerifyMobileNumber } from '../../client/hooks/useVerifyMobileNumber.js';
21
+ import { toaster } from '../../client/utils/toaster.js';
22
+ import { ArrowLeftIcon } from '../../icons/ArrowLeftIcon.js';
23
+ import { Button } from '../../ui/Button/index.js';
24
+ import { Dialog } from '../../ui/Dialog/index.js';
25
+ import { Field } from '../../ui/Field/index.js';
26
+ import { PinInput } from '../../ui/PinInput/index.js';
27
+ import { Portal } from '../../ui/Portal/index.js';
28
+ export function SingleSignOn() {
29
+ const params = useParams();
30
+ const searchParams = useSearchParams();
31
+ const router = useRouter();
32
+ const partnerId = params.partnerId;
33
+ const token = searchParams.get('token');
34
+ const ssoMutation = useSignInSSOMutation({
35
+ onSuccess: () => {
36
+ console.log('SSO Sign-in successful:');
37
+ // Redirect to home or dashboard
38
+ router.push('/');
39
+ },
40
+ onError: (error) => {
41
+ console.log('error:', error);
42
+ console.error('SSO Login failed:', error);
43
+ },
44
+ });
45
+ // biome-ignore lint/correctness/useExhaustiveDependencies: We only want to trigger the mutation when the URL parameters change.
46
+ useEffect(() => {
47
+ // Trigger SSO login when component mounts
48
+ if (partnerId && token && !ssoMutation.isPending) {
49
+ ssoMutation.mutate({ partnerId, token });
50
+ }
51
+ }, [partnerId, token]);
52
+ if (ssoMutation.isPending) {
53
+ return (_jsx(Dialog.Root, { open: true, 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 h-[333px] w-[375px] flex-col items-center space-y-4 bg-[#111827] p-6 text-center", style: {
54
+ backgroundImage: `url(${lightBg.src})`,
55
+ }, children: [_jsx(Image, { src: inplayLogo, alt: "inplay logo", width: 82, height: 34, className: "h-auto w-[82px]" }), _jsx("div", { className: "mb-4 h-7 w-7 animate-spin rounded-full border-4 border-[#101730] border-t-[#F05027]" }), _jsx("div", { className: "text-[#CECFD2] text-sm", children: "We\u2019re creating an account for you..." }), _jsx("div", { className: "text-[#CECFD2] text-sm", children: "In the meantime, you can watch your favorite series on CinePop!" }), _jsxs("div", { className: "pt-3", children: [_jsx(Image, { src: cinePopLogo, alt: "cine poplogo", width: 151, height: 24, className: "mx-auto mb-2 h-auto w-[151px]" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Image, { src: secretConfessionsLogo, alt: "secret confessions logo", width: 104, height: 53, className: "h-auto w-[104px]" }), _jsx(Image, { src: dearUtolLogo, alt: "dear utol logo", width: 105, height: 53, className: "h-auto w-[105px]" }), _jsx(Image, { src: mariasDiary, alt: "maria's diary logo", width: 104, height: 53, className: "h-auto w-[104px]" })] })] })] }) })] }) }));
56
+ }
57
+ if (ssoMutation.isError) {
58
+ return _jsx(VerifyMobileNumber, {});
59
+ }
60
+ }
61
+ export function VerifyMobileNumber() {
62
+ const [step, setStep] = useState(1);
63
+ const sendVerificationCodeMutation = useSendVerificationCodeMutation({
64
+ onSuccess: () => {
65
+ setStep(2);
66
+ cooldown.start();
67
+ },
68
+ onError: (err) => {
69
+ toaster.error({
70
+ title: 'Sign In Failed',
71
+ description: err.message,
72
+ });
73
+ },
74
+ });
75
+ const verifyMobileNumberMutation = useVerifyMobileNumber({
76
+ onSuccess: async () => {
77
+ step1Form.reset();
78
+ step2Form.reset();
79
+ setStep(1);
80
+ toaster.success({
81
+ title: 'Verification Successful',
82
+ description: 'Your mobile number has been verified.',
83
+ });
84
+ },
85
+ onError: (err) => {
86
+ const errorMessage = err.name === 'Forbidden'
87
+ ? 'Please enter the correct verification code'
88
+ : err.message;
89
+ toaster.error({
90
+ title: 'Sign In Failed',
91
+ description: errorMessage,
92
+ });
93
+ },
94
+ });
95
+ const localeInfo = useLocaleInfo();
96
+ const mobileNumberParser = useMobileNumberParser();
97
+ const Step1Definition = z.object({
98
+ mobileNumber: z
99
+ .string()
100
+ .min(1, 'Mobile number is required')
101
+ .superRefine((v, ctx) => {
102
+ if (!mobileNumberParser.validate(v)) {
103
+ ctx.addIssue({
104
+ code: z.ZodIssueCode.custom,
105
+ message: 'Invalid mobile number',
106
+ });
107
+ }
108
+ }),
109
+ });
110
+ const Step2Definition = z.object({
111
+ verificationCode: z.array(z.string()).superRefine((val, ctx) => {
112
+ if (val.length !== 6 || val.some((v) => v.length !== 1)) {
113
+ ctx.addIssue({
114
+ code: z.ZodIssueCode.custom,
115
+ message: 'Please enter your 6-digit verification code.',
116
+ });
117
+ }
118
+ }),
119
+ });
120
+ const step1Form = useForm({
121
+ resolver: zodResolver(Step1Definition),
122
+ defaultValues: {
123
+ mobileNumber: '',
124
+ },
125
+ });
126
+ const step2Form = useForm({
127
+ resolver: zodResolver(Step2Definition),
128
+ defaultValues: {
129
+ verificationCode: Array.from({ length: 6 }).fill(''),
130
+ },
131
+ });
132
+ const cooldown = useCooldown({
133
+ max: 60,
134
+ duration: 1000 * 60,
135
+ });
136
+ const formRef = useRef(null);
137
+ return (_jsx(Dialog.Root, { open: true, 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: {
138
+ backgroundImage: `url(${lightBg.src})`,
139
+ }, children: [_jsx(Image, { src: inplayLogo, alt: "inplay logo", width: 82, height: 34, className: "h-auto w-[82px]" }), _jsxs("div", { children: [step === 1 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "font-bold text-sm", children: ["Get ", _jsx("span", { className: "text-[#F05127]", children: "\u20B150 Bonus" }), " when you verify your account and play."] }), _jsxs("form", { className: "mt-3xl", onSubmit: step1Form.handleSubmit(async (data) => {
140
+ sendVerificationCodeMutation.mutateAsync({
141
+ channel: 'SMS',
142
+ recipient: mobileNumberParser.format(data.mobileNumber),
143
+ strict: true,
144
+ });
145
+ }), children: [_jsxs(Field.Root, { invalid: !!step1Form.formState.errors.mobileNumber, className: "text-left", children: [_jsx(Field.Label, { children: "Mobile Number" }), _jsxs("div", { className: "relative", children: [_jsx("div", { className: "-translate-y-1/2 absolute top-1/2 left-3.5 flex shrink-0 items-center gap-md", children: _jsx("span", { className: "text-text-placeholder", children: localeInfo.mobileNumber.areaCode }) }), _jsx(Field.Input, { style: {
146
+ paddingLeft: `calc(1.25rem + ${localeInfo.mobileNumber.areaCode.length}ch)`,
147
+ }, ...step1Form.register('mobileNumber') })] }), _jsx(Field.ErrorText, { children: step1Form.formState.errors.mobileNumber?.message })] }), _jsx(Button, { type: "submit", className: "mt-3xl", disabled: step1Form.formState.isSubmitting, children: "Send Code" })] })] })), step === 2 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "mt-xl text-center font-semibold text-lg", children: "Check your Phone" }), _jsxs("p", { className: "mt-xs text-center text-sm text-text-secondary-700", children: ["We\u2019ve sent a verification code to your mobile number", ' ', _jsx("span", { className: "font-semibold text-[#F05127]", children: mobileNumberParser.format(step1Form.getValues('mobileNumber')) }), ' ', "via text"] }), _jsxs("form", { ref: formRef, className: "mt-5", onSubmit: step2Form.handleSubmit(async ({ verificationCode }) => {
148
+ verifyMobileNumberMutation.mutateAsync({
149
+ verificationCode: verificationCode.join(''),
150
+ });
151
+ }), children: [_jsx(Controller, { name: "verificationCode", control: step2Form.control, render: (o) => (_jsxs(Field.Root, { invalid: o.fieldState.invalid, children: [_jsxs(PinInput.Root, { placeholder: "0", onKeyDown: (e) => {
152
+ if (e.key === 'Backspace') {
153
+ step2Form.reset();
154
+ }
155
+ }, value: o.field.value, onValueChange: (details) => {
156
+ o.field.onChange(details.value);
157
+ o.field.onBlur();
158
+ }, otp: true, onValueComplete: () => {
159
+ formRef.current?.requestSubmit();
160
+ }, blurOnComplete: true, readOnly: step2Form.formState.isSubmitting, type: "numeric", children: [_jsxs(PinInput.Control, { className: "grid-cols-[1fr_1fr_1fr_auto_1fr_1fr_1fr] items-center gap-md", children: [_jsx(PinInput.Input, { index: 0 }), _jsx(PinInput.Input, { index: 1 }), _jsx(PinInput.Input, { index: 2 }), _jsx("span", { className: "font-medium text-2xl text-text-placeholder-subtle", children: "\u2013" }), _jsx(PinInput.Input, { index: 3 }), _jsx(PinInput.Input, { index: 4 }), _jsx(PinInput.Input, { index: 5 })] }), _jsx(PinInput.HiddenInput, {})] }), _jsx(Field.ErrorText, { children: o.fieldState.error?.message })] })) }), _jsx(Button, { type: "submit", className: "mt-4xl", disabled: step2Form.formState.isSubmitting, children: "Verify" }), _jsxs("div", { className: "mt-4 flex w-full items-center justify-center gap-xs text-xs", children: [_jsx("span", { className: "text-[#9CA3AF]", children: "Didn't receive the code?" }), _jsx("button", { type: "button", className: "font-semibold text-[#C084FC]", disabled: cooldown.cooling, onClick: async () => {
161
+ await sendVerificationCodeMutation.mutateAsync({
162
+ channel: 'SMS',
163
+ recipient: mobileNumberParser.format(step1Form.getValues('mobileNumber')),
164
+ });
165
+ cooldown.start();
166
+ }, children: cooldown.cooling
167
+ ? `Resend in ${cooldown.countdown}s`
168
+ : 'Resend' })] }), _jsx("button", { type: "button", className: "absolute top-0 left-6 mx-auto mt-3xl flex h-8 w-8 items-center gap-1 rounded-full bg-[#1f2638] font-semibold text-sm text-text-tertiary-600", onClick: () => {
169
+ setStep(1);
170
+ step2Form.reset();
171
+ cooldown.stop();
172
+ }, children: _jsx(ArrowLeftIcon, { className: "mx-auto size-5" }) })] })] }))] })] }) })] }) }));
173
+ }
@@ -0,0 +1 @@
1
+ export * from './SingleSignOn';
@@ -0,0 +1 @@
1
+ export * from './SingleSignOn.js';
@@ -290,6 +290,12 @@ export const GAME_PROVIDER_DATA = {
290
290
  slug: 'evolution-netent',
291
291
  logo: evolutionNetent,
292
292
  },
293
+ EVOLUTIONNETENT: {
294
+ id: 'EVOLUTIONNETENT',
295
+ name: 'Evolution Netent',
296
+ slug: 'evolution-netent',
297
+ logo: evolutionNetent,
298
+ },
293
299
  DIGITAIN: {
294
300
  id: 'DIGITAIN',
295
301
  name: 'Digitain',
@@ -332,6 +338,12 @@ export const GAME_PROVIDER_DATA = {
332
338
  slug: 'oneapi-spadegaming',
333
339
  logo: spadegaming,
334
340
  },
341
+ TEST: {
342
+ id: 'TEST',
343
+ name: 'Test',
344
+ slug: 'test',
345
+ logo: '',
346
+ },
335
347
  };
336
348
  export const GAME_PROVIDERS = Object.values(GAME_PROVIDER_DATA).map(({ id }) => id);
337
349
  export const LEGACY_GAME_PROVIDERS = [
@@ -8,23 +8,23 @@ export declare const createForgotPasswordSchema: (mobileNumberParser: MobileNumb
8
8
  mobileNumber: z.ZodEffects<z.ZodString, string, string>;
9
9
  verificationCode: z.ZodEffects<z.ZodString, string, string>;
10
10
  }, "strip", z.ZodTypeAny, {
11
- password: string;
12
11
  verificationCode: string;
12
+ password: string;
13
13
  mobileNumber: string;
14
14
  confirmPassword: string;
15
15
  }, {
16
- password: string;
17
16
  verificationCode: string;
17
+ password: string;
18
18
  mobileNumber: string;
19
19
  confirmPassword: string;
20
20
  }>, {
21
- password: string;
22
21
  verificationCode: string;
22
+ password: string;
23
23
  mobileNumber: string;
24
24
  confirmPassword: string;
25
25
  }, {
26
- password: string;
27
26
  verificationCode: string;
27
+ password: string;
28
28
  mobileNumber: string;
29
29
  confirmPassword: string;
30
30
  }>;
@@ -222,6 +222,9 @@ export type VerifyMobileNumberError = {
222
222
  export interface VerifyMobileNumberMutation {
223
223
  verifyMobileNumber?: null | VerifyMobileNumberError;
224
224
  }
225
+ export type VerifyMobileNumberInput = {
226
+ verificationCode: string;
227
+ };
225
228
  export interface VerifyMobileNumberMutationVariables {
226
229
  input: {
227
230
  verificationCode: string;
@@ -97,3 +97,8 @@ export interface CreateSingleUseTokenMutation {
97
97
  token: string;
98
98
  }
99
99
  export declare function createSingleUseToken(options?: HttpRequestOptions): Promise<CreateSingleUseTokenMutation>;
100
+ export interface SignInSSOInput {
101
+ partnerId: string;
102
+ token: string;
103
+ }
104
+ export declare function signInSSO(input: SignInSSOInput, options?: HttpRequestOptions): Promise<CreateSessionMutation_next>;
@@ -169,3 +169,29 @@ export async function createSingleUseToken(options) {
169
169
  throw error;
170
170
  }
171
171
  }
172
+ export async function signInSSO(input, options) {
173
+ const { partnerId, token } = input;
174
+ const authString = btoa(`${partnerId}:${token}`);
175
+ const headers = new Headers();
176
+ headers.set('Authorization', `OPEXA_SINGLE_SIGN_ON ${authString}`);
177
+ try {
178
+ const response = await httpRequest.json(`${AUTH_ENDPOINT}/v3/sessions`, {
179
+ ...options,
180
+ method: 'POST',
181
+ headers,
182
+ });
183
+ return response;
184
+ }
185
+ catch (e) {
186
+ if (e instanceof Error) {
187
+ throw e;
188
+ }
189
+ else {
190
+ const error = new Error();
191
+ error.name = 'AccountNotFoundError';
192
+ error.message = 'Account not found';
193
+ Error.captureStackTrace?.(error, createSession);
194
+ throw error;
195
+ }
196
+ }
197
+ }
@@ -157,7 +157,7 @@ export interface Announcement {
157
157
  dateTimeLastUpdated: string;
158
158
  }
159
159
  export type GameType = 'SLOTS' | 'SPORTS' | 'FISHING' | 'BINGO' | 'LIVE' | 'GAMES' | 'TABLE' | 'SPECIALTY' | 'NUMERICAL' | 'NUMERIC' | 'ARCADE';
160
- export type GameProvider = 'JILI' | 'JILI_BINGO' | 'PGSOFT' | 'FACHAI' | 'BTI' | 'DG' | 'PLAYTECH' | 'E2E' | 'ONEAPI_EVOLUTION' | 'EVOLUTION' | 'EVOLUTION_NETENT' | 'EVOLUTION_REDTIGER' | 'MEGABALL' | 'MEGA2SPIN' | 'DARWIN' | 'RTG' | 'DRBINGO' | 'HOLLYWOODTV' | 'CQ9' | 'JDB' | 'HABANERO' | 'SPINIX' | 'JOKER' | 'HACKSAW' | 'JDBGTF' | 'JDBSPRIBE' | 'MICROGAMING' | 'RELAXGAMING' | 'EVOPLAY' | 'BOOONGO' | 'BGAMING' | 'KINGMAKER' | 'KINGMIDAS' | 'YELLOWBAT' | 'ETENGJUE' | 'SABA' | 'PRAGMATICPLAY' | 'SPRIBE' | 'EZUGI' | 'ALIZE' | 'DIGITAIN' | 'BNG' | 'NO_LIMIT_CITY' | 'BIG_TIME_GAMING' | 'SAGAMING' | 'ONEAPI_SPADEGAMING' | 'JK8' | 'RUBYPLAY';
160
+ export type GameProvider = 'JILI' | 'JILI_BINGO' | 'PGSOFT' | 'FACHAI' | 'BTI' | 'DG' | 'PLAYTECH' | 'E2E' | 'ONEAPI_EVOLUTION' | 'EVOLUTION' | 'EVOLUTION_NETENT' | 'EVOLUTION_REDTIGER' | 'MEGABALL' | 'MEGA2SPIN' | 'DARWIN' | 'RTG' | 'DRBINGO' | 'HOLLYWOODTV' | 'CQ9' | 'JDB' | 'HABANERO' | 'SPINIX' | 'JOKER' | 'HACKSAW' | 'JDBGTF' | 'JDBSPRIBE' | 'MICROGAMING' | 'RELAXGAMING' | 'EVOPLAY' | 'BOOONGO' | 'BGAMING' | 'KINGMAKER' | 'KINGMIDAS' | 'YELLOWBAT' | 'ETENGJUE' | 'SABA' | 'PRAGMATICPLAY' | 'SPRIBE' | 'EZUGI' | 'ALIZE' | 'DIGITAIN' | 'BNG' | 'NO_LIMIT_CITY' | 'BIG_TIME_GAMING' | 'SAGAMING' | 'ONEAPI_SPADEGAMING' | 'JK8' | 'RUBYPLAY' | 'EVOLUTIONNETENT' | 'TEST';
161
161
  export type GameStatus = 'ACTIVE' | 'INACTIVE';
162
162
  export type GameTag = 'HOT' | 'hot' | 'NEW' | 'new' | 'TOP' | 'top' | 'POPULAR' | 'popular' | (string & {});
163
163
  export interface Game {