@opexa/portal-components 0.0.869 → 0.0.870

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.
@@ -13,17 +13,21 @@ import { Combobox } from '../../ui/Combobox/index.js';
13
13
  import { Portal } from '../../ui/Portal/index.js';
14
14
  import { Presence } from '../../ui/Presence/index.js';
15
15
  import { getGameImageUrl } from '../../utils/getGameImageUrl.js';
16
+ import { sanitizeGamesSearch } from '../../utils/sanitizeGamesSearch.js';
16
17
  import { GameLaunchTrigger } from '../GameLaunch/index.js';
17
18
  export function GamesSearch(props) {
18
19
  const isBypass = useBypassKycChecker(props.bypassDomains);
19
- const [search, setSearch] = useState('');
20
+ const [searchInput, setSearchInput] = useState('');
21
+ const sanitizedSearch = sanitizeGamesSearch(searchInput);
22
+ const trimmedInputLength = searchInput.trim().length;
23
+ const showMinLengthWarning = trimmedInputLength > 0 && trimmedInputLength < 3 && sanitizedSearch.length < 3;
20
24
  const gamesQuery = useGamesQuery({
21
25
  first: props.first ?? 18,
22
26
  filter: props.filter,
23
- search,
27
+ search: sanitizedSearch,
24
28
  sort: props.sort,
25
29
  }, {
26
- enabled: search.length >= 3,
30
+ enabled: sanitizedSearch.length >= 3,
27
31
  });
28
32
  const games = gamesQuery.data?.pages.flatMap((page) => page.edges.map((edge) => edge.node)) ?? [];
29
33
  const collection = createListCollection({
@@ -37,8 +41,8 @@ export function GamesSearch(props) {
37
41
  return (_jsx("div", { className: classNames.root, children: _jsxs(Combobox.Root, { collection: collection, positioning: {
38
42
  sameWidth: true,
39
43
  placement: 'bottom',
40
- }, inputValue: search, onInputValueChange: (details) => {
41
- setSearch(details.inputValue);
44
+ }, inputValue: searchInput, onInputValueChange: (details) => {
45
+ setSearchInput(details.inputValue);
42
46
  }, selectionBehavior: "preserve", allowCustomValue: true, children: [_jsxs(Combobox.Control, { className: "relative z-50", children: [_jsx(Combobox.Input, { placeholder: props.placeholder ?? 'Search' }), _jsx(Combobox.Context, { children: (api) => {
43
47
  if (api.inputValue.length <= 0)
44
48
  return null;
@@ -46,7 +50,7 @@ export function GamesSearch(props) {
46
50
  api.setInputValue('');
47
51
  api.focus();
48
52
  }, children: "Clear" }));
49
- } })] }), _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: search.trim().length > 0 && (_jsx(Combobox.Content, { className: "z-50 max-h-[33.25rem] overflow-y-auto p-0", children: search.length >= 1 && search.length <= 2 ? (_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: () => {
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: () => {
50
54
  api.setOpen(false);
51
55
  }, className: twMerge('block w-full shadow-sm', classNames.thumbnailRoot), children: [_jsx(Image, { src: getGameImageUrl({
52
56
  reference: game.reference,
@@ -1,5 +1,6 @@
1
+ import type { SignUpCrazyWinProps } from './SignUpCrazyWin/SignUpCrazyWin.lazy';
1
2
  import type { SignUpDefaultProps } from './SignUpDefault/SignUpDefault.lazy';
2
3
  import type { SignUpKYCProps } from './SignUpKYC/SignUpKYC.lazy';
3
4
  import type { SignUpNameAndPasswordProps } from './SignUpNameAndPassword/SignUpNameAndPassword.lazy';
4
- export type SignUpProps = SignUpDefaultProps | SignUpKYCProps | SignUpNameAndPasswordProps;
5
+ export type SignUpProps = SignUpDefaultProps | SignUpKYCProps | SignUpNameAndPasswordProps | SignUpCrazyWinProps;
5
6
  export declare function SignUp(props: SignUpProps): import("react/jsx-runtime").JSX.Element | null;
@@ -15,6 +15,10 @@ const SignUpNameAndPassword = dynamic(() => import('./SignUpNameAndPassword/Sign
15
15
  ssr: false,
16
16
  loading: () => null,
17
17
  });
18
+ const SignUpCrazyWin = dynamic(() => import('./SignUpCrazyWin/SignUpCrazyWin.lazy.js').then((m) => m.SignUpCrazyWin), {
19
+ ssr: false,
20
+ loading: () => null,
21
+ });
18
22
  export function SignUp(props) {
19
23
  const touched = useGlobalStore(useShallow((ctx) => ctx.signUp['~touched']));
20
24
  if (!touched) {
@@ -26,6 +30,9 @@ export function SignUp(props) {
26
30
  else if (props.layout === 'nameAndPassword') {
27
31
  return _jsx(SignUpNameAndPassword, { ...props });
28
32
  }
33
+ else if (props.layout === 'crazywin') {
34
+ return _jsx(SignUpCrazyWin, { ...props });
35
+ }
29
36
  else {
30
37
  return _jsx(SignUpDefault, { ...props });
31
38
  }
@@ -0,0 +1,22 @@
1
+ import { type ImageProps } from 'next/image';
2
+ import { type ReactNode } from 'react';
3
+ import type { Branch } from '../../../types';
4
+ export interface ClassNameEntries {
5
+ root?: string;
6
+ publicPlayProhibitionRoot?: string;
7
+ publicPlayProhibitionLogoContainer?: string;
8
+ }
9
+ export interface SignUpCrazyWinProps {
10
+ layout: 'crazywin';
11
+ logo: ImageProps['src'];
12
+ branches?: Branch[];
13
+ termsAndConditionsUrl?: string;
14
+ responsibleGamingUrl?: string;
15
+ className?: ClassNameEntries;
16
+ children?: ReactNode;
17
+ showPublicPlayProhibition?: boolean;
18
+ pagcorLogo?: ImageProps['src'];
19
+ responsibleGamingLogo?: ImageProps['src'];
20
+ isRegulated?: boolean;
21
+ }
22
+ export declare function SignUpCrazyWin(props: SignUpCrazyWinProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import Image, {} from 'next/image';
4
+ import { Suspense } from 'react';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { useShallow } from 'zustand/shallow';
7
+ import { useGlobalStore } from '../../../client/hooks/useGlobalStore.js';
8
+ import { XIcon } from '../../../icons/XIcon.js';
9
+ import { Dialog } from '../../../ui/Dialog/index.js';
10
+ import { Portal } from '../../../ui/Portal/index.js';
11
+ import { SignUpCrazyWinPropsProvider } from './SignUpCrazyWinContext.js';
12
+ import { SignUpCrazyWinForm } from './SignUpCrazyWinForm.js';
13
+ export function SignUpCrazyWin(props) {
14
+ const signUpStore = useGlobalStore(useShallow((ctx) => ctx.signUp));
15
+ const globalStore = useGlobalStore(useShallow((ctx) => ({
16
+ termsAndConditions: ctx.termsAndConditions,
17
+ responsibleGaming: ctx.responsibleGaming,
18
+ })));
19
+ return (_jsx(SignUpCrazyWinPropsProvider, { value: props, children: _jsx(Dialog.Root, { open: signUpStore.open, onOpenChange: (details) => {
20
+ signUpStore.setOpen(details.open);
21
+ }, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, onExitComplete: () => {
22
+ globalStore.termsAndConditions.setAccepted(false);
23
+ globalStore.responsibleGaming.setAccepted(false);
24
+ }, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: _jsxs(Dialog.Content, { className: twMerge('h-full w-full lg:mx-auto lg:grid lg:h-[50rem] lg:w-[44.25rem] lg:grid-cols-2', props.className?.root), 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: 'border-white/10 bg-[radial-gradient(103.87%_100%_at_50.15%_0%,_#072b37_20.3%,_#051125_100%)] backdrop-blur-[25px] lg:rounded-r-xl', children: [_jsx(Dialog.CloseTrigger, { className: "md:-right-10 -top-15 absolute 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(Suspense, { children: _jsx(SignUpCrazyWinForm, {}) })] })] }) })] }) }) }));
25
+ }
@@ -0,0 +1,6 @@
1
+ import type { SignUpCrazyWinProps } from './SignUpCrazyWin.lazy';
2
+ export declare const SignUpCrazyWinPropsProvider: (props: {
3
+ value: SignUpCrazyWinProps;
4
+ } & {
5
+ children?: import("react").ReactNode | undefined;
6
+ }) => React.ReactNode, useSignUpCrazyWinPropsContext: () => SignUpCrazyWinProps;
@@ -0,0 +1,2 @@
1
+ import { createContext } from '../../../client/utils/createContext.js';
2
+ export const [SignUpCrazyWinPropsProvider, useSignUpCrazyWinPropsContext,] = createContext();
@@ -0,0 +1 @@
1
+ export declare function SignUpCrazyWinForm(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,282 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { createListCollection, parseDate } from '@ark-ui/react';
4
+ import { zodResolver } from '@hookform/resolvers/zod';
5
+ import { ObjectId } from '@opexa/object-id';
6
+ import { differenceInYears, format } from 'date-fns';
7
+ import Image from 'next/image';
8
+ import { useRouter } from 'next/navigation';
9
+ import { Controller, useForm } from 'react-hook-form';
10
+ import { twMerge } from 'tailwind-merge';
11
+ import { z } from 'zod';
12
+ import { useShallow } from 'zustand/shallow';
13
+ import { useControllableState } from '../../../client/hooks/useControllableState.js';
14
+ import { useCooldown } from '../../../client/hooks/useCooldown.js';
15
+ import { useGlobalStore } from '../../../client/hooks/useGlobalStore.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 { useSignUpMutation } from '../../../client/hooks/useSignUpMutation.js';
20
+ import { toaster } from '../../../client/utils/toaster.js';
21
+ import { CRAZY_WIN_BRANCH_CODES } from '../../../constants/BranchCodes.js';
22
+ import { CalendarIcon } from '../../../icons/CalendarIcon.js';
23
+ import { CheckIcon } from '../../../icons/CheckIcon.js';
24
+ import { ChevronLeftIcon } from '../../../icons/ChevronLeftIcon.js';
25
+ import { ChevronRightIcon } from '../../../icons/ChevronRightIcon.js';
26
+ import { HelpCircleIcon } from '../../../icons/HelpCircleIcon.js';
27
+ import calendarIcon from '../../../images/calendar-icon.webp';
28
+ import { ObjectType } from '../../../services/ObjectType.js';
29
+ import { Button } from '../../../ui/Button/index.js';
30
+ import { Checkbox } from '../../../ui/Checkbox/index.js';
31
+ import { DatePicker } from '../../../ui/DatePicker/index.js';
32
+ import { Field } from '../../../ui/Field/index.js';
33
+ import { Select } from '../../../ui/Select/index.js';
34
+ import { Tooltip } from '../../../ui/Tooltip/index.js';
35
+ import { useSignUpCrazyWinPropsContext } from './SignUpCrazyWinContext.js';
36
+ export function SignUpCrazyWinForm() {
37
+ const signUpProps = useSignUpCrazyWinPropsContext();
38
+ const signUpMutation = useSignUpMutation();
39
+ const branchCollection = createListCollection({
40
+ items: CRAZY_WIN_BRANCH_CODES || [],
41
+ itemToValue: (item) => item.code,
42
+ itemToString: (item) => item.name,
43
+ });
44
+ const router = useRouter();
45
+ const globalStore = useGlobalStore(useShallow((ctx) => ({
46
+ kycReminder: ctx.kycReminder,
47
+ signIn: ctx.signIn,
48
+ signUp: ctx.signUp,
49
+ termsAndConditions: ctx.termsAndConditions,
50
+ responsibleGaming: ctx.responsibleGaming,
51
+ kyc: ctx.kyc,
52
+ })));
53
+ const mobileNumberParser = useMobileNumberParser();
54
+ const definition = z.object({
55
+ mobileNumber: z
56
+ .string()
57
+ .min(1, 'Mobile number is required')
58
+ .superRefine((v, ctx) => {
59
+ if (!mobileNumberParser.validate(v)) {
60
+ ctx.addIssue({
61
+ code: z.ZodIssueCode.custom,
62
+ message: 'Invalid mobile number',
63
+ });
64
+ }
65
+ }),
66
+ firstName: z
67
+ .string()
68
+ .regex(/^[a-z ]+$/gi, 'First name must contain only letters')
69
+ .transform((val) => val.trim())
70
+ .refine((val) => val.length >= 2, {
71
+ message: 'First name must be 2 or more characters',
72
+ })
73
+ .refine((val) => val.length <= 50, {
74
+ message: 'First name must not be more than 50 characters',
75
+ }),
76
+ lastName: z
77
+ .string()
78
+ .regex(/^[a-z ]+$/gi, 'Last name must contain only letters')
79
+ .transform((val) => val.trim())
80
+ .refine((val) => val.length >= 2, {
81
+ message: 'Last name must be 2 or more characters',
82
+ })
83
+ .refine((val) => val.length <= 50, {
84
+ message: 'Last name must not be more than 50 characters',
85
+ }),
86
+ birthDay: z
87
+ .date({
88
+ invalid_type_error: 'Date of birth is required',
89
+ required_error: 'Date of birth is required',
90
+ })
91
+ .superRefine((val, ctx) => {
92
+ const now = new Date();
93
+ const age = differenceInYears(now, val);
94
+ if (age < 21) {
95
+ return ctx.addIssue({
96
+ code: z.ZodIssueCode.custom,
97
+ message: 'You must be at least 21 years old',
98
+ });
99
+ }
100
+ }),
101
+ branchCode: z.string().min(1, 'Branch is required').trim(),
102
+ verificationCode: z.string().regex(/^\d{4,6}$/, 'Invalid OTP'),
103
+ termsAccepted: z.boolean().superRefine((v, ctx) => {
104
+ if (!v) {
105
+ ctx.addIssue({
106
+ code: z.ZodIssueCode.custom,
107
+ message: 'You must accept the terms and conditions',
108
+ });
109
+ }
110
+ }),
111
+ });
112
+ const form = useForm({
113
+ mode: 'all',
114
+ resolver: zodResolver(definition),
115
+ defaultValues: {
116
+ firstName: '',
117
+ lastName: '',
118
+ branchCode: '',
119
+ termsAccepted: globalStore.responsibleGaming.accepted &&
120
+ globalStore.termsAndConditions.accepted
121
+ ? true
122
+ : false,
123
+ },
124
+ });
125
+ const localeInfo = useLocaleInfo();
126
+ const cooldown = useCooldown({
127
+ max: 60,
128
+ duration: 1000 * 60,
129
+ });
130
+ const sendVerificationCodeMutation = useSendVerificationCodeMutation({
131
+ onSuccess: () => {
132
+ cooldown.start();
133
+ },
134
+ onError: (err) => {
135
+ toaster.error({
136
+ title: 'Sign In Failed',
137
+ description: err.message,
138
+ });
139
+ },
140
+ });
141
+ const commonInputClass = 'h-10 w-full rounded-full border-none bg-black/40 text-xs placeholder:text-text-placeholder focus:outline-none focus:ring-0';
142
+ return (_jsxs("div", { className: "scrollbar-gray-hover h-full flex-1 gap-6 overflow-y-auto px-6 pt-4 pb-[1.625rem] md:pt-11", children: [_jsx("h2", { className: "bg-[linear-gradient(50deg,_#c7802d_-5.1%,_#ffe585_44.95%,_#c7802d_109.05%)] bg-clip-text text-center font-semibold text-2xl text-transparent uppercase", children: "Register" }), _jsxs("form", { autoComplete: "off", onSubmit: form.handleSubmit(async (data) => {
143
+ const id = ObjectId.generate(ObjectType.MemberAccount).toString();
144
+ try {
145
+ await signUpMutation.mutateAsync({
146
+ id,
147
+ realName: `${data.firstName} ${data.lastName}`,
148
+ birthDay: format(data.birthDay, 'yyyy-MM-dd'),
149
+ branchCode: data.branchCode,
150
+ mobileNumber: mobileNumberParser.format(data.mobileNumber),
151
+ verificationCode: data.verificationCode,
152
+ });
153
+ globalStore.signUp.setOpen(false);
154
+ form.reset();
155
+ globalStore.signIn.setOpen(true);
156
+ }
157
+ catch (error) {
158
+ toaster.error({
159
+ description: error instanceof Error ? error.message : 'Something went wrong',
160
+ });
161
+ }
162
+ }), children: [_jsxs(Field.Root, { invalid: !!form.formState.errors.mobileNumber, children: [_jsx(Field.Label, { className: "text-xs", children: "Phone Number" }), _jsxs("div", { className: "relative flex h-10 gap-3", children: [_jsxs("div", { className: "flex h-full items-center gap-2 rounded-full bg-black/40 px-3.5 text-xs", children: [_jsx(localeInfo.country.flag, { className: "size-5" }), _jsx("span", { className: "text-text-placeholder", children: localeInfo.mobileNumber.areaCode })] }), _jsx(Field.Input, { className: commonInputClass, placeholder: "Enter Phone Number", ...form.register('mobileNumber') })] }), _jsx(Field.ErrorText, { className: "text-[#ff2525b3] text-xs", children: form.formState.errors.mobileNumber?.message })] }), _jsxs(Field.Root, { invalid: !!form.formState.errors.firstName, className: "mt-xl", children: [_jsx(Field.Label, { className: "text-xs", children: "First Name" }), _jsx(Field.Input, { className: commonInputClass, placeholder: "Enter your first name", ...form.register('firstName') }), _jsx(Field.ErrorText, { className: "text-[#ff2525b3] text-xs", children: form.formState.errors.firstName?.message })] }), _jsxs(Field.Root, { invalid: !!form.formState.errors.lastName, className: "mt-xl", children: [_jsx(Field.Label, { className: "text-xs", children: "Last Name" }), _jsx(Field.Input, { className: commonInputClass, placeholder: "Enter your last name", ...form.register('lastName') }), _jsx(Field.ErrorText, { className: "text-[#ff2525b3] text-xs", children: form.formState.errors.lastName?.message })] }), _jsxs(Field.Root, { invalid: !!form.formState.errors.birthDay, className: "mt-xl", children: [_jsx(DateOfBirthField, { value: form.watch('birthDay'), onChange: (value) => {
163
+ if (!value)
164
+ return;
165
+ form.setValue('birthDay', value, {
166
+ shouldDirty: true,
167
+ shouldTouch: true,
168
+ shouldValidate: true,
169
+ });
170
+ }, onBlur: () => form.trigger('birthDay') }), _jsx(Field.ErrorText, { className: "text-[#ff2525b3] text-xs", children: form.formState.errors.birthDay?.message })] }), _jsxs(Field.Root, { invalid: !!form.formState.errors.branchCode, className: "mt-xl", children: [_jsxs(Select.Root, { value: [form.watch('branchCode') ?? ''], onValueChange: (details) => {
171
+ form.setValue('branchCode', details.value?.[0], {
172
+ shouldDirty: true,
173
+ shouldTouch: true,
174
+ shouldValidate: true,
175
+ });
176
+ }, collection: branchCollection, positioning: {
177
+ sameWidth: true,
178
+ placement: 'bottom',
179
+ }, lazyMount: true, unmountOnExit: true, children: [_jsxs(Select.Label, { className: "flex items-center gap-1 text-xs", children: ["Branch Code", _jsxs(Tooltip.Root, { openDelay: 0, closeDelay: 100, lazyMount: true, unmountOnExit: true, positioning: {
180
+ placement: 'top',
181
+ }, children: [_jsx(Tooltip.Trigger, { asChild: true, children: _jsx(HelpCircleIcon, { className: "size-4 text-text-nav-item-button-icon" }) }), _jsx(Tooltip.Positioner, { children: _jsxs(Tooltip.Content, { children: [_jsx(Tooltip.Arrow, { children: _jsx(Tooltip.ArrowTip, {}) }), "Choose branch closest to your residence."] }) })] })] }), _jsx(Select.Trigger, { className: twMerge('h-10 rounded-full border-none bg-black/40 px-4 text-xs'), children: form.watch('branchCode') ? (_jsx(Select.ValueText, {})) : (_jsx("span", { className: "text-text-placeholder", children: "Select Branch Code" })) }), _jsx(Select.Positioner, { children: _jsx(Select.Content, { children: CRAZY_WIN_BRANCH_CODES.map((item) => {
182
+ return (_jsx(Select.Item, { item: item, children: _jsx("div", { title: item.name, className: "line-clamp-1", children: item.name }) }, item.code));
183
+ }) }) })] }), _jsx(Field.ErrorText, { className: "text-[#ff2525b3] text-xs", children: form.formState.errors.branchCode?.message })] }), _jsxs(Field.Root, { invalid: !!form.formState.errors.firstName, className: "mt-xl", children: [_jsx(Field.Label, { className: "text-xs", children: "OTP Code" }), _jsxs("div", { className: "relative flex", children: [_jsx(Field.Input, { className: commonInputClass, placeholder: "Enter OTP code", ...form.register('verificationCode') }), _jsx("button", { type: "button", className: "-translate-y-1/2 absolute top-1/2 right-1 inline-flex h-fit w-fit items-center justify-center gap-2 rounded-full bg-[linear-gradient(89deg,#c7802d_-15.91%,#ffe585_28.75%,#ffeca6_49.46%,#c7802d_112.69%)] px-3.5 py-[0.375rem] font-semibold text-[#6c5200] text-sm shadow-[0px_-1px_2px_1px_#fff7e1_inset] [text-shadow:0px_1px_0px_rgba(255,255,255,0.9)] active:bg-[linear-gradient(89.27deg,#c7802d_-33.65%,#ffe585_12.75%,#ffeca6_80.96%,#c7802d_128.79%)] disabled:cursor-not-allowed disabled:opacity-50", style: {
184
+ boxShadow: 'inset 0px -1px 2px 1px #fff7e1',
185
+ backgroundImage: 'linear-gradient(89deg, #c7802d -15.91%, #ffe585 28.75%, #ffeca6 49.46%, #c7802d 112.69%)',
186
+ }, disabled: cooldown.cooling ||
187
+ !form.getValues('mobileNumber') ||
188
+ !mobileNumberParser.validate(form.getValues('mobileNumber')), onClick: async () => {
189
+ await sendVerificationCodeMutation.mutateAsync({
190
+ channel: 'SMS',
191
+ recipient: mobileNumberParser.format(form.getValues('mobileNumber')),
192
+ });
193
+ cooldown.start();
194
+ }, children: cooldown.cooling ? `${cooldown.countdown}s` : 'Get OTP' })] }), _jsx(Field.ErrorText, { className: "text</button>-[#ff2525b3] text-xs", children: form.formState.errors.verificationCode?.message })] }), _jsxs("div", { className: "mt-4 flex gap-4.5 md:mt-7.5", children: [_jsx(Button, { type: "button", onClick: () => form.reset(), className: "flex h-10 w-full items-center justify-center gap-2 rounded-full border border-[#ffe5af] bg-[radial-gradient(365.61%_103.33%_at_54.76%_0%,#656564_0%,#031d18_22.17%,#031d19_78.16%,#4a4a45_98.9%)] px-3.5 font-semibold text-[#ffe5af] text-sm shadow-[0px_3px_6px_0px_rgba(255,228,183,0.3)] active:bg-[radial-gradient(365.61%_103.33%_at_54.76%_0%,#656564_0%,#282433_22.17%,#282433_78.16%,#4a4a45_98.9%)] disabled:cursor-not-allowed disabled:opacity-50", children: "Reset" }), _jsx(Button, { className: "h-10 rounded-full text-sm", type: "submit", disabled: form.formState.isSubmitting, style: {
195
+ boxShadow: 'inset 0px -1px 2px 1px #fff7e1',
196
+ backgroundImage: 'linear-gradient(89deg, #c7802d -15.91%, #ffe585 28.75%, #ffeca6 49.46%, #c7802d 112.69%)',
197
+ }, children: "Continue" })] }), _jsx(Controller, { control: form.control, name: "termsAccepted", render: (o) => (_jsxs(Field.Root, { className: "mt-2xl", invalid: o.fieldState.invalid, children: [_jsxs(Checkbox.Root, { checked: o.field.value, onCheckedChange: (details) => {
198
+ if (details.checked === 'indeterminate')
199
+ return;
200
+ o.field.onChange(details.checked);
201
+ globalStore.termsAndConditions.setAccepted(details.checked);
202
+ globalStore.responsibleGaming.setAccepted(details.checked);
203
+ }, children: [_jsx(Checkbox.Control, { className: "border-none bg-white", children: _jsx(Checkbox.Indicator, { asChild: true, className: "bg-white", children: _jsx(CheckIcon, {}) }) }), _jsxs(Checkbox.Label, { className: "text-white/60 text-xs", children: ["I confirm that I am at least 21 years old, not listed in the PAGCOR NDRP, not a GEL license holder, government employee, or member of the Armed Forces of the Philippines. I am not playing in a public space and agree to", ' ', _jsx("button", { type: "button", className: "font-semibold text-[#FFE595]", onClick: () => {
204
+ if (signUpProps.termsAndConditionsUrl) {
205
+ globalStore.signUp.setOpen(false);
206
+ router.push(signUpProps.termsAndConditionsUrl);
207
+ }
208
+ else {
209
+ globalStore.termsAndConditions.setOpen(true);
210
+ globalStore.termsAndConditions.setNext('SIGN_UP');
211
+ globalStore.signUp.setOpen(false);
212
+ }
213
+ }, children: "Terms of Use" }), ' ', "and", ' ', _jsx("button", { type: "button", className: "font-semibold text-[#FFE595]", onClick: () => {
214
+ if (signUpProps.responsibleGamingUrl) {
215
+ globalStore.signUp.setOpen(false);
216
+ router.push(signUpProps.responsibleGamingUrl);
217
+ }
218
+ else {
219
+ globalStore.responsibleGaming.setOpen(true);
220
+ globalStore.responsibleGaming.setNext('SIGN_UP');
221
+ globalStore.signUp.setOpen(false);
222
+ }
223
+ }, children: "Privacy Policy" }), ' ', ". Ineligible accounts will have their funds forfeited to the government."] }), _jsx(Checkbox.HiddenInput, {})] }), _jsx(Field.ErrorText, { className: "ml-6 text-[#ff2525b3] text-xs", children: o.fieldState.error?.message })] })) }), _jsxs("div", { className: "mx-auto mt-3 max-w-[18.75rem] text-2xs text-white/35", children: ["This site is protected by reCAPTCHA and the Google", ' ', _jsx("a", { href: "https://policies.google.com/privacy", target: "_blank", rel: "noreferrer noopener", className: "font-semibold underline underline-offset-2", children: "Privacy Policy" }), ' ', "and", ' ', _jsx("a", { href: "https://policies.google.com/terms", target: "_blank", rel: "noreferrer noopener", className: "font-semibold underline underline-offset-2", children: "Terms of Service" }), ' ', "apply."] })] })] }));
224
+ }
225
+ const DEVICE_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;
226
+ function DateOfBirthField(props) {
227
+ const [value, setValue] = useControllableState({
228
+ value: props.value,
229
+ defaultValue: props.defaultValue ?? null,
230
+ onChange: props.onChange,
231
+ });
232
+ // Custom input handler to automatically add slashes
233
+ const handleInput = (e) => {
234
+ const input = e.currentTarget;
235
+ const oldValue = input.value;
236
+ const oldCursorPosition = input.selectionStart ?? 0;
237
+ // Get digits only from the current input value string
238
+ const digitsOnly = input.value.replace(/\D/g, '');
239
+ let formattedValue = '';
240
+ // Format MM/DD/YYYY
241
+ if (digitsOnly.length > 0) {
242
+ formattedValue += digitsOnly.substring(0, 2);
243
+ if (digitsOnly.length > 2) {
244
+ formattedValue += `/${digitsOnly.substring(2, 4)}`;
245
+ }
246
+ if (digitsOnly.length > 4) {
247
+ formattedValue += `/${digitsOnly.substring(4, 8)}`;
248
+ }
249
+ }
250
+ formattedValue = formattedValue.substring(0, 10); // Limit length
251
+ if (formattedValue !== oldValue) {
252
+ input.value = formattedValue;
253
+ let newCursorPosition = oldCursorPosition;
254
+ // Calculate the number of slashes before the old cursor position in the old value
255
+ const oldSlashesBeforeCursor = (oldValue.substring(0, oldCursorPosition).match(/\//g) || []).length;
256
+ // Calculate the number of slashes before the new cursor position in the new formatted value
257
+ // We take the substring of the new formatted value up to where the cursor *would* be based on the old digits
258
+ const digitsBeforeOldCursor = oldValue
259
+ .substring(0, oldCursorPosition)
260
+ .replace(/\D/g, '').length;
261
+ const newSlashesBeforeCursor = (formattedValue
262
+ .substring(0, digitsBeforeOldCursor + oldSlashesBeforeCursor)
263
+ .match(/\//g) || []).length;
264
+ // Adjust newCursorPosition based on the difference in slash count
265
+ newCursorPosition += newSlashesBeforeCursor - oldSlashesBeforeCursor;
266
+ // Ensure cursor stays within bounds
267
+ newCursorPosition = Math.min(newCursorPosition, formattedValue.length);
268
+ newCursorPosition = Math.max(0, newCursorPosition);
269
+ setTimeout(() => {
270
+ input.setSelectionRange(newCursorPosition, newCursorPosition);
271
+ }, 0);
272
+ }
273
+ };
274
+ return (_jsxs(DatePicker.Root, { size: "md", variant: "outline", placeholder: "mm/dd/yyyy", lazyMount: true, unmountOnExit: true, fixedWeeks: true, selectionMode: "single", value: value ? [parseDate(value)] : [], onValueChange: (details) => {
275
+ const value = details.value.at(0)?.toDate(DEVICE_TIMEZONE);
276
+ setValue(value ?? null);
277
+ }, children: [_jsx(DatePicker.Label, { className: "text-xs", children: "Date of Birth" }), _jsxs(DatePicker.Control, { className: "relative flex h-10 items-center rounded-full border-none bg-[#05121c] text-xs placeholder:text-text-placeholder focus:outline-none focus:ring-0", children: [_jsx(Image, { src: calendarIcon, alt: "calendar icon", width: 20, height: 20, className: "-translate-y-1/2 pointer-events-none absolute top-1/2 left-3 h-5 w-5" }), _jsx(DatePicker.Input, { className: "pl-10", onBlur: props.onBlur, onFocus: props.onFocus, onInput: handleInput }), _jsx(DatePicker.Trigger, { children: _jsx(CalendarIcon, {}) })] }), _jsx(DatePicker.Positioner, { children: _jsxs(DatePicker.Content, { children: [_jsx(DatePicker.View, { view: "day", children: _jsx(DatePicker.Context, { children: (api) => (_jsxs(_Fragment, { children: [_jsxs(DatePicker.ViewControl, { children: [_jsx(DatePicker.PrevTrigger, { children: _jsx(ChevronLeftIcon, {}) }), _jsx(DatePicker.ViewTrigger, { children: _jsx(DatePicker.RangeText, {}) }), _jsx(DatePicker.NextTrigger, { children: _jsx(ChevronRightIcon, {}) })] }), _jsxs(DatePicker.Table, { children: [_jsx(DatePicker.TableHead, { children: _jsx(DatePicker.TableRow, { children: api.weekDays.map((weekDay, id) => (_jsx(DatePicker.TableHeader, { children: weekDay.short.substring(0, 2) }, id))) }) }), _jsx(DatePicker.TableBody, { children: api.weeks.map((week, id) => (_jsx(DatePicker.TableRow, { children: week.map((day, id) => (_jsx(DatePicker.TableCell, { value: day, children: _jsx(DatePicker.TableCellTrigger, { children: day.day }) }, id))) }, id))) })] })] })) }) }), _jsx(DatePicker.View, { view: "month", children: _jsx(DatePicker.Context, { children: (datePicker) => (_jsxs(_Fragment, { children: [_jsxs(DatePicker.ViewControl, { children: [_jsx(DatePicker.PrevTrigger, { children: _jsx(ChevronLeftIcon, {}) }), _jsx(DatePicker.ViewTrigger, { children: _jsx(DatePicker.RangeText, {}) }), _jsx(DatePicker.NextTrigger, { children: _jsx(ChevronRightIcon, {}) })] }), _jsx(DatePicker.Table, { children: _jsx(DatePicker.TableBody, { children: datePicker
278
+ .getMonthsGrid({ columns: 4, format: 'short' })
279
+ .map((months, id) => (_jsx(DatePicker.TableRow, { children: months.map((month, id) => (_jsx(DatePicker.TableCell, { value: month.value, children: _jsx(DatePicker.TableCellTrigger, { className: "w-[4.425rem]", children: month.label }) }, id))) }, id))) }) })] })) }) }), _jsx(DatePicker.View, { view: "year", children: _jsx(DatePicker.Context, { children: (datePicker) => (_jsxs(_Fragment, { children: [_jsxs(DatePicker.ViewControl, { children: [_jsx(DatePicker.PrevTrigger, { children: _jsx(ChevronLeftIcon, {}) }), _jsx(DatePicker.ViewTrigger, { children: _jsx(DatePicker.RangeText, {}) }), _jsx(DatePicker.NextTrigger, { children: _jsx(ChevronRightIcon, {}) })] }), _jsx(DatePicker.Table, { children: _jsx(DatePicker.TableBody, { children: datePicker
280
+ .getYearsGrid({ columns: 4 })
281
+ .map((years, id) => (_jsx(DatePicker.TableRow, { children: years.map((year, id) => (_jsx(DatePicker.TableCell, { value: year.value, children: _jsx(DatePicker.TableCellTrigger, { className: "w-[4.425rem]", children: year.label }) }, id))) }, id))) }) })] })) }) })] }) })] }));
282
+ }
@@ -0,0 +1,4 @@
1
+ export declare const CRAZY_WIN_BRANCH_CODES: {
2
+ code: string;
3
+ name: string;
4
+ }[];
@@ -0,0 +1,38 @@
1
+ export const CRAZY_WIN_BRANCH_CODES = [
2
+ {
3
+ code: 'BRCAL',
4
+ name: 'Bingo Republic 2nd floor Primark Center Deparo St., Caloocan City',
5
+ },
6
+ {
7
+ code: 'BRNAG',
8
+ name: 'Bingo Republic 3rd floor Robinsons Mall, Naga City, Camarines Sur',
9
+ },
10
+ {
11
+ code: 'BRANT',
12
+ name: 'Bingo Republic 2nd floor Robinsons Mall, San Vicente,Antique',
13
+ },
14
+ {
15
+ code: 'BRRQC',
16
+ name: '143 Roosevelt Ave., Brgy. Paraiso, San Francisco Del Monte District 1, Quezon City',
17
+ },
18
+ {
19
+ code: 'OT2TURBN',
20
+ name: '2nd Floor Turbina-Morales Business Center, KM. 52, National Highway, Brgy. Turbina, Calamba City',
21
+ },
22
+ {
23
+ code: 'OT2BYPAS',
24
+ name: '2nd Floor Infinity Square Building, Along the Road, Lamesa, Calamba City',
25
+ },
26
+ {
27
+ code: 'OT2STACZ',
28
+ name: 'Units GF & 49 Sunstar Mall, National Road, Brgy. Gatid, Sta. Cruz, Laguna',
29
+ },
30
+ {
31
+ code: 'OECPANG',
32
+ name: 'Xentro Mall Angono, ML Quezon Ave., Brgy. San Pedro Angono, Rizal',
33
+ },
34
+ {
35
+ code: 'OECPLIPA',
36
+ name: 'Lipa Exchange Bldg. Brgy. Mataas na Lupa Lipa City',
37
+ },
38
+ ];
Binary file
@@ -0,0 +1 @@
1
+ export declare function sanitizeGamesSearch(search?: string): string;
@@ -0,0 +1,9 @@
1
+ export function sanitizeGamesSearch(search) {
2
+ if (!search) {
3
+ return search || '';
4
+ }
5
+ // Strip disallowed punctuation before sending to backend search
6
+ const cleaned = search.replace(/[!@#$%^&*()?><}{\][|\\/`:;"',=+\-_]/g, '');
7
+ const normalized = cleaned.trim().replace(/\s+/g, ' ');
8
+ return normalized.length > 0 ? normalized : '';
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opexa/portal-components",
3
- "version": "0.0.869",
3
+ "version": "0.0.870",
4
4
  "exports": {
5
5
  "./ui/*": {
6
6
  "types": "./dist/ui/*/index.d.ts",