@timardex/cluemart-server-shared 1.0.310 → 1.1.0

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/service/promoCode/constants.ts","../../src/service/affiliate/activeAffiliateCodeExists.ts","../../node_modules/@timardex/cluemart-shared/src/utils/mapArrayToOptions.ts","../../node_modules/@timardex/cluemart-shared/src/utils/date.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/relationShareTypes.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/constants.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/joinShareDescriptionSections.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/normalizeShareDescription.ts","../../node_modules/@timardex/cluemart-shared/src/types/auth.ts","../../node_modules/@timardex/cluemart-shared/src/types/global.ts","../../node_modules/@timardex/cluemart-shared/src/types/ad.ts","../../node_modules/@timardex/cluemart-shared/src/types/resourceActivities.ts","../../node_modules/@timardex/cluemart-shared/src/types/post.ts","../../node_modules/@timardex/cluemart-shared/src/types/game/index.ts","../../node_modules/@timardex/cluemart-shared/src/types/game/dailyClue.ts","../../node_modules/@timardex/cluemart-shared/src/types/game/global.ts","../../node_modules/@timardex/cluemart-shared/src/types/affiliate.ts","../../node_modules/@timardex/cluemart-shared/src/utils/dailyClueGame.ts","../../node_modules/@timardex/cluemart-shared/src/utils/utils.ts","../../node_modules/@timardex/cluemart-shared/src/utils/resourceImage.ts","../../node_modules/@timardex/cluemart-shared/src/utils/school.ts","../../node_modules/@timardex/cluemart-shared/src/utils/eventDateStatus.ts","../../node_modules/@timardex/cluemart-shared/src/calendar/eventCalendar.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/clothingAndFashion.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/electronicsAndTechnology.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/foodAndBeverages.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/handmadeAndLocalProducts.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/healthAndWellness.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/homeGardenHousehold.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/petProductsAndAnimalGoods.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/serviceAndExperience.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/toysChildren.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/vintageAndAntique.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/index.ts","../../node_modules/@timardex/cluemart-shared/src/eventStallholders/eventStallholderFilters.ts","../../node_modules/@timardex/cluemart-shared/src/vendorEvents/vendorEventFilters.ts","../../node_modules/@timardex/cluemart-shared/src/utils/affiliate.ts","../../node_modules/@timardex/cluemart-shared/src/utils/irdNumber.ts","../../node_modules/@timardex/cluemart-shared/src/utils/toUserFacingErrorMessage.ts","../../src/service/promoCode/normalizePromoCode.ts","../../src/service/saveNotificationsInDb.ts","../../src/service/sendPushNotifications.ts","../../src/service/affiliate/vendorSubscriptionRewards.ts","../../src/service/affiliate/awardAffiliateVendorSubscriptionRewards.ts","../../src/service/affiliate/findAffiliateByPromoCode.ts","../../src/service/affiliate/normalizeAffiliatePromoCodes.ts","../../src/service/database.ts","../../src/service/notifyUsers.ts","../../src/service/updateAdStatus.ts","../../src/service/vendor.ts","../../src/service/objectIdToString.ts","../../src/service/event/updateAllEventDateTimeStatuses.ts","../../src/service/event/findEventOrImportedMarketById.ts","../../src/service/relations.ts"],"sourcesContent":["/** Matches partial unique indexes for promo/affiliate codes on active documents. */\nexport const ACTIVE_NOT_DELETED_FILTER = {\n active: true,\n deletedAt: null,\n} as const;\n","import { AffiliateModel } from \"src/mongoose\";\n\nimport { ACTIVE_NOT_DELETED_FILTER } from \"../promoCode/constants\";\n\n/**\n * Checks whether an affiliate code is already taken by an active, non-deleted affiliate.\n *\n * Used when generating new affiliate codes so collisions are avoided before insert.\n * Matches the partial unique index on `affiliateCode` for active documents.\n *\n * @param affiliateCode - Candidate affiliate promo code to check.\n * @returns `true` when an active, non-deleted affiliate already uses this code.\n */\nexport async function activeAffiliateCodeExists(\n affiliateCode: string,\n): Promise<boolean> {\n const existing = await AffiliateModel.exists({\n ...ACTIVE_NOT_DELETED_FILTER,\n affiliateCode,\n }).exec();\n\n return existing !== null;\n}\n","import { OptionItem } from \"src/types\";\n\n/**\n * Convert an array of strings to an array of objects with label and value properties.\n */\nexport const mapArrayToOptions = (items: string[]): OptionItem[] =>\n items.map((item) => ({\n label: item,\n value: item,\n }));\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isSameOrAfter from \"dayjs/plugin/isSameOrAfter.js\";\nimport timezone from \"dayjs/plugin/timezone.js\";\nimport utc from \"dayjs/plugin/utc.js\";\n\nimport { EnumEventDateStatus } from \"src/enums\";\n\nimport { mapArrayToOptions } from \"./mapArrayToOptions\";\n\nexport const dateFormat = \"DD-MM-YYYY\";\nexport const timeFormat = \"HH:mm\";\n\n// Enable custom format parsing\ndayjs.extend(customParseFormat);\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrAfter);\n\nconst NZ_TZ = \"Pacific/Auckland\";\n\nexport function toNZTime(date?: Date | string) {\n return date ? dayjs(date).tz(NZ_TZ) : dayjs().tz(NZ_TZ);\n}\n\n/** Start of the calendar day in Pacific/Auckland (daily games, streaks, etc.). */\nexport function nzStartOfDay(\n input?: Date | string | number | null,\n): dayjs.Dayjs {\n if (input == null) {\n return dayjs().tz(NZ_TZ).startOf(\"day\");\n }\n return dayjs.tz(input, NZ_TZ).startOf(\"day\");\n}\n\ntype DateFormat = \"date\" | \"time\" | \"datetime\";\n\n/**\n * Format a date string to a more readable format.\n * @param dateStr - the date string\n * @param timeStr - optional time string\n * @param display - 'date' | 'time' | 'datetime'\n * @returns formatted string based on display option\n */\nexport const formatDate = (\n dateStr: string,\n display: DateFormat = \"datetime\",\n timeStr?: string,\n) => {\n // Combine date and time into a single string if time is provided\n const dateTimeStr = timeStr ? `${dateStr} ${timeStr}` : dateStr;\n\n // Parse with formats\n const dateTime = timeStr\n ? dayjs(dateTimeStr, `${dateFormat} ${timeFormat}`)\n : dayjs(dateStr, dateFormat);\n\n // Format parts\n const formattedDate = dateTime.format(\"dddd, D MMMM, YYYY\");\n const formattedTime = dateTime.format(\"h:mm a\");\n\n // Return based on display option\n switch (display) {\n case \"date\":\n return formattedDate;\n case \"time\":\n return formattedTime;\n case \"datetime\":\n return `${formattedDate} at ${formattedTime}`;\n default:\n return formattedDate;\n }\n};\n\nexport const getCurrentAndFutureDates = <\n T extends { startDate: string; startTime: string },\n>(\n dates: T[],\n): T[] => {\n const now = dayjs(); // current date and time\n\n return dates.filter((dateObj) => {\n const dateTime = dayjs(\n `${dateObj.startDate} ${dateObj.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n return dateTime.isSameOrAfter(now);\n });\n};\n\nexport const isFutureDatesBeforeThreshold = (\n date: {\n startDate: string;\n startTime: string;\n },\n minHoursFromNow: number,\n): boolean => {\n const threshold = minHoursFromNow\n ? dayjs().add(minHoursFromNow, \"hour\")\n : dayjs().startOf(\"day\");\n\n const dateTime = dayjs(\n `${date.startDate} ${date.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n\n return dateTime.isSameOrAfter(threshold);\n};\n\nexport const formatTimestamp = (timestamp: string) => {\n const formattedDate = toNZTime(timestamp).format(dateFormat);\n\n return formatDate(formattedDate, \"date\");\n};\n\nexport const isIsoDateString = (value: unknown): value is string => {\n return typeof value === \"string\" && !Number.isNaN(Date.parse(value));\n};\n\n/**\n * Sort an array of date strings by their proximity to the current date.\n * @param dates - The array of date strings to sort.\n * @returns - The sorted array of date strings.\n */\nexport function sortDatesChronologically<\n T extends { startDate: string; startTime: string },\n>(dates: T[]): T[] {\n if (!dates?.length) {\n return [];\n }\n\n return [...dates].sort((a, b) => {\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const dateA = dayjs(`${a.startDate} ${a.startTime}`, dateTimeFormat);\n const dateB = dayjs(`${b.startDate} ${b.startTime}`, dateTimeFormat);\n return dateA.valueOf() - dateB.valueOf(); // chronological order\n });\n}\n\nexport const futureTimePeriods = mapArrayToOptions(\n Object.values(EnumEventDateStatus),\n)\n .filter(\n (period) =>\n period.value !== EnumEventDateStatus.STARTING_SOON &&\n period.value !== EnumEventDateStatus.CANCELED &&\n period.value !== EnumEventDateStatus.RE_SCHEDULED &&\n period.value !== EnumEventDateStatus.STARTED &&\n period.value !== EnumEventDateStatus.ENDED &&\n period.value !== EnumEventDateStatus.INVALID,\n )\n .map((period) => ({\n label: period.value.replaceAll(\"_\", \" \"),\n value: period.value,\n }));\n","/** Path segments for relation share URLs — must match mobile `RelationTitle` values. */\nexport const RELATION_SHARE_INVITATION = \"invitation\" as const;\nexport const RELATION_SHARE_APPLICATION = \"application\" as const;\n\n/**\n * Bump when relation overlay SVG/layout changes so og:image URLs and API ETags\n * invalidate after deploy. Facebook caches preview JPEGs by exact URL — changing\n * this value forces crawlers to fetch a fresh image after server deploy.\n */\nexport const RELATION_OVERLAY_LAYOUT_VERSION = 43;\n\nexport const RELATION_SHARE_RESOURCE_TYPES = [\n RELATION_SHARE_INVITATION,\n RELATION_SHARE_APPLICATION,\n] as const;\n\nexport type RelationShareResourceType =\n (typeof RELATION_SHARE_RESOURCE_TYPES)[number];\n\nexport function isRelationShareResourceType(\n resourceType: string,\n): resourceType is RelationShareResourceType {\n return (RELATION_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\nexport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n RELATION_OVERLAY_LAYOUT_VERSION,\n RELATION_SHARE_RESOURCE_TYPES,\n isRelationShareResourceType,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_WIDTH = 1200;\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_HEIGHT = 630;\n\n/** Keep in sync with `CLUEMART_MAIN_DOMAIN_URL` in utils (avoid utils↔sharing import cycle). */\nexport const SHARE_SITE_URL = \"https://cluemart.co.nz\";\n\n/** Calendar marker for invitation market-dates share copy (U+1F4C5 📅). */\nexport const SHARE_CALENDAR_ICON = \"\\u{1F4C5}\";\n\nexport const SHARE_MARKET_DATES_SECTION_HEADING = `${SHARE_CALENDAR_ICON} Market dates:`;\n\n/** Check mark for invitation requirements share copy (U+2705 ✅). */\nexport const SHARE_CHECKMARK_ICON = \"\\u{2705}\";\n\nexport const SHARE_REQUIREMENTS_SECTION_HEADING = `${SHARE_CHECKMARK_ICON} Requirements:`;\n\n/** Information marker for tags share copy (U+2139 ℹ️). */\nexport const SHARE_INFO_ICON = \"\\u{2139}\\u{FE0F}\";\n\nexport const SHARE_TAGS_SECTION_HEADING = `${SHARE_INFO_ICON} Tags:`;\n\n/** Standalone line when `rainOrShine` is true (invitation share + OG section parsing). */\nexport const SHARE_RAIN_OR_SHINE_LINE = \"Rain or Shine\";\n\n/** Standalone line when `foodTruck` is true (application share + OG section parsing). */\nexport const SHARE_FOOD_TRUCK_LINE = \"Food Truck\";\n\n/** Prefix for application compliance line (share text + OG section parsing). */\nexport const SHARE_COMPLIANCE_PREFIX = `${SHARE_CHECKMARK_ICON} Compliance:`;\n\n/** Label tag for application categories share copy (U+1F3F7 🏷️). */\nexport const SHARE_CATEGORY_ICON = \"\\u{1F3F7}\\u{FE0F}\";\n\nexport const SHARE_CATEGORIES_SECTION_HEADING = `${SHARE_CATEGORY_ICON} Categories:`;\n\n/** Straight ruler for application stall-size share copy (U+1F4CF 📏). */\nexport const SHARE_RULER_ICON = \"\\u{1F4CF}\";\n\n/** Prefix for application stall-size line (share text + OG section parsing). */\nexport const SHARE_STALL_SIZE_PREFIX = `${SHARE_RULER_ICON} Stall size:`;\n\n/** Dollar marker for price-range share copy (U+0024 $ — text, not emoji, so it stays black). */\nexport const SHARE_DOLLAR_ICON = \"$\";\n\n/** Prefix for application price-range line (share text + OG section parsing). */\nexport const SHARE_PRICE_RANGE_PREFIX = `${SHARE_DOLLAR_ICON} Price range:`;\n\n/** Alarm clock for invitation application-deadline share copy (U+23F0 ⏰). */\nexport const SHARE_CLOCK_ICON = \"\\u{23F0}\";\n\n/** Prefix for the invitation application-deadline sentence (share text + OG section parsing). */\nexport const SHARE_APPLICATION_DEADLINE_PREFIX = `${SHARE_CLOCK_ICON} Application deadline:`;\n\n/** First line on share sheet messages, Facebook SDK quote, and `og:description`. */\nexport const SHARE_MORE_INFO_LINE = \"Shared from ClueMart\";\n\nexport const DEFAULT_SHARE_OG_IMAGE = `${SHARE_SITE_URL}/assets/logo.webp`;\n\nexport const RESOURCE_SHARE_TYPES = [\n \"market\",\n \"stallholder\",\n \"partner\",\n \"affiliate\",\n \"school\",\n] as const;\n\nexport type ResourceShareType = (typeof RESOURCE_SHARE_TYPES)[number];\n\nexport const POST_SHARE_RESOURCE_TYPES = [\n \"market_faces\",\n \"clue_bites\",\n \"play_and_win\",\n] as const;\n\nexport type PostShareResourceType = (typeof POST_SHARE_RESOURCE_TYPES)[number];\n\nexport type ShareResourceType =\n | ResourceShareType\n | PostShareResourceType\n | RelationShareResourceType;\n\nexport const SHARE_RESOURCE_LABEL: Record<ShareResourceType, string> = {\n [RELATION_SHARE_APPLICATION]: \"Application\",\n [RELATION_SHARE_INVITATION]: \"Invitation\",\n clue_bites: \"Clue Bites\",\n market_faces: \"Market Faces\",\n play_and_win: \"Play & Win\",\n market: \"Market\",\n partner: \"Partner\",\n stallholder: \"Stallholder\",\n affiliate: \"Affiliate\",\n school: \"School\",\n};\n\nexport function isPostShareResourceType(\n resourceType: ShareResourceType,\n): resourceType is PostShareResourceType {\n return (POST_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import { SHARE_MORE_INFO_LINE } from \"./constants\";\n\n/** Blank line between share sections — title, address, dates, URL, etc. */\nexport const SHARE_DESCRIPTION_SECTION_BREAK = \"\\n\\n\";\n\n/**\n * Line break for `og:description` meta content. Use instead of `\\n` — Next.js\n * HTML serialisation collapses literal newlines to spaces; Facebook reads `&#10;`.\n */\nexport const OG_DESCRIPTION_LINE_BREAK = \"&#10;\";\n\n/** Joins OG description sections for Facebook link cards (and Twitter). */\nexport function joinShareOgDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .map((section) => (typeof section === \"string\" ? section.trim() : \"\"))\n .filter((section): section is string => section.length > 0)\n .map((section) => section.replace(/\\n/g, OG_DESCRIPTION_LINE_BREAK))\n .join(OG_DESCRIPTION_LINE_BREAK);\n}\n\nexport function joinShareDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .filter((section): section is string => Boolean(section))\n .join(SHARE_DESCRIPTION_SECTION_BREAK);\n}\n\n/** Prepends the standard share branding line when assembling display text. */\nexport function appendShareMoreInfoLine(description: string): string {\n const trimmed = description.trim();\n if (trimmed.startsWith(SHARE_MORE_INFO_LINE)) {\n return trimmed;\n }\n if (trimmed.length === 0) {\n return SHARE_MORE_INFO_LINE;\n }\n return joinShareDescriptionSections([SHARE_MORE_INFO_LINE, trimmed]);\n}\n","import { SHARE_MORE_INFO_LINE, type ShareResourceType } from \"./constants\";\nimport {\n joinShareOgDescriptionSections,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\nexport {\n joinShareOgDescriptionSections,\n OG_DESCRIPTION_LINE_BREAK,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\n/** Trims share text and collapses spaces per line; preserves `\\n\\n` section breaks. */\nconst LEADING_WHITESPACE = /^[ \\t]*/;\n\nexport function normalizeShareText(value: string | null | undefined): string {\n if (value == null) {\n return \"\";\n }\n return value\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const leading = LEADING_WHITESPACE.exec(line)?.[0] ?? \"\";\n const rest = line\n .slice(leading.length)\n .trim()\n .replace(/[ \\t]{2,}/g, \" \");\n return leading + rest;\n })\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\n/** Branding line placement — first so Facebook truncation does not drop it. */\nexport type ShareMessageFooterPlacement = \"before-body\";\n\nexport function shareMessageFooterPlacementForType(\n _shareType?: ShareResourceType,\n): ShareMessageFooterPlacement {\n return \"before-body\";\n}\n\n/**\n * Ordered sections for share-sheet / Facebook SDK quote text:\n * {@link SHARE_MORE_INFO_LINE} → title → description (full body or post caption).\n */\nexport function buildShareMessageSections(input: {\n title?: string | null;\n description?: string | null;\n shareType?: ShareResourceType;\n}): string[] {\n const title = normalizeShareText(input.title);\n const description = normalizeShareText(input.description);\n\n return [SHARE_MORE_INFO_LINE, title, description].filter(\n (section) => section.length > 0,\n );\n}\n\nexport function splitShareDescriptionSections(value: string): string[] {\n const trimmed = normalizeShareText(value);\n if (!trimmed) {\n return [];\n }\n\n if (/\\n\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n if (/\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n return [trimmed];\n}\n\n/**\n * Formats share descriptions for Open Graph / Twitter meta tags.\n * Sections are joined with {@link OG_DESCRIPTION_LINE_BREAK} (`&#10;`).\n */\nexport function normalizeShareOgDescription(value: string): string {\n return joinShareOgDescriptionSections(splitShareDescriptionSections(value));\n}\n\n/**\n * Quote text for Facebook ShareDialog on iOS (`ShareLinkContent.quote`).\n *\n * Title + blank lines + body sections. URL is omitted — `contentUrl` supplies\n * the link card (OG on resource landing pages controls card title/image).\n */\nexport function buildFacebookShareQuote(\n title: string,\n description: string,\n shareType?: ShareResourceType,\n): string | undefined {\n const sections = buildShareMessageSections({ description, shareType, title });\n const quote = sections.join(SHARE_DESCRIPTION_SECTION_BREAK).trim();\n return quote.length > 0 ? quote : undefined;\n}\n\n/**\n * Open Graph / Twitter description from pre-built section strings.\n * Prefer {@link joinShareOgDescriptionSections} when sections are already known.\n */\nexport function buildShareOgDescriptionFromSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return joinShareOgDescriptionSections(sections);\n}\n\n/** Open Graph / Twitter description — body sections only (`og:title` is separate). */\nexport function buildShareOgDescription(\n _title: string,\n description: string,\n): string {\n return joinShareOgDescriptionSections(\n splitShareDescriptionSections(description),\n );\n}\n\n/**\n * Strips emoji and symbols that do not render reliably in overlay SVG fonts.\n * Keeps letters, numbers, whitespace, and common sentence punctuation.\n */\nexport function stripOverlaySubtitleSpecialChars(text: string): string {\n const regex = /[^\\p{L}\\p{M}\\p{N}\\s.,!?'\"&\\-():;/]/gu;\n const result = text\n .replace(/\\p{Extended_Pictographic}/gu, \"\")\n .replace(regex, \"\");\n return result.trim();\n}\n","import {\n CreateFormData,\n EnumOSPlatform,\n PromoCodeType,\n TermsAgreement,\n} from \"src\";\n\nimport { SafeUserType } from \"./user\";\n\nexport enum EnumVerificationType {\n REGISTER = \"register\",\n RESET_PASSWORD = \"resetPassword\",\n}\n\nexport type LoginFormData = {\n email: string;\n isAdminPage?: boolean;\n password: string;\n platform?: EnumOSPlatform;\n};\n\nexport type CreateLoginFormData = CreateFormData<LoginFormData>;\n\nexport type RegisterFormData = {\n email: string;\n firstName: string;\n lastName: string;\n password: string;\n platform?: EnumOSPlatform;\n preferredRegion: string;\n promoCode?: PromoCodeType | null;\n termsAgreement?: TermsAgreement | null;\n};\n\nexport type CreateRegisterFormData = CreateFormData<RegisterFormData>;\n\nexport type RequestPasswordResetFormData = {\n email: string;\n};\n\nexport type CreateRequestPasswordResetFormData =\n CreateFormData<RequestPasswordResetFormData>;\n\nexport type ResetPasswordFormData = {\n confirmPassword: string;\n email: string;\n password: string;\n};\n\nexport type CreateResetPasswordFormData = CreateFormData<ResetPasswordFormData>;\n\nexport type ValidateVerificationTokenFormData = {\n email: string;\n verificationToken: string;\n verificationType?: EnumVerificationType;\n};\n\nexport type CreateValidateVerificationTokenFormData =\n CreateFormData<ValidateVerificationTokenFormData>;\n\nexport type AuthPayloadType = {\n message: string;\n token: string;\n refreshToken: string | null;\n user: SafeUserType;\n};\n\nexport type RefreshTokenPayloadType = {\n refreshToken: string;\n token: string;\n};\n","import {\n Control,\n FieldValues,\n FormState,\n UseFormGetValues,\n UseFormHandleSubmit,\n UseFormReset,\n UseFormSetValue,\n UseFormWatch,\n} from \"react-hook-form\";\n\nimport {\n EnumEventDateStatus,\n EnumEventType,\n EnumResourceType,\n EnumSocialMedia,\n EnumUserLicence,\n} from \"../enums\";\n\nimport { EventListItemType } from \"./event\";\nimport { EnumPostType } from \"./post\";\nimport { VendorType } from \"./vendor\";\n\nexport const PROMO_CODE_PREFIX = \"CM-\";\n\nexport type PromoCodeType = `${typeof PROMO_CODE_PREFIX}${string}`;\n\nexport type Nullable<T> = {\n [K in keyof T]: T[K] | null | undefined;\n};\n\nexport type DeviceInfo = {\n appBuildNumber: string;\n appId: string;\n appVersion: string;\n brand: string;\n deviceName: string;\n installationId: string;\n manufacturer: string;\n modelName: string;\n osName: string;\n osVersion: string;\n timestamp: string;\n};\n\nexport type TermsAgreement = DeviceInfo & {\n termVersion: string;\n};\n\nexport type ResourceContactDetailsType = {\n email?: string | null;\n landlinePhone?: string | null;\n mobilePhone?: string | null;\n};\n\nexport type ResourceImageType = {\n active: boolean;\n source: string;\n title: string;\n};\n\nexport type SocialMediaType = {\n name?: EnumSocialMedia;\n link?: string;\n};\n\nexport type UserLicenceType = {\n expiryDate: Date;\n issuedDate: Date;\n licenceType: EnumUserLicence;\n prevLicenceType?: EnumUserLicence | null;\n};\n\nexport type OwnerType = {\n email: string;\n userId: string;\n};\n\nexport interface BaseResourceTypeFormData {\n _id?: string;\n active: boolean;\n contactDetails: ResourceContactDetailsType | null;\n cover: ResourceImageType;\n coverUpload?: ResourceImageType | null;\n description: string;\n images?: ResourceImageType[] | null;\n imagesUpload?: ResourceImageType[] | null;\n logo?: ResourceImageType | null;\n logoUpload?: ResourceImageType | null;\n name: string;\n promoCodes?: PromoCodeType[] | null;\n region: string;\n socialMedia: SocialMediaType[] | null;\n termsAgreement?: TermsAgreement | null;\n}\n\nexport type PosterUsageType = {\n month: string;\n count: number;\n};\n\nexport type RelatedPostType = {\n postActive: boolean;\n postId: string;\n postSlug: string;\n postType: EnumPostType;\n};\n\nexport type SocialShareResourceType = {\n qrCode: ResourceImageType;\n socialImage: ResourceImageType;\n};\n\nexport type BaseResourceType = Omit<\n BaseResourceTypeFormData,\n \"_id\" | \"coverUpload\" | \"imagesUpload\" | \"logoUpload\"\n> & {\n _id: string;\n adIds?: string[] | null;\n approvedAt?: Date | null;\n createdAt: Date;\n deletedAt: Date | null;\n owner: OwnerType;\n posterUsage?: PosterUsageType | null;\n rating?: number | null;\n relatedPost?: RelatedPostType | null;\n reviewCount?: number | null;\n slug: string;\n updatedAt: Date | null;\n};\n\nexport type LocationGeoType = {\n coordinates: number[]; // [longitude, latitude]\n type: \"Point\"; // Mongoose GeoJSON type\n};\n\nexport type LocationType = {\n city: string;\n country: string;\n fullAddress: string;\n geo: LocationGeoType;\n latitude: number;\n longitude: number;\n region: string;\n};\n\nexport type DateTimeType = {\n dateStatus: EnumEventDateStatus;\n endDate: string;\n endTime: string;\n startDate: string;\n startTime: string;\n};\n\nexport type Region = {\n latitude: number;\n latitudeDelta: number;\n longitude: number;\n longitudeDelta: number;\n};\n\nexport type ResourceDetails = {\n dateTime: DateTimeType[] | null;\n description: string | null;\n eventStatus?: EventStatusType | null;\n location: LocationType | null;\n resourceCover: ResourceImageType | null;\n resourceId: string;\n resourceLogo: ResourceImageType | null;\n resourceName: string;\n resourceType: EnumResourceType;\n};\n\nexport type GeocodeLocation = Pick<LocationType, \"latitude\" | \"longitude\">;\n\nexport type EventStatusType = {\n claimed: boolean;\n eventType: EnumEventType;\n googlePlaceId?: string | null;\n};\n\nexport interface FormField {\n disabled?: boolean;\n helperText?: string;\n isTextArea?: boolean;\n keyboardType?:\n | \"default\"\n | \"email-address\"\n | \"number-pad\"\n | \"url\"\n | \"decimal-pad\"\n | \"phone-pad\";\n name: string;\n placeholder: string;\n required?: boolean;\n secureTextEntry?: boolean;\n}\n\nexport interface FormDateField {\n dateMode: \"date\" | \"time\";\n helperText?: string;\n name: \"endDate\" | \"endTime\" | \"startDate\" | \"startTime\";\n placeholder: string;\n}\n\nexport interface SubcategoryItems {\n id: string;\n name: string;\n description?: string | null;\n}\n\nexport interface Subcategory {\n id: string;\n name: string;\n items?: SubcategoryItems[] | null;\n}\n\nexport interface Category {\n color?: string | null;\n description?: string | null;\n id: string;\n name: string;\n subcategories: Subcategory[];\n}\n\nexport type OptionItem = {\n value: string;\n label: string;\n};\n\nexport type ImageObjectType = {\n uri: string;\n type: string;\n name: string;\n};\n\nexport interface ResourceConnectionsType {\n events: EventListItemType[] | null;\n vendors: VendorType[] | null;\n}\n\nexport interface CreateFormData<T extends FieldValues> {\n control: Control<T, any>;\n fields: T;\n formState: FormState<T>;\n handleSubmit: UseFormHandleSubmit<T, any>;\n reset: UseFormReset<T>;\n setValue: UseFormSetValue<T>;\n watch: UseFormWatch<T>;\n getValues: UseFormGetValues<T>;\n}\n\nexport interface UseGetResourcesByRegionOptions {\n onlyClaimed?: boolean;\n limit?: number;\n offset?: number;\n}\n","import { EnumResourceType } from \"src/enums\";\n\nimport { CreateFormData } from \"./global\";\n\nexport enum EnumAdShowOn {\n EVENTS_PAGE = \"Events_page\",\n FRONT_PAGE = \"Front_page\",\n PARTNERS_PAGE = \"Partners_page\",\n VENDORS_PAGE = \"Vendors_page\",\n}\n\nexport enum EnumAdStatus {\n ACTIVE = \"Active\",\n PAUSED = \"Paused\",\n EXPIRED = \"Expired\",\n}\n\nexport enum EnumAdType {\n SPONSORED = \"Sponsored\",\n FREE = \"Free\",\n}\n\nexport enum EnumAdStyle {\n BLOOM = \"Bloom\",\n RISE = \"Rise\",\n}\n\nexport type AdResource = {\n adDescription: string;\n adImage: string;\n adStyle: EnumAdStyle;\n adTitle: string;\n adType: EnumAdType;\n resourceId: string;\n resourceName: string;\n resourceRegion: string;\n resourceType: EnumResourceType;\n resourceSlug: string;\n};\n\nexport interface AdFormData {\n active: boolean;\n end: Date; // ISO date string\n resource: AdResource;\n showOn: EnumAdShowOn[];\n start?: Date; // ISO date string\n status: EnumAdStatus;\n targetRegion: string[];\n}\n\n// Form state matches the validation schema: all resource fields are present.\n// Use empty strings / explicit enum defaults in `defaultValues` for \"create\" flows.\nexport type AdFormState = AdFormData;\n\nexport type CreateAdFormData = CreateFormData<AdFormData>;\nexport type CreateAdFormState = CreateFormData<AdFormState>;\n\nexport interface AdType extends AdFormData {\n _id: string;\n clicks?: number; // How many times the ad was clicked\n createdAt: Date;\n impressions?: number; // How often the ad was seen\n start: Date; // ISO date string\n updatedAt: Date | null;\n}\n","import {\n EnumEventDateStatus,\n EnumOSPlatform,\n EnumResourceType,\n} from \"src/enums\";\n\nimport { LocationGeoType } from \"./global\";\n\nexport enum EnumActivity {\n FAVORITE = \"FAVORITE\",\n GOING = \"GOING\",\n INTERESTED = \"INTERESTED\",\n PRESENT = \"PRESENT\",\n VIEW = \"VIEW\",\n}\n\nexport type ResourceActivityEntry = {\n activityType: EnumActivity;\n location: LocationGeoType | null;\n dateStatus?: EnumEventDateStatus | null;\n startDate?: string | null;\n startTime?: string | null;\n timestamp: Date;\n userAgent: EnumOSPlatform;\n userId?: string | null;\n};\n\nexport type ResourceActivityType = {\n _id: string;\n resourceType: EnumResourceType;\n resourceId: string;\n activity: ResourceActivityEntry[];\n};\n\nexport type ResourceActivityInputType = {\n resourceId: string;\n resourceType: EnumResourceType;\n activity: Omit<ResourceActivityEntry, \"timestamp\">;\n};\n","import { EnumResourceType } from \"src/enums\";\n\nimport { BaseGameType } from \"./game\";\nimport {\n CreateFormData,\n ResourceImageType,\n SocialShareResourceType,\n} from \"./global\";\n\nexport enum EnumPostType {\n MARKET_FACES = \"market_faces\",\n CLUE_BITES = \"clue_bites\",\n PLAY_AND_WIN = \"play_and_win\",\n}\n\nexport enum EnumPostContentType {\n GAME = \"game\",\n IMAGE = \"image\",\n LIST = \"list\",\n TEXTAREA = \"textarea\",\n VIDEO = \"video\",\n}\n\nexport type PostFileInput = {\n source: File;\n title?: string;\n};\n\nexport type PostContentTextarea = {\n textarea: {\n title?: string;\n data: string;\n };\n};\n\nexport type PostContentImage = {\n images: ResourceImageType[] | null;\n imagesUpload?: PostFileInput[] | null;\n};\n\nexport type PostContentVideo = {\n video: {\n source: string;\n title?: string;\n };\n};\n\nexport type PostContentList = {\n list: {\n title?: string;\n items: {\n text: string;\n }[];\n };\n};\n\nexport type PostContentGame = {\n game: BaseGameType;\n};\n\nexport type PostContentData =\n | PostContentGame\n | PostContentTextarea\n | PostContentImage\n | PostContentVideo\n | PostContentList;\n\nexport type PostContentFormData = {\n contentData?: PostContentData | null;\n contentOrder?: number | null;\n contentType?: EnumPostContentType | null;\n};\n\nexport type PostResource = {\n resourceSlug: string;\n resourceId: string;\n resourceType: EnumResourceType;\n resourceRegion: string;\n};\n\nexport interface PostFormData {\n active: boolean;\n caption: string;\n content: PostContentFormData[];\n cover?: ResourceImageType | null;\n coverUpload?: PostFileInput | null;\n postType: EnumPostType;\n resource?: PostResource | null;\n tags?: string[] | null;\n title: string;\n notifyUsers?: boolean | null;\n}\n\nexport type CreatePostFormData = CreateFormData<PostFormData>;\n\nexport type PostContentType = Omit<PostContentFormData, \"contentData\"> & {\n contentData: Omit<PostContentData, \"imagesUpload\">;\n};\n\nexport type PostType = Omit<\n PostFormData,\n \"content\" | \"coverUpload\" | \"resource\"\n> & {\n _id: string;\n approvedAt?: Date | null;\n content: PostContentType[];\n createdAt: Date;\n deletedAt: Date | null;\n resource?: PostResource | null;\n slug: string;\n sharePublic?: SocialShareResourceType | null;\n updatedAt: Date | null;\n};\n","import { OwnerType } from \"../global\";\n\nimport { DailyClueBaseGame, DailyClueGameData } from \"./dailyClue\";\nimport { EnumGameType, GameDate } from \"./global\";\nimport { PuzzleBaseGame, PuzzleGameData } from \"./puzzleGame\";\n\nexport type BaseGameMap = {\n [EnumGameType.DAILY_CLUE]: DailyClueBaseGame;\n [EnumGameType.MINI_QUIZ]: PuzzleBaseGame;\n [EnumGameType.ODD_ONE_OUT]: PuzzleBaseGame;\n};\n\nexport type BaseGameType = {\n gameType: EnumGameType;\n gameTypeId: string;\n gameTitle: string;\n} & {\n [K in keyof BaseGameMap]?: BaseGameMap[K] | null;\n};\n\nexport enum EnumGameStatus {\n GAME_COMPLETED = \"GAME_COMPLETED\",\n GAME_IN_PROGRESS = \"GAME_IN_PROGRESS\",\n GAME_LEFT = \"GAME_LEFT\",\n GAME_STARTED = \"GAME_STARTED\",\n}\n\nexport type GameHistory = Pick<\n BaseGameType,\n \"gameTitle\" | \"gameType\" | \"gameTypeId\"\n> & {\n createdAt: Date;\n gameDate: GameDate;\n gameStatus: EnumGameStatus;\n /** Per-event delta. Not persisted for overallGamePoints (computed at read time). */\n pointsEarned: number;\n /** Running total for this game instance; computed at read time, not stored in Mongo. */\n overallGamePoints?: number;\n};\n\ntype GameDataMap = {\n [EnumGameType.DAILY_CLUE]: DailyClueGameData;\n [EnumGameType.MINI_QUIZ]: PuzzleGameData;\n [EnumGameType.ODD_ONE_OUT]: PuzzleGameData;\n};\ntype GameDataType = {\n [K in keyof GameDataMap]?: GameDataMap[K] | null;\n};\n\nexport type GameType = Pick<\n BaseGameType,\n \"gameTitle\" | \"gameType\" | \"gameTypeId\"\n> & {\n _id: string;\n active: boolean;\n createdAt: Date;\n gameData: GameDataType;\n gameHistory: GameHistory[] | null;\n updatedAt: Date | null;\n};\n\nexport type GameDocType = {\n _id: string;\n active: boolean;\n createdAt: Date;\n deletedAt: Date | null;\n games: GameType[] | null;\n owner: OwnerType;\n points: number;\n updatedAt: Date | null;\n};\n\nexport type GameLeaderboard = {\n gameHistory: GameHistory[] | null;\n overallPoints: number;\n owner: OwnerType;\n};\n","import { GameDate, GlobalGameData } from \"./global\";\n\nconst OBJECT_ID_PATH_SEGMENT = \"[a-f0-9]{24}\";\nconst OBJECT_ID_PATH_SEGMENT_END = `${OBJECT_ID_PATH_SEGMENT}$`;\n\nexport const gameScreenIdentifierList = [\n {\n clue: \"Where your actions turn into a timeline.\",\n id: \"activities\",\n match: \"/profile/activities\",\n },\n {\n clue: \"Where conversations happen without speaking.\",\n id: \"chat\",\n match: \"/profile/chat\",\n },\n {\n clue: \"The place to redefine who you are.\",\n id: \"edit-profile\",\n match: \"/profile/edit-profile\",\n },\n {\n clue: \"A single moment worth showing up for.\",\n id: \"single-event\",\n match: new RegExp(`^/events/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"What’s happening around you, right now.\",\n id: \"events-near-me\",\n match: \"/events/events-near-me\",\n },\n {\n clue: \"Where events appear as pins on a map.\",\n id: \"events-map\",\n match: \"/events/events-map\",\n },\n {\n clue: \"A collection of events worth attending.\",\n id: \"events\",\n match: \"/events\",\n },\n {\n clue: \"What’s happening in a wider area — not just nearby.\",\n id: \"events-region\",\n match: /^\\/events\\/region\\/[^/]+$/,\n },\n {\n clue: \"Where fun becomes a challenge.\",\n id: \"games\",\n match: \"/games\",\n },\n {\n clue: \"Your starting point for everything.\",\n id: \"home\",\n match: \"/\",\n },\n {\n clue: \"Where the app whispers what you shouldn’t miss.\",\n id: \"notifications\",\n match: \"/notifications\",\n },\n {\n clue: \"Where you fine-tune your experience.\",\n id: \"options\",\n match: \"/options\",\n },\n {\n clue: \"An organisation or creator supporting the community.\",\n id: \"single-partner\",\n match: new RegExp(`^/partners/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Organisations and creators supporting the community.\",\n id: \"partners\",\n match: \"/partners\",\n },\n {\n clue: \"A single published post in full view.\",\n id: \"single-visitor-post\",\n match: new RegExp(`^/visitors/post/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Your identity, on display.\",\n id: \"profile\",\n match: \"/profile\",\n },\n {\n clue: \"One stallholder offering something valuable.\",\n id: \"single-vendor\",\n match: new RegExp(`^/vendors/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Where every stallholder waits under the right category.\",\n id: \"vendors\",\n match: \"/vendors\",\n },\n {\n clue: \"Where you browse articles and posts from around the platform.\",\n id: \"visitors\",\n match: \"/visitors\",\n },\n] as const;\n\nexport type GamePlacement = (typeof gameScreenIdentifierList)[number][\"id\"];\nexport type GamePlacementClue =\n (typeof gameScreenIdentifierList)[number][\"clue\"];\n\nexport type DailyClueBaseGame = {\n gameDate: GameDate;\n gameSolution: string;\n};\n\nexport type DailyClueGameData = GlobalGameData & {\n gameFields: DailyClueBaseGame;\n lastFoundDate: Date | null;\n letterInfo: {\n collected: string[] | null; // The letters the user has found, e.g. [\"C\", \"L\", \"U\"]\n solutionShuffled: string[]; // The letters of the solution, but shuffled, e.g. [\"L\", \"C\", \"U\"]\n todaysClue: GamePlacementClue | null; // The clue for user to find the letter, e.g. related to {todaysPlacement}\n todaysLetter: string | null; // The letter the user has to find today, e.g. \"C\"\n todaysPlacement: GamePlacement | null; // The screen where the user has to find the clue, e.g. \"HomeScreen\"\n };\n};\n","export enum EnumGameType {\n DAILY_CLUE = \"dailyClue\",\n MINI_QUIZ = \"miniQuiz\",\n ODD_ONE_OUT = \"oddOneOut\",\n}\n\nexport type GameDate = {\n startDate: Date;\n endDate: Date;\n};\n\nexport const gameTypeToDisplayName: Record<EnumGameType, string> = {\n [EnumGameType.DAILY_CLUE]: \"Daily Clue\",\n [EnumGameType.MINI_QUIZ]: \"Mini Quiz\",\n [EnumGameType.ODD_ONE_OUT]: \"Odd One Out\",\n};\n\nexport type GlobalGameData = {\n points: number;\n // User has found the clue 3 days in a row, this is incrementing if the user finds the clue and decrements if user misses a day\n streak: number;\n};\n","import { EnumResourceType } from \"src/enums\";\n\nimport {\n CreateFormData,\n LocationType,\n OwnerType,\n PromoCodeType,\n SocialMediaType,\n} from \"./global\";\nimport { UserFormData } from \"./user\";\n\nexport enum EnumAffiliateParticipantType {\n INDIVIDUAL = \"INDIVIDUAL\",\n SOLE_TRADER = \"SOLE_TRADER\",\n COMPANY = \"COMPANY\",\n}\n\nexport type AffiliateContactDetails = {\n mobilePhone: string;\n};\n\nexport type AffiliateBankAccountDetailsType = {\n accountHolderName: string;\n accountNumber: string;\n};\n\nexport type AffiliateUserDefaultFields = Pick<\n UserFormData,\n \"email\" | \"firstName\" | \"lastName\" | \"termsAgreement\"\n>;\n\nexport type AffiliateDetailsType = AffiliateUserDefaultFields & {\n bankAccountDetails: AffiliateBankAccountDetailsType;\n contactDetails: AffiliateContactDetails;\n irdNumber: string;\n location: LocationType;\n participantType: EnumAffiliateParticipantType;\n socialMedia: SocialMediaType[] | null;\n};\n\nexport enum EnumAffiliateRewardType {\n ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS = \"ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS\",\n ACTIVE_VENDOR_BONUS_REWARD = \"ACTIVE_VENDOR_BONUS_REWARD\",\n ACTIVE_VENDOR_PRO_SUBSCRIPTION = \"ACTIVE_VENDOR_PRO_SUBSCRIPTION\",\n ACTIVE_VENDOR_STANDARD_SUBSCRIPTION = \"ACTIVE_VENDOR_STANDARD_SUBSCRIPTION\",\n NEW_EVENT_REGISTRATION = \"NEW_EVENT_REGISTRATION\",\n NEW_VENDOR_REGISTRATION = \"NEW_VENDOR_REGISTRATION\",\n}\n\nexport type AffiliateRewardType = {\n createdAt: Date;\n redeemedAt: Date | null;\n rewardDescription: string;\n rewardType: EnumAffiliateRewardType;\n rewardValue: number;\n};\n\nexport type AffiliateResourceType = {\n resourceActive: boolean;\n resourceDeletedAt: Date | null;\n resourceId: string;\n resourceName: string;\n resourceOwner: OwnerType;\n resourceType: EnumResourceType;\n rewards: AffiliateRewardType[];\n};\n\nexport type RedeemHistoryType = {\n paidAt?: Date | null;\n redeemedAt: Date;\n rewardValue: number;\n};\n\nexport interface AffiliateType {\n _id: string;\n active: boolean;\n approvedAt?: Date | null;\n affiliateBonusRewards?: AffiliateRewardType[] | null;\n affiliateCode: PromoCodeType | null;\n affiliateDetails: AffiliateDetailsType | null;\n affiliateResources: AffiliateResourceType[];\n createdAt: Date;\n deletedAt: Date | null;\n overallPoints: number;\n owner: OwnerType;\n redeemHistory?: RedeemHistoryType[] | null;\n updatedAt: Date | null;\n}\n\nexport type AffiliateFormData = Omit<\n AffiliateDetailsType,\n \"email\" | \"firstName\" | \"lastName\"\n>;\n\nexport type CreateAffiliateFormData = CreateFormData<AffiliateFormData>;\n","import type { Dayjs } from \"dayjs\";\n\nimport {\n DailyClueGameData,\n GamePlacement,\n GamePlacementClue,\n gameScreenIdentifierList,\n} from \"../types/game/dailyClue\";\n\nimport { nzStartOfDay } from \"./date\";\n\nfunction createSeededRng(seed: number) {\n let t = seed >>> 0;\n\n return function random() {\n t += 0x6d2b79f5;\n let x = t;\n\n x = Math.imul(x ^ (x >>> 15), x | 1);\n x ^= x + Math.imul(x ^ (x >>> 7), x | 61);\n\n return ((x ^ (x >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nfunction hashStringToNumber(seed: string): number {\n let hash = 2166136261;\n\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.codePointAt(i) ?? 0;\n hash = Math.imul(hash, 16777619);\n }\n\n return hash >>> 0;\n}\n\n/** Seeded shuffle so all players see the same letter order / placements for a game. */\nexport function seededShuffle<T>(array: readonly T[], seed: string): T[] {\n const rng = createSeededRng(hashStringToNumber(seed));\n const result = [...array];\n\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n\n return result;\n}\n\nfunction getDayIndex(start: Dayjs, today: Dayjs): number {\n return today.diff(start, \"day\");\n}\n\nexport function computeDailyClueState(dailyClue: DailyClueGameData): {\n todaysClue: GamePlacementClue | null;\n todaysLetter: string | null;\n todaysPlacement: GamePlacement | null;\n} | null {\n const { startDate, endDate } = dailyClue.gameFields.gameDate;\n const { solutionShuffled, collected } = dailyClue.letterInfo;\n\n const today = nzStartOfDay();\n const start = nzStartOfDay(startDate);\n const end = nzStartOfDay(endDate);\n\n // Before game starts\n if (today.isBefore(start)) {\n return null;\n }\n\n const shuffledPlacements = seededShuffle(\n gameScreenIdentifierList,\n start.toISOString(),\n );\n\n const index = getDayIndex(start, today);\n\n // After game ends\n if (today.isAfter(end)) {\n return {\n todaysClue: null,\n todaysLetter: null,\n todaysPlacement: null,\n };\n }\n\n // Safety: index must exist in BOTH arrays\n if (\n index < 0 ||\n index >= solutionShuffled.length ||\n index >= shuffledPlacements.length\n ) {\n return null;\n }\n\n const letterToday = solutionShuffled[index];\n const placement = shuffledPlacements[index];\n\n if (!letterToday || !placement) return null;\n\n const alreadyCollectedToday = (collected ?? []).includes(letterToday);\n\n // Already completed today\n if (alreadyCollectedToday) {\n return {\n todaysClue: null,\n todaysLetter: null,\n todaysPlacement: null,\n };\n }\n\n // Active state\n return {\n todaysClue: placement.clue,\n todaysLetter: letterToday,\n todaysPlacement: placement.id,\n };\n}\n","import {\n EnumInviteStatus,\n EnumPaymentMethod,\n EnumRegions,\n EnumReward,\n EnumSocialMedia,\n EnumUserLicence,\n} from \"src/enums\";\nimport { stripOverlaySubtitleSpecialChars } from \"src/sharing/normalizeShareDescription\";\nimport {\n EnumPostType,\n OptionItem,\n PROMO_CODE_PREFIX,\n SocialMediaType,\n} from \"src/types\";\n\nimport { isIsoDateString } from \"./date\";\nimport { mapArrayToOptions } from \"./mapArrayToOptions\";\n\nexport const removeTypename = (obj: any): any => {\n // Preserve Date objects\n if (obj instanceof Date) {\n return obj;\n }\n\n // Preserve File objects (for apollo-upload-client)\n if (obj instanceof File) {\n return obj;\n }\n\n // Preserve ISO date strings\n if (isIsoDateString(obj)) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map(removeTypename);\n }\n\n // Handle plain objects only\n if (obj !== null && typeof obj === \"object\") {\n const { __typename, ...cleanedObj } = obj;\n\n return Object.keys(cleanedObj).reduce((acc: any, key) => {\n acc[key] = removeTypename(cleanedObj[key]);\n return acc;\n }, {});\n }\n\n // Primitives\n return obj;\n};\n\n/**\n * Truncate text to a specified length and append ellipsis if necessary.\n * @param text\n * @param maxLength\n * @returns\n */\nexport const truncateText = (text: string, maxLength: number = 30): string => {\n const result = stripOverlaySubtitleSpecialChars(text);\n return result.length > maxLength\n ? result.substring(0, maxLength) + \"...\"\n : result;\n};\n\nexport const capitalizeFirstLetter = (str: string): string => {\n return str\n .split(\" \")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \");\n};\n\nexport const statusOptions = [\n ...Object.values(EnumInviteStatus)\n .map((status) => ({\n label: status,\n value: status,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)), // Sort the options alphabetically\n];\n\nexport const availableRegionTypes = Object.values(EnumRegions);\nexport const availableRegionOptions: OptionItem[] =\n mapArrayToOptions(availableRegionTypes);\n\nexport const paymentMethodOptions: OptionItem[] = mapArrayToOptions(\n Object.values(EnumPaymentMethod),\n);\n\nexport function normalizeUrl(url: string): string {\n const withProtocol =\n !url.startsWith(\"http://\") && !url.startsWith(\"https://\")\n ? `https://${url}`\n : url;\n return withProtocol.replace(/\\/+$/, \"\");\n}\n\nexport const licenseNiceNames: Record<EnumUserLicence, string> = {\n [EnumUserLicence.PRO_EVENT]: \"Pro Event\",\n [EnumUserLicence.PRO_VENDOR]: \"Pro Stallholder\",\n [EnumUserLicence.STANDARD_EVENT]: \"Standard Event\",\n [EnumUserLicence.STANDARD_VENDOR]: \"Standard Stallholder\",\n [EnumUserLicence.PRO_PLUS_EVENT]: \"Pro+Ads Event\",\n [EnumUserLicence.PRO_PLUS_VENDOR]: \"Pro+Ads Stallholder\",\n [EnumUserLicence.STANDARD_PARTNER]: \"Partner\",\n [EnumUserLicence.STANDARD_AFFILIATE]: \"Affiliate\",\n [EnumUserLicence.STANDARD_SCHOOL]: \"School\",\n};\n\nexport const cluemartSocialMedia: SocialMediaType[] = [\n {\n link: \"https://www.facebook.com/ClueMartApp\",\n name: EnumSocialMedia.FACEBOOK,\n },\n {\n link: \"https://www.instagram.com/cluemart_app\",\n name: EnumSocialMedia.INSTAGRAM,\n },\n {\n link: \"https://www.tiktok.com/@cluemart\",\n name: EnumSocialMedia.TIKTOK,\n },\n {\n link: \"https://www.youtube.com/@ClueMart-App-NZ\",\n name: EnumSocialMedia.YOUTUBE,\n },\n];\n\nexport const IOS_URL = \"https://apps.apple.com/nz/app/cluemart/id6747251008\";\nexport const ANDROID_URL =\n \"https://play.google.com/store/apps/details?id=com.timardex.cluemart\";\n\nexport const DEFAULT_RESOURCES_RETURN_LIMIT = 1000;\nexport const DEFAULT_RESOURCES_RETURN_OFFSET = 0;\nexport const CLUEMART_MAIN_DOMAIN_URL = \"https://cluemart.co.nz\";\n\nexport const rewardNiceNames: Record<\n EnumReward,\n { name: string; points: number }\n> = {\n [EnumReward.MYSTERY_JOY_BOX]: {\n name: \"Mystery Joy Box\",\n points: 250,\n },\n [EnumReward.MYSTERY_SQUEEZE_BOX]: {\n name: \"Mystery Squeeze Box\",\n points: 350,\n },\n [EnumReward.MYSTERY_DELUXE_BOX]: {\n name: \"Mystery Deluxe Box\",\n points: 500,\n },\n};\n\nexport const PostTypeLabels: Record<EnumPostType, string> = {\n [EnumPostType.MARKET_FACES]: \"Market Faces\",\n [EnumPostType.CLUE_BITES]: \"Clue Bites\",\n [EnumPostType.PLAY_AND_WIN]: \"Play & Win\",\n};\n\n/** Hardcoded registration gateway code; only valid at sign-up (not a generated school referral code). */\nexport const CM_SCHOOL_PROMO_CODE = `${PROMO_CODE_PREFIX}SCHOOL`;\n\n/** Hardcoded registration gateway code; only valid at sign-up (not a generated affiliate referral code). */\nexport const CM_AFFILIATE_PROMO_CODE = `${PROMO_CODE_PREFIX}AFFILIATE`;\n\nexport function formatNZBankAccount(input: string): string {\n // Remove all non-digit characters\n const digitsOnly = input.replaceAll(/\\D/g, \"\");\n\n // Build the formatted string step-by-step\n const parts = [];\n if (digitsOnly.length > 0) parts.push(digitsOnly.slice(0, 2)); // bank\n if (digitsOnly.length > 2) parts.push(digitsOnly.slice(2, 6)); // branch\n if (digitsOnly.length > 6) parts.push(digitsOnly.slice(6, 13)); // account\n if (digitsOnly.length > 13) parts.push(digitsOnly.slice(13, 15)); // suffix\n\n return parts.join(\"-\");\n}\n","import { ResourceImageType } from \"../types/global\";\n\n/**\n * Loose input shape for normalization helpers. Callers often pass partial or legacy\n * data (e.g. Mongo docs without `active`, GraphQL input, empty placeholders) before\n * it becomes a strict `ResourceImageType`.\n */\ntype ResourceImageLike = {\n active?: boolean | null;\n source?: string | null;\n title?: string | null;\n};\n\n/**\n * Resolves whether a resource image is active.\n * Only an explicit `active: true` shows the image; missing `active` defaults to false\n * so licence downgrades (e.g. standard vendor gallery limit) are not overridden.\n */\nexport function resolveResourceImageActive(\n image: ResourceImageLike | null | undefined,\n): boolean {\n if (image?.active != null) {\n return image.active;\n }\n\n return false;\n}\n\nexport function normalizeResourceImage(\n image: ResourceImageLike | null | undefined,\n): ResourceImageType {\n return {\n active: resolveResourceImageActive(image),\n source: image?.source ?? \"\",\n title: image?.title ?? \"\",\n };\n}\n\nexport function isResourceImageActive(\n image: ResourceImageLike | null | undefined,\n): boolean {\n return resolveResourceImageActive(image);\n}\n","export const SCHOOL_MIN_STUDENT_COUNT = 300;\nexport const SCHOOL_MAX_STUDENT_COUNT = 0;\n","import { EnumEventDateStatus } from \"../enums\";\nimport type { DateTimeType } from \"../types/global\";\n\nexport const EVENT_DATE_STATUS_PERIOD_MAP: Partial<\n Record<EnumEventDateStatus, EnumEventDateStatus[]>\n> = {\n [EnumEventDateStatus.TODAY]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.TODAY,\n ],\n [EnumEventDateStatus.THIS_WEEK]: [\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.THIS_WEEK,\n EnumEventDateStatus.TODAY,\n EnumEventDateStatus.TOMORROW,\n ],\n};\n\nexport function getAllowedEventDateStatuses(\n period: EnumEventDateStatus,\n): EnumEventDateStatus[] {\n return EVENT_DATE_STATUS_PERIOD_MAP[period] ?? [period];\n}\n\nexport function eventMatchesDateStatusPeriod(\n dateTime: Pick<DateTimeType, \"dateStatus\">[] | null | undefined,\n period: EnumEventDateStatus,\n): boolean {\n const allowedStatuses = getAllowedEventDateStatuses(period);\n\n return (\n dateTime?.some((dt) => allowedStatuses.includes(dt.dateStatus)) ?? false\n );\n}\n\nexport function filterEventsByDateStatusPeriod<\n T extends { dateTime?: Pick<DateTimeType, \"dateStatus\">[] | null },\n>(events: T[], period: EnumEventDateStatus): T[] {\n return events.filter((event) =>\n eventMatchesDateStatusPeriod(event.dateTime, period),\n );\n}\n","import dayjs from \"dayjs\";\n\nimport { EnumEventDateStatus, EnumResourceType } from \"../enums\";\nimport type { DateTimeType, LocationType } from \"../types/global\";\nimport { dateFormat, formatDate } from \"../utils/date\";\n\nexport type CalendarSheetContentData = {\n dateTime: DateTimeType;\n location: LocationType;\n resourceId: string;\n resourceType: EnumResourceType;\n};\n\nexport const KNOWN_EVENT_SCHEDULE_STATUSES = new Set<EnumEventDateStatus>([\n EnumEventDateStatus.STARTED,\n EnumEventDateStatus.STARTING_SOON,\n EnumEventDateStatus.ENDED,\n]);\n\nexport type EventScheduleStatusTone =\n | \"started\"\n | \"startingSoon\"\n | \"ended\"\n | \"default\";\n\nexport type EventScheduleStatusPresentation = {\n status: EnumEventDateStatus;\n statusLabel: string;\n tone: EventScheduleStatusTone;\n};\n\nexport function toCalendarIsoDate(startDate: string): string | null {\n const formatted = dayjs(startDate, dateFormat).format(\"YYYY-MM-DD\");\n\n return dayjs(formatted, \"YYYY-MM-DD\", true).isValid() ? formatted : null;\n}\n\nexport function fromCalendarIsoDate(isoDate: string): string {\n return dayjs(isoDate, \"YYYY-MM-DD\").format(dateFormat);\n}\n\nexport function getEventCalendarIsoDates(\n dateTimes: DateTimeType[] | null | undefined,\n): string[] {\n return (\n dateTimes\n ?.map((dateTime) => toCalendarIsoDate(dateTime.startDate))\n .filter((date): date is string => Boolean(date)) ?? []\n );\n}\n\nexport function findEventDateTimeByIsoDate(\n dateTimes: DateTimeType[] | null | undefined,\n isoDate: string,\n): DateTimeType | null {\n const selected = fromCalendarIsoDate(isoDate);\n\n return dateTimes?.find((dateTime) => dateTime.startDate === selected) ?? null;\n}\n\nexport function buildCalendarSheetContentData(\n dateTimes: DateTimeType[],\n isoDate: string,\n location: LocationType,\n resourceId: string,\n resourceType: EnumResourceType,\n): CalendarSheetContentData | null {\n const dateTime = findEventDateTimeByIsoDate(dateTimes, isoDate);\n\n if (!dateTime) {\n return null;\n }\n\n return {\n dateTime,\n location,\n resourceId,\n resourceType,\n };\n}\n\nexport function getEventScheduleStatusTone(\n status: EnumEventDateStatus,\n): EventScheduleStatusTone {\n switch (status) {\n case EnumEventDateStatus.STARTED:\n return \"started\";\n case EnumEventDateStatus.STARTING_SOON:\n return \"startingSoon\";\n case EnumEventDateStatus.ENDED:\n return \"ended\";\n default:\n return \"default\";\n }\n}\n\nexport function getEventScheduleStatusPresentation(\n dateTime: DateTimeType,\n): EventScheduleStatusPresentation {\n return {\n status: dateTime.dateStatus,\n statusLabel: dateTime.dateStatus.replaceAll(\"_\", \" \").toLowerCase(),\n tone: getEventScheduleStatusTone(dateTime.dateStatus),\n };\n}\n\nexport function getEventScheduleStatusLabel(\n dateTime: DateTimeType,\n onlyShowKnownStatuses: boolean,\n): string | null {\n const { dateStatus: status } = dateTime;\n const isKnownStatus = Boolean(\n status && KNOWN_EVENT_SCHEDULE_STATUSES.has(status),\n );\n\n if (onlyShowKnownStatuses && !isKnownStatus) {\n return null;\n }\n\n return isKnownStatus\n ? status.replaceAll(\"_\", \" \").toLowerCase()\n : `Next event date: ${formatDate(dateTime.startDate, \"date\")}`;\n}\n\nexport function shouldShowEventActionsWithWeather(\n resourceType: EnumResourceType,\n dateTime: DateTimeType,\n location: LocationType | null | undefined,\n): boolean {\n return (\n resourceType === EnumResourceType.EVENT &&\n Boolean(location) &&\n Boolean(dateTime?.dateStatus) &&\n ![\n EnumEventDateStatus.ENDED,\n EnumEventDateStatus.CANCELED,\n EnumEventDateStatus.STARTED,\n ].includes(dateTime.dateStatus)\n );\n}\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const clothingAndFashion: Category[] = [\n {\n id: \"clothing-fashion\",\n name: \"Clothing & Fashion\",\n description:\n \"New, handmade, or upcycled clothing and accessories with a creative twist.\",\n subcategories: [\n {\n id: \"apparel-babywear\",\n name: \"Apparel & Babywear\",\n items: [\n {\n id: \"apparel\",\n name: \"Apparel\",\n description: \"Dresses, t-shirts, jumpers, rompers, sets, kidswear.\",\n },\n {\n id: \"baby-toddler-apparel\",\n name: \"Baby & Toddler Apparel\",\n description:\n \"Handmade baby clothes, soft shoes, bibs, hats, knitted sets.\",\n },\n {\n id: \"upcycled-fashion\",\n name: \"Upcycled Fashion\",\n description:\n \"Reworked garments, patchwork pieces, restyled vintage.\",\n },\n {\n id: \"other-wearable-items\",\n name: \"Other wearable items\",\n description: \"Unique clothing not listed above.\",\n },\n ],\n },\n {\n id: \"fashion-accessories\",\n name: \"Fashion Accessories\",\n items: [\n {\n id: \"accessories\",\n name: \"Accessories\",\n description: \"Scarves, belts, gloves, hats, headbands, caps.\",\n },\n {\n id: \"shoes\",\n name: \"Shoes\",\n description: \"Handmade shoes, baby booties, sandals, slippers.\",\n },\n {\n id: \"bags-wallets\",\n name: \"Bags & Wallets\",\n description:\n \"Leather bags, fabric purses, wallets, backpacks, totes.\",\n },\n {\n id: \"other-accessories\",\n name: \"Other accessories\",\n description: \"Brooches, pins, or hybrid functional items.\",\n },\n ],\n },\n {\n id: \"jewelry-creative-wearables\",\n name: \"Jewelry & Creative Wearables\",\n items: [\n {\n id: \"jewelry\",\n name: \"Jewelry\",\n description: \"Necklaces, earrings, bracelets, rings, anklets.\",\n },\n {\n id: \"other-creative-wearables\",\n name: \"Other creative wearables\",\n description:\n \"Wearable art, statement pieces, bold handmade designs.\",\n },\n ],\n },\n {\n id: \"traditional-cultural-clothing-accessories\",\n name: \"Traditional & Cultural Clothing and Accessories\",\n items: [\n {\n id: \"traditional-clothing-accessories\",\n name: \"Traditional Clothing & Accessories \",\n description:\n \"raditional clothing, jewellery, accessories and footwear from around the world, including both handmade and non-handmade items.\",\n },\n {\n id: \"other-traditional-cultural-items\",\n name: \"Other Traditional & Cultural Items\",\n description:\n \"traditional or culturally inspired wearables and decorative cultural pieces such as plates, table linens and similar items.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const electronicsAndTechnology: Category[] = [\n {\n id: \"electronics-technology\",\n name: \"Electronics & Technology\",\n description:\n \"New, second-hand, or handmade tech and digital items commonly found at markets.\",\n subcategories: [\n {\n id: \"mobile-everyday-tech\",\n name: \"Mobile & Everyday Tech\",\n items: [\n {\n id: \"mobile-phone-accessories\",\n name: \"Mobile & Phone Accessories\",\n description:\n \"Phone cases, holders, screen protectors, charging cables, power banks, grips.\",\n },\n {\n id: \"other-mobile-gadgets\",\n name: \"Other mobile gadgets\",\n description:\n \"Stands, styluses, SIM tools, cleaning kits, wallet accessories.\",\n },\n ],\n },\n {\n id: \"audio-music-creative-tech\",\n name: \"Audio, Music & Creative Tech\",\n items: [\n {\n id: \"audio-music-tech\",\n name: \"Audio & Music Tech\",\n description:\n \"Portable speakers, headphones, mini radios, Bluetooth adapters, sound accessories.\",\n },\n {\n id: \"instruments-music-gear\",\n name: \"Instruments & Music Gear\",\n description:\n \"Small electronic instruments, DIY synth kits, drum pads, loop machines.\",\n },\n {\n id: \"recording-creative-devices\",\n name: \"Recording & Creative Devices\",\n description:\n \"USB microphones, podcast tools, voice recorders, mobile lighting, content tools.\",\n },\n {\n id: \"other-audio-music-items\",\n name: \"Other audio or music-related items\",\n description: \"Musical gadgets or creative gear not listed above.\",\n },\n ],\n },\n {\n id: \"diy-gadgets-secondhand-finds\",\n name: \"DIY, Gadgets & Second-Hand Finds\",\n items: [\n {\n id: \"diy-electronics-tools\",\n name: \"DIY Electronics & Tools\",\n description:\n \"LED lights, USB gadgets, circuit boards, repair kits, tech-themed toys, novelty items.\",\n },\n {\n id: \"other-tech-finds\",\n name: \"Other Tech Finds\",\n description:\n \"Used electronics, smart gadgets, wearable tech, calculators, tablet stands, e-waste upcycles.\",\n },\n {\n id: \"other-technology-innovation-items\",\n name: \"Other technology or innovation-related items\",\n description: \"Anything not covered but clearly tech-driven.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const foodAndBeverages: Category[] = [\n {\n id: \"food-beverages\",\n name: \"Food & Beverages\",\n description:\n \"Fresh produce, drinks, sweet and savoury street food, ready-to-eat meals, and packaged goods.\",\n subcategories: [\n {\n id: \"fresh-food-groceries\",\n name: \"Fresh Food & Groceries\",\n items: [\n {\n id: \"fruits-vegetables\",\n name: \"Fruits & Vegetables\",\n description:\n \"Fresh seasonal fruit, organic vegetables, specialty produce, berries, tropical fruit, heritage varieties.\",\n },\n {\n id: \"meat-seafood\",\n name: \"Meat & Seafood\",\n description:\n \"Butcher cuts, fresh fish, smoked meats, sausages, seafood platters, artisanal jerky.\",\n },\n {\n id: \"dairy-eggs\",\n name: \"Dairy & Eggs\",\n description:\n \"Farm eggs, handmade cheeses, yoghurt, fresh milk, goat’s milk products.\",\n },\n {\n id: \"bakery-pastries\",\n name: \"Bakery & Pastries\",\n description:\n \"Freshly baked bread, sourdough, bagels, croissants, focaccia, traditional baked goods.\",\n },\n {\n id: \"spices-condiments\",\n name: \"Spices & Condiments\",\n description:\n \"Dried herbs, spice blends, specialty salts, hot sauces, infused oils, chutneys.\",\n },\n {\n id: \"packaged-specialty-foods\",\n name: \"Packaged & Specialty Foods\",\n description:\n \"Honey, jams, preserves, pickles, nut butters, sauces, pre-packaged snacks.\",\n },\n {\n id: \"other-fresh-food-items\",\n name: \"Other fresh food items\",\n description:\n \"Items that don’t fit above, e.g. fermented foods, plant-based substitutes.\",\n },\n ],\n },\n {\n id: \"beverages-specialty-drinks\",\n name: \"Beverages & Specialty Drinks\",\n items: [\n {\n id: \"fresh-juices-smoothies\",\n name: \"Fresh Juices & Smoothies\",\n description:\n \"Cold-pressed juices, detox blends, fruit smoothies, tropical mixes.\",\n },\n {\n id: \"coffee-teas\",\n name: \"Coffee & Teas\",\n description:\n \"Espresso, pour-over coffee, herbal teas, matcha, bubble tea, locally blended tea.\",\n },\n {\n id: \"dairy-plant-based-drinks\",\n name: \"Dairy & Plant-Based Drinks\",\n description:\n \"Milkshakes, lassis, almond/oat milk drinks, coconut water.\",\n },\n {\n id: \"alcoholic-beverages\",\n name: \"Alcoholic Beverages\",\n description:\n \"Local wines, craft beer, mead, cider, infused spirits, cocktail kits.\",\n },\n {\n id: \"other-beverages\",\n name: \"Other beverages\",\n description:\n \"Kombucha, kefir, energy drinks, non-alcoholic wines or beers.\",\n },\n ],\n },\n {\n id: \"savoury-prepared-foods-street-food\",\n name: \"Savoury Prepared Foods & Street Food\",\n items: [\n {\n id: \"local-specialties\",\n name: \"Local Specialties\",\n description:\n \"Meat pies, hangi, seafood chowder, fry bread, traditional NZ dishes.\",\n },\n {\n id: \"asian-cuisine\",\n name: \"Asian Cuisine\",\n description:\n \"Sushi, bao buns, dumplings, ramen, satay, spring rolls, fried rice.\",\n },\n {\n id: \"mediterranean-cuisine\",\n name: \"Mediterranean Cuisine\",\n description: \"Falafel, hummus wraps, souvlaki, Greek salad, dolma.\",\n },\n {\n id: \"italian-delights\",\n name: \"Italian Delights\",\n description: \"Pizza, calzone, focaccia, fresh pasta, arancini.\",\n },\n {\n id: \"bbq-grilled-foods\",\n name: \"BBQ & Grilled Foods\",\n description: \"Kebabs, grilled chicken, ribs, burgers, sausages.\",\n },\n {\n id: \"savoury-crepes-pancakes\",\n name: \"Savoury Crepes & Pancakes\",\n description: \"Filled savoury crepes, mini savoury pancakes.\",\n },\n {\n id: \"savoury-baked-goods-pastries\",\n name: \"Savoury Baked Goods & Pastries\",\n description:\n \"Quiches, savoury muffins, filled savoury pastries, empanadas.\",\n },\n {\n id: \"vegan-vegetarian-dishes\",\n name: \"Vegan & Vegetarian Dishes\",\n description:\n \"Buddha bowls, plant-based burgers, salads, vegan sushi.\",\n },\n {\n id: \"other-savoury-foods\",\n name: \"Other Savoury Foods\",\n description: \"Fusion dishes, mixed platters, savoury meal kits.\",\n },\n ],\n },\n {\n id: \"sweet-prepared-foods\",\n name: \"Sweet Prepared Foods\",\n items: [\n {\n id: \"sweet-crepes-pancakes-fried-treats\",\n name: \"Sweet Crepes, Pancakes & Fried Treats\",\n description: \"Crepes, waffles, mini pancakes, muffins, doughnuts.\",\n },\n {\n id: \"sweet-baked-goods-desserts\",\n name: \"Sweet Baked Goods & Desserts\",\n description: \"Cakes, pastries, slices, trifles, layered desserts.\",\n },\n {\n id: \"fruit-based-snacks\",\n name: \"Fruit-Based Snacks\",\n description:\n \"Fruit skewers, dried fruit packs, chocolate-dipped fruit.\",\n },\n {\n id: \"candy-confectionery\",\n name: \"Candy & Confectionery\",\n description: \"Fudge, handmade candies, toffee, nougat, brittle.\",\n },\n {\n id: \"other-sweet-foods\",\n name: \"Other Sweet Foods\",\n description: \"Fusion desserts, sweet platters, sweet meal kits.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const handmadeAndLocalProducts: Category[] = [\n {\n id: \"handmade-local-products\",\n name: \"Handmade & Local Products\",\n description: \"Unique, handmade, or locally produced artisan goods.\",\n subcategories: [\n {\n id: \"home-living\",\n name: \"Home & Living\",\n items: [\n {\n id: \"ceramics-pottery\",\n name: \"Ceramics & Pottery\",\n description:\n \"Handmade mugs, vases, bowls, decorative plates, plant pots.\",\n },\n {\n id: \"candles-home-scents\",\n name: \"Candles & Home Scents\",\n description:\n \"Soy candles, beeswax candles, wax melts, incense sticks, room sprays and herbal sachets.\",\n },\n {\n id: \"botanical-crafts\",\n name: \"Botanical Crafts\",\n description: \"Dried-flower décor and pressed-flower crafts\",\n },\n {\n id: \"textiles-embroidery\",\n name: \"Textiles & Embroidery\",\n description:\n \"Handsewn items, embroidered napkins, home linens, aprons, fabric gift wraps, handmade fabric bags, quilted goods, personalized textile gifts.\",\n },\n {\n id: \"woodcraft-metalcraft\",\n name: \"Woodcraft & Metalcraft\",\n description:\n \"Wooden boards, handmade frames, sculptures, metal signs, furniture.\",\n },\n {\n id: \"handmade-soaps-natural-body-goods\",\n name: \"Handmade Soaps & Natural Body Goods\",\n description: \"Handmade soaps, balms, oils, bath items\",\n },\n {\n id: \"seasonal-festive-crafts\",\n name: \"Seasonal & Festive Crafts\",\n description: \"Christmas, Easter and seasonal handmade decor\",\n },\n {\n id: \"other-home-living-products\",\n name: \"Other home & living products\",\n description:\n \"Items that don't fit above but serve home-related purposes.\",\n },\n ],\n },\n {\n id: \"art-personal-expression\",\n name: \"Art & Personal Expression\",\n items: [\n {\n id: \"paintings-illustrations\",\n name: \"Paintings & Illustrations\",\n description:\n \"Paintings, canvas art, hand-drawn illustrations, digital prints, graphic art and calligraphy pieces.\",\n },\n {\n id: \"sculptures-carvings\",\n name: \"Sculptures & Carvings \",\n description:\n \"Sculptures made from wood, stone, metal, clay or resin, carved decorative pieces and artistic 3D works.\",\n },\n {\n id: \"creative-handmade-alternative-art\",\n name: \"Creative Handmade & Alternative Art\",\n description:\n \"Handmade textile décor, fabric ornaments, mixed-media art, creative craft pieces and modern handmade artworks.\",\n },\n {\n id: \"handmade-mini-figures-decor\",\n name: \"Handmade Mini Figures & Décor\",\n description:\n \"Small handmade figures, tiny houses and crafted mini decorative items.\",\n },\n {\n id: \"other-artistic-expressive-products\",\n name: \"Other Artistic or Expressive Products\",\n description:\n \"Custom artworks, specialty handmade décor, unique crafts.\",\n },\n ],\n },\n {\n id: \"handmade-jewellery-accessories-cultural-crafts\",\n name: \"Handmade Jewellery, Accessories & Cultural Crafts\",\n items: [\n {\n id: \"jewellery-handmade-items-nz-traditional-materials\",\n name: \"Jewellery & Handmade Items from NZ Traditional Materials\",\n description:\n \"Handmade jewellery and decorative or functional items crafted from pounamu, bone, wood and other traditional New Zealand materials.\",\n },\n {\n id: \"maori-pasifika-cultural-crafts-clothing\",\n name: \"Māori & Pasifika Cultural Crafts and Clothing\",\n description:\n \"Culturally inspired handmade items, accessories and garments featuring Māori or Pasifika motifs, traditional patterns and regional craftsmanship.\",\n },\n {\n id: \"traditional-handmade-clothing-jewellery-crafts-global-cultures\",\n name: \"Traditional Handmade Clothing, Jewellery & Crafts from Global Cultures\",\n description:\n \"Handcrafted clothing, jewellery and cultural items from diverse traditions — including Asian, African, European, Middle Eastern, Indian, Chinese, Japanese, Pacific, and other culturally significant handmade pieces.\",\n },\n {\n id: \"other-handmade-cultural-items\",\n name: \"Other Handmade Cultural Items\",\n description:\n \"Unique or culturally inspired handmade pieces not specifically covered in the categories above.\",\n },\n ],\n },\n {\n id: \"gift-ideas-accessories\",\n name: \"Gift Ideas & Accessories\",\n items: [\n {\n id: \"handmade-pens-keychains-fridge-magnets\",\n name: \"Handmade Pens, Keychains and Fridge Magnets\",\n description: null,\n },\n {\n id: \"gift-packaging-wrapping-accessories\",\n name: \"Gift Packaging & Wrapping Accessories\",\n description: null,\n },\n {\n id: \"handmade-crochet-knitting-fibre-crafts\",\n name: \"Handmade Crochet, Knitting & Fibre Crafts\",\n description:\n \"Handmade crochet and knitted items, fabric-based crafts, small decorative pieces, creative fibre artworks and unique handcrafted accessories.\",\n },\n {\n id: \"handmade-toys-mini-play-items\",\n name: \"Handmade Toys & Mini Play Items\",\n description:\n \"small handmade toys, soft toys, wooden miniatures, crochet or felt play pieces\",\n },\n {\n id: \"other-small-handmade-gifts-accessories\",\n name: \"Other small handmade gifts or accessories\",\n description: \"Compact creative items made to surprise or delight.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const healthAndWellness: Category[] = [\n {\n id: \"health-wellness\",\n name: \"Health & Wellness\",\n description:\n \"Natural products and services that promote wellbeing, body care, and holistic health.\",\n subcategories: [\n {\n id: \"body-skincare\",\n name: \"Body & Skincare\",\n items: [\n {\n id: \"skincare-body-products\",\n name: \"Skincare & Body Products\",\n description:\n \"Soaps, creams, lip balms, bath salts, bath bombs, body oils, natural deodorants.\",\n },\n {\n id: \"other-body-care-items\",\n name: \"Other body care items\",\n description:\n \"Additional handmade or eco-conscious personal care goods.\",\n },\n ],\n },\n {\n id: \"aromatherapy-herbal-wellness\",\n name: \"Aromatherapy & Herbal Wellness\",\n items: [\n {\n id: \"aromatherapy-herbal-remedies\",\n name: \"Aromatherapy & Herbal Remedies\",\n description:\n \"Essential oils, herbal balms, massage oils, salves, natural teas, rollers.\",\n },\n {\n id: \"other-herbal-aroma-products\",\n name: \"Other herbal or aroma-based products\",\n description: \"Wellness blends, herb sachets, custom infusions.\",\n },\n ],\n },\n {\n id: \"wellness-tools-accessories\",\n name: \"Wellness Tools & Accessories\",\n items: [\n {\n id: \"wellness-accessories\",\n name: \"Wellness Accessories\",\n description:\n \"Yoga mats, meditation cushions, eye pillows, incense, smudging sticks, eco water bottles, wellness journals.\",\n },\n {\n id: \"spiritual-tools-crystals\",\n name: \"Spiritual Tools & Crystals\",\n description:\n \"Healing crystals, gemstone bracelets, pendulums, sprays, spiritual kits, altar decor.\",\n },\n {\n id: \"other-wellness-spiritual-items\",\n name: \"Other wellness or spiritual items\",\n description: \"Items that aid relaxation, focus, or inner work.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const homeGardenHousehold: Category[] = [\n {\n id: \"home-garden-household-goods\",\n name: \"Home, Garden & Household Goods\",\n description:\n \"Functional, decorative, and eco-conscious products designed for everyday use indoors and outdoors.\",\n subcategories: [\n {\n id: \"home-decor-living\",\n name: \"Home Decor & Living\",\n items: [\n {\n id: \"home-decor\",\n name: \"Home Decor\",\n description:\n \"Cushions, wall art, table runners, vases, trays, mirrors, handmade centerpieces.\",\n },\n {\n id: \"kitchenware-dining\",\n name: \"Kitchenware & Dining\",\n description:\n \"Mugs, bowls, cutting boards, utensils, jars, coasters, kitchen textiles.\",\n },\n {\n id: \"mini-figures-decor\",\n name: \"Mini Figures & Décor\",\n description:\n \"handmade or non-handmade small figures, tiny houses and miniature decorative items.\",\n },\n {\n id: \"other-indoor-home-items\",\n name: \"Other indoor home items\",\n description:\n \"Any decorative or practical household items not listed above.\",\n },\n ],\n },\n {\n id: \"cleaning-eco-essentials\",\n name: \"Cleaning & Eco Essentials\",\n items: [\n {\n id: \"cleaning-eco-supplies\",\n name: \"Cleaning & Eco Supplies\",\n description:\n \"Beeswax wraps, reusable cloths, brushes, natural soaps, detergent bars, eco sponges.\",\n },\n {\n id: \"other-eco-cleaning-items\",\n name: \"Other eco or cleaning items\",\n description: \"Environmentally friendly goods not listed above.\",\n },\n ],\n },\n {\n id: \"garden-outdoor-living\",\n name: \"Garden & Outdoor Living\",\n items: [\n {\n id: \"plants-botanical-decor\",\n name: \"Plants & Botanical Decor\",\n description:\n \"Potted herbs, succulents, dried flowers, terrariums, plant-based ornaments.\",\n },\n {\n id: \"fresh-flowers-botanical-bouquets\",\n name: \"Fresh Flowers & Botanical Bouquets\",\n description:\n \"Cut flowers, seasonal bouquets, simple floral arrangements, native flower selections, and other fresh botanical items.\",\n },\n {\n id: \"natural-decor-nature-inspired-elements\",\n name: \"Natural Decor & Nature-Inspired Elements\",\n description:\n \"Seashell décor, driftwood pieces, sand ornaments, natural wood accents, stone or mineral decorations, and other nature-based decorative items.\",\n },\n {\n id: \"garden-tools-outdoor-items\",\n name: \"Garden Tools & Outdoor Items\",\n description:\n \"Plant markers, garden signs, stakes, small tools, wind chimes, gifts.\",\n },\n {\n id: \"other-outdoor-garden-products\",\n name: \"Other outdoor or garden products\",\n description: \"Functional or decorative items for outside use.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const petProductsAndAnimalGoods: Category[] = [\n {\n id: \"pet-products-animal-goods\",\n name: \"Pet Products & Animal Goods\",\n description: \"Items for pets, pet lovers, or animal-themed market stalls.\",\n subcategories: [\n {\n id: \"products-for-pets\",\n name: \"Products for Pets\",\n items: [\n {\n id: \"pet-food-treats\",\n name: \"Pet Food & Treats\",\n description:\n \"Homemade dog biscuits, cat snacks, natural chews, pet-safe cakes, training treats.\",\n },\n {\n id: \"apparel-toys-accessories\",\n name: \"Apparel, Toys & Accessories\",\n description:\n \"Leashes, collars, harnesses, toys, grooming tools, beds, travel gear, jumpers, bandanas.\",\n },\n {\n id: \"other-pet-products\",\n name: \"Other pet products\",\n description: \"Any pet-related items not listed above.\",\n },\n ],\n },\n {\n id: \"small-pets-birds-exotic-animals\",\n name: \"Small Pets, Birds & Exotic Animals\",\n items: [\n {\n id: \"products-small-pets-birds-exotics\",\n name: \"Products for Small Pets, Birds & Exotics\",\n description:\n \"Toys, enclosures, perches, feeding bowls, bedding, habitat decor, transport gear, and care items for birds, rabbits, hamsters, reptiles, turtles, aquarium pets, and other exotic species.\",\n },\n {\n id: \"other-small-exotic-animal-items\",\n name: \"Other small or exotic animal items\",\n description: \"Unusual accessories for non-mainstream pets.\",\n },\n ],\n },\n {\n id: \"farm-working-animals\",\n name: \"Farm & Working Animals\",\n items: [\n {\n id: \"goods-for-farm-working-animals\",\n name: \"Goods for Farm & Working Animals\",\n description:\n \"Treats, care products, equipment, signage and accessories for chickens, goats, alpacas, horses, and other livestock.\",\n },\n {\n id: \"other-farm-animal-items\",\n name: \"Other farm animal-related items\",\n description:\n \"Rural, barnyard, or utility-specific gear not listed above.\",\n },\n ],\n },\n {\n id: \"animal-themed-gifts-custom-items\",\n name: \"Animal-Themed Gifts & Custom Items\",\n items: [\n {\n id: \"pet-art-custom-gifts\",\n name: \"Pet Art & Custom Gifts\",\n description:\n \"Pet portraits, name tags, personalized bowls, breed-specific items, pet-themed home decor and stationery.\",\n },\n {\n id: \"other-animal-themed-gifts\",\n name: \"Other animal-themed gifts\",\n description:\n \"Artistic or sentimental items made for animal lovers.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"src/types\";\n\n/* eslint-disable sort-keys */\nexport const serviceAndExperience: Category[] = [\n {\n id: \"services-experiences\",\n name: \"Services & Experiences\",\n description:\n \"On-site offerings that provide entertainment, personal care, learning, or interactive activities beyond products.\",\n subcategories: [\n {\n id: \"personal-care-body-art\",\n name: \"Personal Care & Body Art\",\n items: [\n {\n id: \"nails-handcare\",\n name: \"Nails & Handcare\",\n description:\n \"Nail painting, decoration, quick manicures, temporary nail extensions.\",\n },\n {\n id: \"hair-styling-braiding\",\n name: \"Hair Styling & Braiding\",\n description:\n \"Hair braiding, plaits, child-friendly festival hairstyles.\",\n },\n {\n id: \"face-body-decoration\",\n name: \"Face & Body Decoration\",\n description:\n \"Henna, glitter tattoos, face painting, light makeup, eyelash styling, professional tattooing (where permitted).\",\n },\n {\n id: \"other-beauty-grooming-services\",\n name: \"Other beauty or grooming services\",\n description: \"Small-scale personal care options offered on-site.\",\n },\n ],\n },\n {\n id: \"practical-wellness-services\",\n name: \"Practical & Wellness Services\",\n items: [\n {\n id: \"mobile-practical-services\",\n name: \"Mobile & Practical Services\",\n description:\n \"Shoe repair, phone repairs, knife sharpening, key cutting, battery replacement, bike repairs, engraving.\",\n },\n {\n id: \"wellness-alternative-therapies\",\n name: \"Wellness & Alternative Therapies\",\n description:\n \"Massage, aromatherapy, reflexology, energy healing (e.g. Reiki), natural consultations.\",\n },\n {\n id: \"other-service-based-offerings\",\n name: \"Other service-based offerings\",\n description: \"Wellness or functional services not listed above.\",\n },\n ],\n },\n {\n id: \"creative-educational-experiences\",\n name: \"Creative & Educational Experiences\",\n items: [\n {\n id: \"creative-workshops-maker-services\",\n name: \"Creative Workshops & Maker Services\",\n description:\n \"Candle making, pottery, jewelry crafting, soap or balm workshops, calligraphy, seasonal crafts.\",\n },\n {\n id: \"education-awareness-stalls\",\n name: \"Education & Awareness Stalls\",\n description:\n \"Eco awareness, cultural storytelling, local history, first aid demos, health booths, sustainability education, kids’ science displays.\",\n },\n {\n id: \"other-creative-educational-services\",\n name: \"Other creative or educational services\",\n description:\n \"Informal learning, demonstrations, or community-focused sessions.\",\n },\n ],\n },\n {\n id: \"kids-activities-family-fun\",\n name: \"Kids’ Activities & Family Fun\",\n items: [\n {\n id: \"kids-activities-fun\",\n name: \"Kids’ Activities & Fun\",\n description:\n \"Face painting, glitter tattoos, pony rides, bouncy castles, small amusement rides, balloon twisting, animal petting zones.\",\n },\n {\n id: \"other-family-oriented-activities\",\n name: \"Other family-oriented activities\",\n description:\n \"On-site entertainment that engages children or family groups.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"src/types\";\n\n/* eslint-disable sort-keys */\nexport const toysChildren: Category[] = [\n {\n id: \"toys-childrens-items\",\n name: \"Toys & Children’s Items\",\n description: \"Products and services made for or inspired by children.\",\n subcategories: [\n {\n id: \"toys-playthings\",\n name: \"Toys & Playthings\",\n items: [\n {\n id: \"toys-classic-electric-character\",\n name: \"Toys – Classic, Electric & Character-Based\",\n description:\n \"Building blocks, dolls, puzzles, plush animals, toy vehicles, remote-control toys, light-up gadgets, character figurines, themed playsets.\",\n },\n {\n id: \"handmade-toys-crafty-playthings\",\n name: \"Handmade Toys & Crafty Playthings\",\n description:\n \"Wooden puzzles, crocheted animals, felt toys, fabric dolls, DIY kits, nature-inspired games, sensory toys.\",\n },\n {\n id: \"other-play-items\",\n name: \"Other play items\",\n description:\n \"Toys not listed above, including limited-edition or hybrid items.\",\n },\n ],\n },\n {\n id: \"educational-developmental\",\n name: \"Educational & Developmental\",\n items: [\n {\n id: \"educational-developmental-tools\",\n name: \"Educational & Developmental Tools\",\n description:\n \"STEM kits, Montessori toys, storybooks, picture books, flashcards, early learning games, language tools.\",\n },\n {\n id: \"other-educational-experience-based-items\",\n name: \"Other educational or experience-based items\",\n description:\n \"Creative experiences or learning aids not listed above.\",\n },\n ],\n },\n {\n id: \"baby-kidswear-accessories\",\n name: \"Baby & Kidswear + Accessories\",\n items: [\n {\n id: \"baby-kidswear-accessories\",\n name: \"Baby & Kidswear + Accessories\",\n description:\n \"Handmade baby clothes, toddler outfits, bibs, hats, headbands, bags, pacifier clips, soft shoes.\",\n },\n {\n id: \"baby-developmental-soft-toys\",\n name: \"Baby Developmental Soft Toys\",\n description:\n \"Sensory toys, rattles, fabric books, teething items, high-contrast cards, and early-skill. Montessori materials designed to support infants’ cognitive and motor development.\",\n },\n {\n id: \"other-childrens-clothing-accessories\",\n name: \"Other children’s clothing or accessories\",\n description: \"Unique fashion or functional pieces for kids.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const vintageAndAntique: Category[] = [\n {\n id: \"vintage-antique\",\n name: \"Vintage & Antique\",\n description:\n \"Unique, historic, or nostalgic items with collectible or decorative value.\",\n subcategories: [\n {\n id: \"vintage-antique-clothing-accessories\",\n name: \"Vintage & Antique Clothing & Accessories\",\n items: [\n {\n id: \"clothing-vintage-fashion\",\n name: \"Clothing and wearable items from past eras\",\n description:\n \"Vintage dresses, jackets, hats, gloves, belts, bags, shoes, jewellery.\",\n },\n {\n id: \"other-clothing-accessory-items\",\n name: \"Other clothing-related items\",\n description:\n \"Hair clips, brooches, pins, scarf rings, small fashion accessories.\",\n },\n ],\n },\n {\n id: \"collectibles-memorabilia\",\n name: \"Collectibles & Memorabilia\",\n items: [\n {\n id: \"small-collectible-items\",\n name: \"Small collectible items with historical or nostalgic significance\",\n description:\n \"Coins, stamps, toys, postcards, comics, sports cards, vintage packaging.\",\n },\n {\n id: \"other-collectible-items\",\n name: \"Other collectible items\",\n description:\n \"Rare small objects, miniature figurines, special-edition items.\",\n },\n ],\n },\n {\n id: \"homewares-decor-curiosities\",\n name: \"Homewares, Decor & Curiosities\",\n items: [\n {\n id: \"decorative-functional-vintage-items\",\n name: \"Decorative or functional items with a vintage or antique aesthetic\",\n description:\n \"Teacups, plates, vases, mirrors, clocks, furniture, old tools, lanterns, typewriters, curiosities.\",\n },\n {\n id: \"handmade-vintage-art\",\n name: \"Handmade vintage art\",\n description: \"Paintings, sculptures, crafted pieces.\",\n },\n {\n id: \"other-home-decor-items\",\n name: \"Other home or decor items\",\n description:\n \"Decorative pieces not listed above, unique household objects.\",\n },\n ],\n },\n {\n id: \"vintage-media-printed-nostalgia\",\n name: \"Vintage Media & Printed Nostalgia\",\n items: [\n {\n id: \"older-media-printed-works\",\n name: \"Older media formats and printed works\",\n description:\n \"Vinyl records, cassette tapes, CDs, DVDs, books, magazines, board games, posters.\",\n },\n {\n id: \"other-media-printed-items\",\n name: \"Other media or printed items\",\n description: \"Maps, manuals, leaflets, out-of-print materials.\",\n },\n ],\n },\n {\n id: \"other-vintage-antique-items\",\n name: \"Other Vintage & Antique Items\",\n items: [\n {\n id: \"any-vintage-antique-items-not-listed\",\n name: \"Any vintage or antique items not listed above\",\n description: \"Unique, rare or uncategorised pieces.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"../../types/global\";\n\nimport { clothingAndFashion } from \"./clothingAndFashion\";\nimport { electronicsAndTechnology } from \"./electronicsAndTechnology\";\nimport { foodAndBeverages } from \"./foodAndBeverages\";\nimport { handmadeAndLocalProducts } from \"./handmadeAndLocalProducts\";\nimport { healthAndWellness } from \"./healthAndWellness\";\nimport { homeGardenHousehold } from \"./homeGardenHousehold\";\nimport { petProductsAndAnimalGoods } from \"./petProductsAndAnimalGoods\";\nimport { serviceAndExperience } from \"./serviceAndExperience\";\nimport { toysChildren } from \"./toysChildren\";\nimport { vintageAndAntique } from \"./vintageAndAntique\";\n\nexport const categoryColors: Record<string, string> = {\n \"clothing-fashion\": \"#9D4EDD\",\n \"electronics-technology\": \"#3AF3FF\",\n \"food-beverages\": \"#FF0D1F\",\n \"handmade-local-products\": \"#EE7E54\",\n \"health-wellness\": \"#E23794\",\n \"home-garden-household-goods\": \"#067325\",\n \"pet-products-animal-goods\": \"#68E788\",\n \"services-experiences\": \"#2E16A5\",\n \"toys-childrens-items\": \"#FFF966\",\n \"vintage-antique\": \"#8D6748\",\n};\n\nconst assignColorToCategories = (categories: Category[]): Category[] => {\n const result = categories.map((category) => ({\n ...category,\n color: categoryColors[category.id],\n }));\n return result;\n};\n\nexport const availableCategories = assignColorToCategories([\n ...foodAndBeverages,\n ...handmadeAndLocalProducts,\n ...clothingAndFashion,\n ...homeGardenHousehold,\n ...toysChildren,\n ...healthAndWellness,\n ...electronicsAndTechnology,\n ...vintageAndAntique,\n ...petProductsAndAnimalGoods,\n ...serviceAndExperience,\n]);\n","import { availableCategories } from \"../formFields/categories\";\nimport type { DateTimeType } from \"../types/global\";\nimport type { UnregisteredVendorType, VendorType } from \"../types/vendor\";\nimport { sortDatesChronologically } from \"../utils/date\";\n\ntype RelationType = NonNullable<VendorType[\"relations\"]>[number];\ntype RelationDateType = NonNullable<RelationType[\"relationDates\"]>[number];\n\nexport type StallholderFilterOption = {\n label: string;\n value: string;\n};\n\nexport function eventStartDatesSet(\n dateTime: DateTimeType[] | undefined,\n): Set<string> {\n return new Set((dateTime ?? []).map((date) => date.startDate));\n}\n\nexport function hasMatchingEventDate(\n vendorStartDates: string[],\n eventStartDates: Set<string>,\n): boolean {\n return vendorStartDates.some((startDate) => eventStartDates.has(startDate));\n}\n\nexport function matchesSelectedDate(\n vendorStartDates: string[],\n selectedDate: string | null,\n): boolean {\n if (!selectedDate) {\n return true;\n }\n\n return vendorStartDates.includes(selectedDate);\n}\n\nexport function matchesCategory(\n categoryNames: string[],\n selectedCategory: string | null,\n): boolean {\n if (!selectedCategory) {\n return true;\n }\n\n return categoryNames.includes(selectedCategory);\n}\n\nexport function getRegisteredVendorStartDates(vendor: VendorType): string[] {\n const relations = vendor.relations ?? [];\n\n return relations.flatMap((relation) =>\n (relation.relationDates ?? []).map(\n (relationDate: RelationDateType) => relationDate.dateTime.startDate,\n ),\n );\n}\n\nexport function getRegisteredVendorCategoryNames(vendor: VendorType): string[] {\n return (vendor.categories ?? []).map((category) => category.name);\n}\n\nexport function getUnregisteredVendorStartDates(\n vendor: UnregisteredVendorType,\n): string[] {\n return (vendor.invitations ?? []).flatMap((invitation) =>\n invitation.dateTime.map((date) => date.startDate),\n );\n}\n\nexport function getUnregisteredVendorCategoryNames(\n vendor: UnregisteredVendorType,\n): string[] {\n return availableCategories\n .filter(\n (category) => category.id && vendor.categoryIds.includes(category.id),\n )\n .map((category) => category.name);\n}\n\nexport function filterRegisteredVendorsForEvent(\n vendors: VendorType[] | null | undefined,\n eventStartDates: Set<string>,\n selectedDate: string | null,\n selectedCategory: string | null,\n): VendorType[] {\n if (!vendors) {\n return [];\n }\n\n return vendors.filter((vendor) => {\n const startDates = getRegisteredVendorStartDates(vendor);\n\n return (\n hasMatchingEventDate(startDates, eventStartDates) &&\n matchesSelectedDate(startDates, selectedDate) &&\n matchesCategory(\n getRegisteredVendorCategoryNames(vendor),\n selectedCategory,\n )\n );\n });\n}\n\nexport function filterUnregisteredVendorsForEvent(\n vendors: UnregisteredVendorType[],\n eventStartDates: Set<string>,\n selectedDate: string | null,\n selectedCategory: string | null,\n): UnregisteredVendorType[] {\n return vendors.filter((vendor) => {\n const startDates = getUnregisteredVendorStartDates(vendor);\n\n return (\n hasMatchingEventDate(startDates, eventStartDates) &&\n matchesSelectedDate(startDates, selectedDate) &&\n matchesCategory(\n getUnregisteredVendorCategoryNames(vendor),\n selectedCategory,\n )\n );\n });\n}\n\nexport function filterRegisteredVendorsByEventDatesOnly(\n vendors: VendorType[] | null | undefined,\n eventStartDates: Set<string>,\n): VendorType[] {\n if (!vendors) {\n return [];\n }\n\n return vendors.filter((vendor) =>\n hasMatchingEventDate(\n getRegisteredVendorStartDates(vendor),\n eventStartDates,\n ),\n );\n}\n\nexport function filterUnregisteredVendorsByEventDatesOnly(\n vendors: UnregisteredVendorType[],\n eventStartDates: Set<string>,\n): UnregisteredVendorType[] {\n return vendors.filter((vendor) =>\n hasMatchingEventDate(\n getUnregisteredVendorStartDates(vendor),\n eventStartDates,\n ),\n );\n}\n\nexport function collectStallholderStartDates(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): string[] {\n return [\n ...registeredVendors.flatMap(getRegisteredVendorStartDates),\n ...unregisteredVendors.flatMap(getUnregisteredVendorStartDates),\n ];\n}\n\nexport function getEventDatesWithStallholders(\n eventDateTime: DateTimeType[] | undefined,\n stallholderStartDates: string[],\n): DateTimeType[] {\n if (!eventDateTime?.length) {\n return [];\n }\n\n const startDates = new Set(stallholderStartDates);\n\n return sortDatesChronologically(\n eventDateTime.filter((date) => startDates.has(date.startDate)),\n );\n}\n\nexport function getStallholderCategoryNames(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): string[] {\n const names = [\n ...registeredVendors.flatMap(getRegisteredVendorCategoryNames),\n ...unregisteredVendors.flatMap(getUnregisteredVendorCategoryNames),\n ];\n\n return Array.from(new Set(names));\n}\n\nexport function getStallholderCategoryOptions(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): StallholderFilterOption[] {\n return getStallholderCategoryNames(\n registeredVendors,\n unregisteredVendors,\n ).map((name) => ({\n label: name,\n value: name,\n }));\n}\n\nexport function getEventStallholderEmptyMessage(\n hasAnyStallholdersForEvent: boolean,\n selectedDate: string | null,\n): string {\n return hasAnyStallholdersForEvent && selectedDate\n ? \"No stallholders found for this event date.\"\n : \"No stallholders found for this event.\";\n}\n","import type { StallholderFilterOption } from \"../eventStallholders/eventStallholderFilters\";\nimport type { EventListItemType } from \"../types/event\";\nimport type { DateTimeType } from \"../types/global\";\nimport { sortDatesChronologically } from \"../utils/date\";\n\ntype RelationType = NonNullable<EventListItemType[\"relations\"]>[number];\ntype RelationDateType = NonNullable<RelationType[\"relationDates\"]>[number];\n\nexport function relationHasSelectedDate(\n relation: RelationType,\n selectedDate: string,\n): boolean {\n return (\n relation.relationDates?.some(\n (date: RelationDateType) => date.dateTime.startDate === selectedDate,\n ) ?? false\n );\n}\n\nexport function eventHasSelectedDate(\n event: EventListItemType,\n selectedDate: string,\n): boolean {\n return (\n event.relations?.some((relation) =>\n relationHasSelectedDate(relation, selectedDate),\n ) ?? false\n );\n}\n\nexport function filterVendorEventsBySelectedDate(\n events: EventListItemType[] | null | undefined,\n selectedDate: string | null,\n): EventListItemType[] {\n if (!events) {\n return [];\n }\n\n if (!selectedDate) {\n return events;\n }\n\n return events.filter((event) => eventHasSelectedDate(event, selectedDate));\n}\n\nexport function collectVendorEventRelationDateTimes(\n events: EventListItemType[] | null | undefined,\n): DateTimeType[] {\n if (!events?.length) {\n return [];\n }\n\n return events.flatMap((event) =>\n (event.relations ?? []).flatMap((relation) =>\n (relation.relationDates ?? []).map(\n (relationDate) => relationDate.dateTime,\n ),\n ),\n );\n}\n\nexport function getVendorEventRelationStartDates(\n events: EventListItemType[] | null | undefined,\n): string[] {\n const sortedDates = sortDatesChronologically(\n collectVendorEventRelationDateTimes(events),\n );\n const uniqueStartDates: string[] = [];\n\n for (const dateTime of sortedDates) {\n if (!uniqueStartDates.includes(dateTime.startDate)) {\n uniqueStartDates.push(dateTime.startDate);\n }\n }\n\n return uniqueStartDates;\n}\n\nexport function getVendorEventRelationDateOptions(\n events: EventListItemType[] | null | undefined,\n): StallholderFilterOption[] {\n return getVendorEventRelationStartDates(events).map((startDate) => ({\n label: startDate,\n value: startDate,\n }));\n}\n\nexport function getVendorEventsEmptyMessage(): string {\n return \"No events found.\";\n}\n","import {\n AffiliateRewardType,\n EnumAffiliateParticipantType,\n EnumAffiliateRewardType,\n} from \"src/types/affiliate\";\n\nexport const AFFILIATE_PARTICIPANT_TYPE_LABELS = {\n [EnumAffiliateParticipantType.INDIVIDUAL]: \"Individual\",\n [EnumAffiliateParticipantType.SOLE_TRADER]: \"Sole Trader\",\n [EnumAffiliateParticipantType.COMPANY]: \"Company\",\n} as const satisfies Record<EnumAffiliateParticipantType, string>;\n\ntype AffiliateRewardConfig = {\n description: string;\n value: number;\n};\n\nexport const AFFILIATE_REWARDS = {\n [EnumAffiliateRewardType.NEW_EVENT_REGISTRATION]: {\n description: \"10 points for new event registration (one-time reward)\",\n value: 10,\n },\n [EnumAffiliateRewardType.NEW_VENDOR_REGISTRATION]: {\n description: \"5 points for new vendor registration (one-time reward)\",\n value: 5,\n },\n [EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION]: {\n description: \"3 points for active vendor pro subscription (monthly reward)\",\n value: 3,\n },\n [EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION]: {\n description:\n \"1 point for active vendor standard subscription (monthly reward)\",\n value: 1,\n },\n [EnumAffiliateRewardType.ACTIVE_VENDOR_BONUS_REWARD]: {\n description:\n \"5 bonus points for every 10th active vendor (one-time reward)\",\n value: 5,\n },\n [EnumAffiliateRewardType.ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS]: {\n description:\n \"40 points for active event with vendor registrations (one-time reward)\",\n value: 40,\n },\n} satisfies Record<EnumAffiliateRewardType, AffiliateRewardConfig>;\n\n/**\n * Create an affiliate reward\n * @param rewardType - The type of reward\n * @param createdAt - The date the reward was received\n * @returns The affiliate reward\n * @example\n * const reward = createAffiliateReward(EnumAffiliateRewardType.NEW_EVENT_REGISTRATION, new Date());\n * console.log(reward);\n */\nexport function createAffiliateReward(\n rewardType: EnumAffiliateRewardType,\n createdAt: Date,\n): AffiliateRewardType {\n const reward = AFFILIATE_REWARDS[rewardType];\n\n return {\n createdAt,\n redeemedAt: null,\n rewardDescription: reward.description,\n rewardType,\n rewardValue: reward.value,\n };\n}\n","/**\n * NZ IRD number validation (Inland Revenue modulus-11 check digit).\n *\n * Valid range: 10,000,000 – 200,000,000 (upper limit raised Feb 2026; length stays\n * 8–9 digits). See IRD file-upload / payday filing specifications.\n */\n\nconst PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\nconst IRD_MIN = 10_000_000;\n/** Raised from 150_000_000 as of IRD's Feb 2026 annual update. */\nconst IRD_MAX = 200_000_000;\n\nfunction calculateCheckDigit(\n baseDigits: string,\n weights: readonly number[],\n): number {\n const sum = [...baseDigits].reduce(\n (acc, digit, index) => acc + Number(digit) * weights[index],\n 0,\n );\n const remainder = sum % 11;\n return remainder === 0 ? 0 : 11 - remainder;\n}\n\n/** Digits only; strips spaces/hyphens and other non-digits. */\nexport function normalizeIrdNumber(value: string): string {\n return value.replace(/\\D/g, \"\");\n}\n\n/**\n * Formats an IRD number as groups of three digits separated by hyphens\n * (e.g. `490-918-50` / `049-091-850`), capping at 9 digits.\n */\nexport function formatIrdNumber(input: string): string {\n const digitsOnly = normalizeIrdNumber(input).slice(0, 9);\n\n const parts = [];\n if (digitsOnly.length > 0) parts.push(digitsOnly.slice(0, 3));\n if (digitsOnly.length > 3) parts.push(digitsOnly.slice(3, 6));\n if (digitsOnly.length > 6) parts.push(digitsOnly.slice(6, 9));\n\n return parts.join(\"-\");\n}\n\n/**\n * Returns true when `value` is a valid NZ IRD number (8–9 digits, in issued\n * range, and modulus-11 check digit matches).\n */\nexport function isValidIrdNumber(value: string): boolean {\n const digits = normalizeIrdNumber(value);\n\n if (!/^\\d{8,9}$/.test(digits)) {\n return false;\n }\n\n const ird = Number(digits);\n if (ird < IRD_MIN || ird > IRD_MAX) {\n return false;\n }\n\n const padded = digits.padStart(9, \"0\");\n const baseDigits = padded.slice(0, -1);\n const checkDigit = Number(padded.slice(-1));\n\n let calculated = calculateCheckDigit(baseDigits, PRIMARY_WEIGHTS);\n if (calculated === 10) {\n calculated = calculateCheckDigit(baseDigits, SECONDARY_WEIGHTS);\n if (calculated === 10) {\n return false;\n }\n }\n\n return calculated === checkDigit;\n}\n","import { isTransientNetworkError } from \"./isTransientNetworkError\";\n\nexport const DEFAULT_USER_FACING_ERROR =\n \"Something went wrong. Please try again.\";\n\nexport const NETWORK_USER_FACING_ERROR =\n \"Unable to connect. Please check your internet connection and try again.\";\n\n/** Backend/infra messages that must never be shown to end users. */\nconst INTERNAL_ERROR_PATTERNS = [\n /connection\\s*<monitor>/i,\n /\\bmongo/i,\n /\\b\\d{1,3}(?:\\.\\d{1,3}){3}(?::\\d+)?\\b/,\n /\\beconn(?:refused|reset|aborted)\\b/i,\n /\\betimedout\\b/i,\n /\\benotfound\\b/i,\n /\\bsocket hang up\\b/i,\n /\\binternal server error\\b/i,\n /received status code 5\\d\\d/i,\n /\\bat\\s+\\S+\\s+\\([^)]+:\\d+:\\d+\\)/,\n] as const;\n\nexport function isInternalErrorMessage(message: string): boolean {\n return INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));\n}\n\n/**\n * Maps raw API/client errors to a safe message for UI surfaces.\n * Keeps intentional product copy (e.g. \"Vendor not found\"); hides infra details.\n */\nexport function toUserFacingErrorMessage(\n error: string | null | undefined,\n fallback = DEFAULT_USER_FACING_ERROR,\n): string {\n const message = (error ?? \"\").trim();\n if (!message) return fallback;\n\n if (isTransientNetworkError(message)) {\n return NETWORK_USER_FACING_ERROR;\n }\n\n if (isInternalErrorMessage(message)) {\n return fallback;\n }\n\n return message;\n}\n\n/** Extracts and sanitizes an unknown catch/Apollo error for toasts and alerts. */\nexport function getErrorMessage(\n error: unknown,\n fallback = DEFAULT_USER_FACING_ERROR,\n): string {\n let raw: string | undefined;\n if (error instanceof Error) {\n raw = error.message;\n } else if (typeof error === \"string\") {\n raw = error;\n }\n\n return toUserFacingErrorMessage(raw, fallback);\n}\n","export function normalizePromoCode(\n decoratedPromoCode: string | null | undefined,\n): string | null {\n const normalized = decoratedPromoCode?.trim().toUpperCase();\n return normalized || null;\n}\n","import {\n SchemaCreateBulkNotificationInput,\n NotificationModel,\n} from \"src/mongoose/Notification\";\nimport { ObjectId } from \"src/types\";\n\n/**\n * Create notifications in the database for multiple users\n * This is typically called when sending push notifications\n */\nexport async function saveNotificationsInDb(\n payload: SchemaCreateBulkNotificationInput,\n): Promise<ObjectId[]> {\n const { data, message, title, type, userIds } = payload;\n console.log('NOTIFICATION DATA', JSON.stringify(payload, null, 2));\n try {\n const notifications = userIds.map((userId) => ({\n data,\n isRead: false,\n message,\n title,\n type,\n userId,\n }));\n\n // Save notifications to database\n await NotificationModel.insertMany(notifications);\n console.log(\n `Created ${notifications.length} notifications for ${userIds.length} users`,\n );\n\n return [...new Set(userIds)];\n } catch (error) {\n console.error(\"Failed to create notifications:\", error);\n return [];\n //throw new Error(`Failed to create notifications: ${error}`);\n }\n}\n","import { NotificationDataType } from \"@timardex/cluemart-shared\";\nimport { Expo, ExpoPushMessage, ExpoPushTicket } from \"expo-server-sdk\";\n\nimport { SchemaCreateBulkNotificationInput } from \"src/mongoose/Notification\";\nimport { PushTokenModel } from \"src/mongoose/PushToken\";\n\nconst expo = new Expo();\n\n/**\n * Safely extract tokens from ExpoPushMessage handling both string and array cases\n */\nfunction extractTokensFromMessage(message: ExpoPushMessage): string[] {\n return Array.isArray(message.to) ? message.to : [message.to];\n}\n\ninterface CreatePushMessagesOptions {\n tokens: string[];\n message: string;\n title: string;\n data: NotificationDataType;\n}\n\n/**\n * Create push messages from valid tokens\n */\nfunction createPushMessages({\n tokens,\n message,\n title,\n data,\n}: CreatePushMessagesOptions): {\n messages: ExpoPushMessage[];\n invalidTokens: string[];\n} {\n const messages: ExpoPushMessage[] = [];\n const invalidTokens: string[] = [];\n\n for (const token of tokens) {\n if (!Expo.isExpoPushToken(token)) {\n invalidTokens.push(token);\n continue;\n }\n\n messages.push({\n body: message,\n data: { ...data },\n sound: \"tui.wav\",\n title,\n to: token,\n });\n }\n\n return { invalidTokens, messages };\n}\n\n/**\n * Process chunk results and extract failed tokens\n */\nfunction processChunkResults(\n tickets: ExpoPushTicket[],\n chunk: ExpoPushMessage[],\n): { successCount: number; failedTokens: string[] } {\n let successCount = 0;\n const failedTokens: string[] = [];\n\n for (const [ticketIndex, ticket] of tickets.entries()) {\n if (ticket.status === \"error\") {\n const message = chunk[ticketIndex];\n if (message) {\n const tokens = extractTokensFromMessage(message);\n if (ticket.details?.error === \"DeviceNotRegistered\") {\n failedTokens.push(...tokens);\n }\n console.log(\"Push notification error\", {\n error: ticket.details?.error,\n tokens,\n });\n }\n } else {\n successCount++;\n }\n }\n\n return { failedTokens, successCount };\n}\n\n/**\n * Send a single chunk of push notifications\n */\nasync function sendChunk(\n chunk: ExpoPushMessage[],\n chunkIndex: number,\n): Promise<{ successCount: number; failedTokens: string[] }> {\n try {\n const tickets = await expo.sendPushNotificationsAsync(chunk);\n const { successCount, failedTokens } = processChunkResults(tickets, chunk);\n\n console.log(\n `Chunk ${chunkIndex + 1}: Sent ${successCount}/${chunk.length} notifications successfully`,\n );\n\n return { failedTokens, successCount };\n } catch (error) {\n console.log(\"Error sending Expo push notification chunk\", {\n chunkIndex,\n chunkSize: chunk.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return { failedTokens: [], successCount: 0 };\n }\n}\n\nexport async function sendPushNotifications({\n data,\n message,\n title,\n userIds,\n}: SchemaCreateBulkNotificationInput) {\n const pushTokens = await PushTokenModel.find({ userId: { $in: userIds } });\n const expoTokens = pushTokens.map((token) => token.token);\n\n if (!data) return;\n\n const { messages, invalidTokens } = createPushMessages({\n data,\n message,\n title,\n tokens: expoTokens,\n });\n\n // Log invalid tokens\n if (invalidTokens.length > 0) {\n console.log(`Found ${invalidTokens.length} invalid push tokens`);\n }\n\n if (messages.length === 0) {\n console.log(\"No valid messages to send after filtering tokens\");\n return;\n }\n\n // Send notifications in chunks\n const chunks = expo.chunkPushNotifications(messages);\n let totalSuccessCount = 0;\n const allFailedTokens: string[] = [];\n\n for (const [chunkIndex, chunk] of chunks.entries()) {\n const { successCount, failedTokens } = await sendChunk(\n chunk,\n chunkIndex + 1,\n );\n totalSuccessCount += successCount;\n allFailedTokens.push(...failedTokens);\n }\n\n // Log final results\n console.log(\n `Sent push notification to ${totalSuccessCount}/${messages.length} tokens across ${chunks.length} chunks`,\n );\n\n if (allFailedTokens.length > 0) {\n console.log(`Found ${allFailedTokens.length} failed push tokens`);\n }\n}\n","import {\n EnumAffiliateRewardType,\n EnumSubscriptionStatus,\n EnumUserLicence,\n UserLicenceType,\n UserType,\n} from \"@timardex/cluemart-shared\";\n\n/**\n * Paying = Stripe subscriptionId present and status active or trialing.\n * Matches `hasActiveStripeSubscription` in cluemart-server User utils.\n */\nfunction isPayingStripeSubscription(\n stripe?: UserType[\"stripe\"] | null,\n): boolean {\n if (!stripe?.subscriptionId) return false;\n\n return (\n (stripe.status === EnumSubscriptionStatus.ACTIVE ||\n stripe.status === EnumSubscriptionStatus.TRIALING) &&\n (stripe.currentPlan === EnumUserLicence.PRO_VENDOR ||\n stripe.currentPlan === EnumUserLicence.PRO_PLUS_VENDOR)\n );\n}\n\n/**\n * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.\n *\n * Prefer Pro / Pro+ over Standard when both are valid.\n * Pro / Pro+ only yields the Pro reward when the owner has an active Stripe subscription;\n * otherwise falls back to the Standard subscription reward.\n * Returns `null` when no non-expired vendor licence applies.\n */\nexport function mapVendorLicenceToSubscriptionRewardType(\n licences: UserLicenceType[] | null | undefined,\n now: Date = new Date(),\n stripe?: UserType[\"stripe\"] | null,\n): EnumAffiliateRewardType | null {\n const validTypes = new Set(\n (licences ?? [])\n .filter(\n (licence) => new Date(licence.expiryDate).getTime() > now.getTime(),\n )\n .map((licence) => licence.licenceType),\n );\n\n if (\n validTypes.has(EnumUserLicence.PRO_VENDOR) ||\n validTypes.has(EnumUserLicence.PRO_PLUS_VENDOR)\n ) {\n if (isPayingStripeSubscription(stripe)) {\n return EnumAffiliateRewardType.ACTIVE_VENDOR_PRO_SUBSCRIPTION;\n }\n return EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION;\n }\n\n if (validTypes.has(EnumUserLicence.STANDARD_VENDOR)) {\n return EnumAffiliateRewardType.ACTIVE_VENDOR_STANDARD_SUBSCRIPTION;\n }\n\n return null;\n}\n\n/** Inclusive start / exclusive end of the UTC calendar month containing `now`. */\nexport function getSubscriptionRewardPeriodBounds(now: Date = new Date()): {\n periodEnd: Date;\n periodStart: Date;\n} {\n const periodStart = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1),\n );\n const periodEnd = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1),\n );\n return { periodEnd, periodStart };\n}\n","import {\n AffiliateResourceType,\n EnumAffiliateRewardType,\n EnumNotificationResourceType,\n EnumNotificationType,\n EnumResourceType,\n PromoCodeType,\n} from \"@timardex/cluemart-shared\";\nimport { createAffiliateReward } from \"@timardex/cluemart-shared/utils\";\n\nimport {\n AffiliateModel,\n SchemaOwnerType,\n UserModel,\n VendorModel,\n} from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { normalizePromoCode } from \"../promoCode/normalizePromoCode\";\nimport { saveNotificationsInDb } from \"../saveNotificationsInDb\";\nimport { sendPushNotifications } from \"../sendPushNotifications\";\n\nimport {\n getSubscriptionRewardPeriodBounds,\n mapVendorLicenceToSubscriptionRewardType,\n} from \"./vendorSubscriptionRewards\";\n\nexport type AwardAffiliateVendorSubscriptionRewardsResult = {\n awarded: number;\n skipped: number;\n};\n\ntype LeanAffiliateResource = Omit<\n AffiliateResourceType,\n \"resourceId\" | \"resourceOwner\" | \"rewards\"\n> & {\n resourceId: ObjectId;\n resourceOwner: SchemaOwnerType;\n};\n\ntype LeanAffiliate = {\n _id: ObjectId;\n affiliateCode: PromoCodeType;\n affiliateResources: LeanAffiliateResource[];\n owner: SchemaOwnerType;\n};\n\ntype PeriodBounds = {\n periodEnd: Date;\n periodStart: Date;\n};\n\nfunction isEligibleVendorResource(resource: LeanAffiliateResource): boolean {\n return (\n resource.resourceType === EnumResourceType.VENDOR &&\n resource.resourceActive === true &&\n resource.resourceDeletedAt == null\n );\n}\n\nasync function findAffiliatesWithActiveVendorReferrals(): Promise<\n LeanAffiliate[]\n> {\n return (await AffiliateModel.find({\n affiliateResources: {\n $elemMatch: {\n resourceActive: true,\n resourceDeletedAt: null,\n resourceType: EnumResourceType.VENDOR,\n },\n },\n deletedAt: null,\n })\n .select(\"_id affiliateCode affiliateResources owner\")\n .lean()\n .exec()) as LeanAffiliate[];\n}\n\nasync function isEligibleReferredVendor({\n affiliateCode,\n resourceId,\n resourceOwnerUserId,\n}: {\n affiliateCode: string;\n resourceId: ObjectId;\n resourceOwnerUserId: ObjectId | string;\n}): Promise<boolean> {\n const match = await VendorModel.exists({\n _id: resourceId,\n active: true,\n deletedAt: null,\n \"owner.userId\": resourceOwnerUserId,\n promoCodes: affiliateCode,\n }).exec();\n\n return Boolean(match);\n}\n\nasync function resolveSubscriptionRewardTypeForVendorOwner(\n vendorOwnerUserId: ObjectId | string,\n now: Date,\n): Promise<EnumAffiliateRewardType | null> {\n const vendorOwner = await UserModel.findById(vendorOwnerUserId)\n .select(\"licences stripe\")\n .lean()\n .exec();\n\n return mapVendorLicenceToSubscriptionRewardType(\n vendorOwner?.licences,\n now,\n vendorOwner?.stripe,\n );\n}\n\nasync function notifyAffiliateOwnerOfSubscriptionReward({\n affiliateId,\n affiliateOwnerUserId,\n resourceName,\n rewardValue,\n}: {\n affiliateId: ObjectId | string;\n affiliateOwnerUserId: ObjectId | string;\n resourceName: string;\n rewardValue: number;\n}): Promise<void> {\n const payload = {\n data: {\n resourceId: String(affiliateId),\n resourceName,\n resourceType: EnumNotificationResourceType.AFFILIATE_REWARD_RECEIVED,\n },\n message: `You earned ${rewardValue} points! Your referred vendor \"${resourceName}\" has an active subscription.`,\n title: \"Affiliate points earned\",\n type: EnumNotificationType.SYSTEM,\n userIds: [affiliateOwnerUserId as ObjectId],\n };\n\n try {\n await saveNotificationsInDb(payload);\n await sendPushNotifications(payload);\n } catch (error) {\n console.error(\n `[affiliate] subscription reward notification failed for affiliate ${String(affiliateId)}:`,\n error instanceof Error ? error.message : error,\n );\n }\n}\n\nasync function tryAwardSubscriptionReward({\n affiliate,\n createdAt,\n period,\n resource,\n rewardType,\n}: {\n affiliate: LeanAffiliate;\n createdAt: Date;\n period: PeriodBounds;\n resource: LeanAffiliateResource;\n rewardType: EnumAffiliateRewardType;\n}): Promise<\"awarded\" | \"skipped\"> {\n const reward = createAffiliateReward(rewardType, createdAt);\n\n const updateResult = await AffiliateModel.updateOne(\n {\n _id: affiliate._id,\n affiliateResources: {\n $elemMatch: {\n resourceActive: true,\n resourceDeletedAt: null,\n resourceId: resource.resourceId,\n resourceType: EnumResourceType.VENDOR,\n rewards: {\n $not: {\n $elemMatch: {\n createdAt: {\n $gte: period.periodStart,\n $lt: period.periodEnd,\n },\n rewardType,\n },\n },\n },\n },\n },\n deletedAt: null,\n },\n {\n $inc: { overallPoints: reward.rewardValue },\n $push: { \"affiliateResources.$.rewards\": reward },\n },\n ).exec();\n\n if (updateResult.modifiedCount === 0) {\n return \"skipped\";\n }\n\n await notifyAffiliateOwnerOfSubscriptionReward({\n affiliateId: affiliate._id,\n affiliateOwnerUserId: affiliate.owner.userId,\n resourceName: resource.resourceName,\n rewardValue: reward.rewardValue,\n });\n\n return \"awarded\";\n}\n\nasync function processAffiliateVendorReferral({\n affiliate,\n affiliateCode,\n createdAt,\n now,\n period,\n resource,\n}: {\n affiliate: LeanAffiliate;\n affiliateCode: string;\n createdAt: Date;\n now: Date;\n period: PeriodBounds;\n resource: LeanAffiliateResource;\n}): Promise<\"awarded\" | \"skipped\"> {\n const vendorEligible = await isEligibleReferredVendor({\n affiliateCode,\n resourceId: resource.resourceId,\n resourceOwnerUserId: resource.resourceOwner.userId,\n });\n if (!vendorEligible) {\n return \"skipped\";\n }\n\n const rewardType = await resolveSubscriptionRewardTypeForVendorOwner(\n resource.resourceOwner.userId,\n now,\n );\n if (!rewardType) {\n return \"skipped\";\n }\n\n return tryAwardSubscriptionReward({\n affiliate,\n createdAt,\n period,\n resource,\n rewardType,\n });\n}\n\n/**\n * Awards monthly vendor subscription rewards to affiliates with active referred vendors.\n *\n * Eligibility per affiliate resource:\n * - Affiliate is not soft-deleted\n * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted\n * - Vendor is active, not deleted, has the affiliate promo code, and\n * `owner.userId` matches `affiliateResources.resourceOwner.userId`\n * - `affiliateResources.resourceOwner` has a non-expired Standard / Pro / Pro+ vendor licence\n *\n * Reward type follows the owner's highest valid licence + Stripe paying status:\n * - Pro / Pro+ with paying Stripe (ACTIVE/TRIALING) → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)\n * - Pro / Pro+ without paying Stripe → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)\n * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)\n *\n * Idempotent per affiliate + vendor + reward type + calendar month.\n * On successful award, notifies the affiliate owner (best-effort).\n *\n * @param now - Reference time for month bounds and licence expiry (defaults to now).\n * @returns Counts of awarded and skipped candidates.\n */\nexport async function awardAffiliateVendorSubscriptionRewards(\n now: Date = new Date(),\n): Promise<AwardAffiliateVendorSubscriptionRewardsResult> {\n const period = getSubscriptionRewardPeriodBounds(now);\n const createdAt = now;\n const affiliates = await findAffiliatesWithActiveVendorReferrals();\n\n let awarded = 0;\n let skipped = 0;\n const seenPairs = new Set<string>();\n\n for (const affiliate of affiliates) {\n const affiliateCode = normalizePromoCode(affiliate.affiliateCode);\n if (!affiliateCode) {\n continue;\n }\n\n for (const resource of affiliate.affiliateResources) {\n if (!isEligibleVendorResource(resource)) {\n continue;\n }\n\n const pairKey = `${String(affiliate._id)}:${String(resource.resourceId)}`;\n if (seenPairs.has(pairKey)) {\n continue;\n }\n seenPairs.add(pairKey);\n\n const outcome = await processAffiliateVendorReferral({\n affiliate,\n affiliateCode,\n createdAt,\n now,\n period,\n resource,\n });\n\n if (outcome === \"awarded\") {\n awarded += 1;\n } else {\n skipped += 1;\n }\n }\n }\n\n return { awarded, skipped };\n}\n","import { PromoCodeType } from \"@timardex/cluemart-shared\";\n\nimport { AffiliateModel } from \"src/mongoose\";\n\n/**\n * Loads the affiliate whose `affiliateCode` matches the given promo code.\n *\n * Affiliate codes are assigned on admin activation, so referrals only resolve after\n * a code exists. Soft-deleted affiliates (`deletedAt` set) are excluded.\n *\n * @param promoCode - Normalized affiliate promo code to resolve.\n * @returns The matching affiliate document, or `null` when not found.\n */\nexport async function findAffiliateByPromoCode(promoCode: PromoCodeType) {\n return AffiliateModel.findOne({\n affiliateCode: promoCode,\n deletedAt: null,\n }).exec();\n}\n","import { PromoCodeType } from \"@timardex/cluemart-shared\";\n\nimport { normalizePromoCode } from \"../promoCode/normalizePromoCode\";\n\n/**\n * Normalizes, trims, uppercases, and deduplicates resource promo codes.\n *\n * @param promoCodes - Raw promo codes from a create-resource mutation input.\n * @returns Unique normalized codes; empty strings and whitespace-only values are removed.\n */\nexport function normalizeAffiliatePromoCodes(\n promoCodes: PromoCodeType[] | null | undefined,\n): PromoCodeType[] {\n const normalized = (promoCodes ?? [])\n .map((code) => normalizePromoCode(code))\n .filter((code): code is PromoCodeType => code !== null);\n\n return [...new Set(normalized)];\n}\n","import mongoose from \"mongoose\";\n\n/**\n * Connect to MongoDB using Mongoose.\n * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).\n */\nexport const connectToDatabase = async ({\n appName,\n dbName,\n dbPassword,\n dbUser,\n mongodbUri,\n}: {\n appName: string;\n dbName: string;\n dbPassword: string;\n dbUser: string;\n mongodbUri: string;\n}) => {\n try {\n // Check if MONGODB_URI is provided (for local Docker MongoDB)\n const mongoUri = mongodbUri\n ? mongodbUri\n : // Fallback to MongoDB Atlas connection string\n `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`;\n\n await mongoose.connect(mongoUri);\n\n const connectionType = mongodbUri ? \"Local MongoDB\" : \"MongoDB Atlas\";\n console.log(\n `${connectionType} connected from server/src/service/database.ts`,\n );\n } catch (err) {\n console.error(\"Error connecting to MongoDB:\", err);\n throw err; // You can throw the error if you want to stop the server in case of connection failure\n }\n};\n","import {\n NotificationModel,\n SchemaCreateBulkNotificationInput,\n} from \"src/mongoose/Notification\";\nimport { EnumPubSubEvents, GraphQLContext, ObjectId } from \"src/types\";\n\nimport { saveNotificationsInDb } from \"./saveNotificationsInDb\";\nimport { sendPushNotifications } from \"./sendPushNotifications\";\n\n/**\n * Publish-only pubsub used to emit GraphQL subscription events. Optional in\n * {@link notifyUsers} because non-GraphQL processes (e.g. the cron worker) have\n * no subscribers to publish to.\n */\nexport type NotificationPubSub = Pick<GraphQLContext[\"pubsub\"], \"publish\">;\n\n/** Publishes notification list/count subscription events for a user. */\nexport async function publishNotificationEvents(\n userId: ObjectId,\n pubsub: NotificationPubSub,\n) {\n try {\n // Get user's notifications for the subscription\n const userNotifications = await NotificationModel.find({\n userId,\n }).sort({ createdAt: -1 });\n\n // Get notification count\n const [total, unread] = await Promise.all([\n NotificationModel.countDocuments({ userId }),\n NotificationModel.countDocuments({ isRead: false, userId }),\n ]);\n\n // Publish both events\n pubsub.publish(EnumPubSubEvents.GET_NOTIFICATIONS, {\n getNotifications: userNotifications,\n getNotificationsUserId: userId,\n });\n\n pubsub.publish(EnumPubSubEvents.GET_NOTIFICATIONS_COUNT, {\n getNotificationsCount: { total, unread, userId },\n });\n\n console.log(`Published notification events for user: ${String(userId)}`);\n } catch (error) {\n console.error(\n `Failed to publish notification events for user ${String(userId)}:`,\n error,\n );\n }\n}\n\n/**\n * Sends push notifications, saves them in the database, and (when a `pubsub`\n * is provided) publishes GraphQL subscription events for each affected user.\n *\n * Errors are logged and swallowed; this function never throws. Instead it\n * returns `false` when notifications were not confirmed (an error was thrown,\n * or `saveNotificationsInDb` failed to persist any rows for a non-empty\n * `userIds`), so callers can gate success logs/metrics on the result.\n */\nexport async function notifyUsers({\n payload,\n pubsub,\n}: {\n payload: SchemaCreateBulkNotificationInput;\n pubsub?: NotificationPubSub;\n}): Promise<boolean> {\n try {\n await sendPushNotifications(payload);\n\n const uniqueUserIds = await saveNotificationsInDb(payload);\n // saveNotificationsInDb swallows its own errors and returns []; treat an\n // empty result for a non-empty input as a persistence failure.\n const persisted = payload.userIds.length === 0 || uniqueUserIds.length > 0;\n\n if (pubsub) {\n for (const userId of uniqueUserIds) {\n await publishNotificationEvents(userId, pubsub);\n }\n }\n\n return persisted;\n } catch (error) {\n console.error(\"Error in notifyUsers:\", error);\n return false;\n }\n}\n","import { EnumAdStatus } from \"@timardex/cluemart-shared\";\n\nimport { AdModel } from \"src/mongoose\";\n\n/**\n * Updates ad statuses based on start/end dates and validity\n */\nexport async function updateAdStatuses(): Promise<void> {\n const now = new Date();\n\n // invalid\n const invalidResult = await AdModel.updateMany(\n {\n $or: [\n { start: { $exists: false } },\n { end: { $exists: false } },\n { $expr: { $gt: [\"$start\", \"$end\"] } },\n ],\n status: { $ne: EnumAdStatus.PAUSED },\n },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n // expired\n const expiredResult = await AdModel.updateMany(\n { end: { $lte: now }, status: { $ne: EnumAdStatus.EXPIRED } },\n { $set: { status: EnumAdStatus.EXPIRED } },\n );\n\n // active\n const activeResult = await AdModel.updateMany(\n {\n end: { $gt: now },\n start: { $lte: now },\n status: { $ne: EnumAdStatus.ACTIVE },\n },\n { $set: { status: EnumAdStatus.ACTIVE } },\n );\n\n // paused\n const pausedResult = await AdModel.updateMany(\n { start: { $gt: now }, status: { $ne: EnumAdStatus.PAUSED } },\n { $set: { status: EnumAdStatus.PAUSED } },\n );\n\n console.log(\n `✅ Ad statuses updated: invalid=${invalidResult.modifiedCount}, expired=${expiredResult.modifiedCount}, active=${activeResult.modifiedCount}, paused=${pausedResult.modifiedCount}`,\n );\n}\n","import { EnumUserLicence } from \"@timardex/cluemart-shared\";\n\nimport { UserModel, VendorModel, SchemaVendorType } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nexport async function updateVendorBasedOnUserLicense(\n userId: ObjectId,\n licenceType: EnumUserLicence,\n): Promise<void> {\n try {\n /**\n * Fetch user vendor reference\n */\n const user = await UserModel.findById(userId)\n .select(\"vendor\")\n .lean()\n .exec();\n\n if (!user?.vendor) {\n console.warn(`[updateVendor] No vendor found for userId=${userId}`);\n return;\n }\n\n /**\n * Fetch vendor\n */\n const vendor = await VendorModel.findById(user.vendor)\n .lean<SchemaVendorType>()\n .exec();\n\n if (!vendor) {\n console.warn(`[updateVendor] Vendor not found for id=${user.vendor}`);\n return;\n }\n\n /**\n * Build vendor update payload\n */\n const updateData: Partial<SchemaVendorType> = {};\n\n const isStandardVendor = licenceType === EnumUserLicence.STANDARD_VENDOR;\n\n if (isStandardVendor) {\n updateData.availability = {\n corporate: false,\n private: false,\n school: false,\n };\n\n updateData.products = {\n active: false,\n productsList: vendor.products?.productsList ?? [],\n };\n\n updateData.calendar = {\n active: false,\n calendarData: vendor.calendar?.calendarData ?? [],\n };\n }\n\n /**\n * Image rules\n * STANDARD_VENDOR => only first 6 active, 6 is the default image limit for standard vendors\n * PRO_VENDOR => all active\n */\n updateData.images = (vendor.images ?? []).map((image, index) => ({\n ...image,\n active: isStandardVendor ? index < 6 : true,\n }));\n\n /**\n * Persist vendor updates\n */\n await VendorModel.updateOne({ _id: vendor._id }, { $set: updateData });\n } catch (error) {\n console.error(\"[updateVendorBasedOnUserLicense] Failed:\", error);\n }\n}\n","import mongoose from \"mongoose\";\n\n/**\n * True when `value` is a valid Mongo ObjectId string.\n */\nexport function isValidObjectId(value: string): boolean {\n return mongoose.Types.ObjectId.isValid(value);\n}\n\n/**\n * Recursively converts all ObjectId fields to strings in an object\n * This is needed because GraphQL expects string IDs, not ObjectIds\n */\nexport function convertObjectIdsToStrings(obj: any): any {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (obj instanceof mongoose.Types.ObjectId) {\n return obj.toString();\n }\n\n if (obj instanceof Date) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertObjectIdsToStrings);\n }\n\n if (typeof obj === \"object\") {\n const converted: any = {};\n for (const [key, value] of Object.entries(obj)) {\n converted[key] = convertObjectIdsToStrings(value);\n }\n return converted;\n }\n\n return obj;\n}\n","import {\n dateFormat,\n DateTimeType,\n EnumEventDateStatus,\n timeFormat,\n} from \"@timardex/cluemart-shared\";\nimport dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat\";\nimport isoWeek from \"dayjs/plugin/isoWeek\";\n\nimport { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\n\n// Enable dayjs plugins used by event date parsing/status logic\ndayjs.extend(customParseFormat);\ndayjs.extend(isoWeek);\n\n/**\n * Determines the dateStatus for a future event date\n * @param startDateTime The start date-time of the event\n * @param now The current date-time\n * @returns The appropriate EnumEventDateStatus\n */\nexport function getFutureEventStatus(\n startDateTime: dayjs.Dayjs,\n now: dayjs.Dayjs,\n): EnumEventDateStatus {\n const hoursUntilStart = startDateTime.diff(now, \"hour\", true);\n if (hoursUntilStart > 0 && hoursUntilStart <= 4) {\n return EnumEventDateStatus.STARTING_SOON;\n }\n\n if (startDateTime.isSame(now, \"day\")) {\n return EnumEventDateStatus.TODAY;\n }\n\n if (startDateTime.isSame(now.add(1, \"day\"), \"day\")) {\n return EnumEventDateStatus.TOMORROW;\n }\n\n if (startDateTime.isSame(now, \"isoWeek\")) {\n return EnumEventDateStatus.THIS_WEEK;\n }\n\n if (startDateTime.isSame(now.add(1, \"week\"), \"isoWeek\")) {\n return EnumEventDateStatus.NEXT_WEEK;\n }\n\n return EnumEventDateStatus.UPCOMING;\n}\n\n/**\n * Updates the dateStatus field for a single DateTimeType object based on the current date/time\n * @param dateTime The date-time object to update\n * @returns A new object with updated dateStatus value\n */\nexport function updateSingleDateTimeStatus<T extends DateTimeType>(\n dateTime: T,\n now: dayjs.Dayjs = dayjs(),\n): T {\n // Parse start and end date-time\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const startDateTime = dayjs(\n `${dateTime.startDate} ${dateTime.startTime}`,\n dateTimeFormat,\n true,\n );\n const endDateTime = dayjs(\n `${dateTime.endDate} ${dateTime.endTime}`,\n dateTimeFormat,\n true,\n );\n\n // Skip if dates are invalid\n if (!startDateTime.isValid() || !endDateTime.isValid()) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n if (endDateTime.isBefore(startDateTime)) {\n return {\n ...dateTime,\n dateStatus: EnumEventDateStatus.INVALID,\n };\n }\n\n let dateStatus: EnumEventDateStatus;\n\n if (endDateTime.isAfter(now)) {\n if (startDateTime.isAfter(now)) {\n // Event has not started yet\n dateStatus = getFutureEventStatus(startDateTime, now);\n } else {\n // Event is in progress\n dateStatus = EnumEventDateStatus.STARTED;\n }\n } else {\n // Event has ended\n dateStatus = EnumEventDateStatus.ENDED;\n }\n\n return {\n ...dateTime,\n dateStatus,\n };\n}\n\nconst dateTimeStatusFilter = {\n dateTime: { $ne: [], $type: \"array\" },\n deletedAt: null,\n} as const;\n\nconst CURSOR_BATCH_SIZE = 50;\n\ntype DocWithDateTime = {\n _id: unknown;\n dateTime: DateTimeType[];\n};\n\ntype DateTimeBulkWriteOp = {\n updateOne: {\n filter: { _id: unknown };\n update: { $set: { dateTime: DateTimeType[] } };\n };\n};\n\ntype DateTimeCursor = AsyncIterable<DocWithDateTime> & {\n close: () => Promise<unknown>;\n};\n\ntype DateTimeUpdatableModel = {\n bulkWrite: (ops: DateTimeBulkWriteOp[]) => Promise<unknown>;\n find: (filter: typeof dateTimeStatusFilter) => {\n select: (fields: { _id: 1; dateTime: 1 }) => {\n lean: () => {\n cursor: (opts: { batchSize: number }) => DateTimeCursor;\n };\n };\n };\n};\n\nfunction hasDateTimeStatusChanges(\n stored: DateTimeType[],\n updated: DateTimeType[],\n): boolean {\n if (stored.length !== updated.length) {\n return true;\n }\n\n return stored.some(\n (slot, index) => slot.dateStatus !== updated[index].dateStatus,\n );\n}\n\nasync function updateModelDateTimeStatuses(\n model: DateTimeUpdatableModel,\n now: dayjs.Dayjs,\n): Promise<number> {\n let updated = 0;\n const cursor = model\n .find(dateTimeStatusFilter)\n .select({ _id: 1, dateTime: 1 })\n .lean()\n .cursor({ batchSize: CURSOR_BATCH_SIZE });\n\n let bulkOps: DateTimeBulkWriteOp[] = [];\n\n const flushBulkWrites = async (): Promise<void> => {\n if (!bulkOps.length) {\n return;\n }\n\n await model.bulkWrite(bulkOps);\n updated += bulkOps.length;\n bulkOps = [];\n };\n\n try {\n for await (const doc of cursor) {\n const dateTime = doc.dateTime.map((slot) =>\n updateSingleDateTimeStatus(slot, now),\n );\n\n if (!hasDateTimeStatusChanges(doc.dateTime, dateTime)) {\n continue;\n }\n\n bulkOps.push({\n updateOne: {\n filter: { _id: doc._id },\n update: { $set: { dateTime } },\n },\n });\n\n if (bulkOps.length >= CURSOR_BATCH_SIZE) {\n await flushBulkWrites();\n }\n }\n\n await flushBulkWrites();\n } finally {\n await cursor.close().catch(() => undefined);\n }\n\n return updated;\n}\n\n/**\n * Recomputes dateStatus for every dateTime slot on events and google imported markets.\n */\nexport async function updateAllEventDateTimeStatuses(): Promise<void> {\n const now = dayjs();\n const [eventCount, marketCount] = await Promise.all([\n updateModelDateTimeStatuses(EventModel as DateTimeUpdatableModel, now),\n updateModelDateTimeStatuses(\n GoogleImportedMarketModel as DateTimeUpdatableModel,\n now,\n ),\n ]);\n\n console.log(\n `✅ Event dateTime statuses updated: events=${eventCount} changed, google imported markets=${marketCount} changed`,\n );\n}\n","import { EventListItemType } from \"@timardex/cluemart-shared\";\n\nimport { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { convertObjectIdsToStrings } from \"../objectIdToString\";\n\ntype EventOrMarket = Pick<EventListItemType, \"_id\" | \"name\"> | null;\n\n/**\n * This function attempts to find an Event or a Google Imported Market by the given resource ID.\n * It first normalizes the resource ID to a string format, then performs parallel queries to both collections.\n * If an Event is found, it returns that; otherwise, it checks for a Google Imported Market and returns it if found.\n * If neither is found, it returns null.\n * @param resourceId - The ID of the resource to find, which can be an ObjectId, string, null, or undefined.\n * @returns A promise that resolves to either an Event or a Google Imported Market object containing _id and name, or null if not found.\n */\n\nexport async function findEventOrImportedMarketById(\n resourceId: ObjectId | string | null | undefined,\n): Promise<EventOrMarket> {\n if (!resourceId) {\n return null;\n }\n\n const normalizedId = convertObjectIdsToStrings(resourceId) as string;\n\n const [eventDoc, googleImportedDoc] = await Promise.all([\n EventModel.findById(normalizedId)\n .select(\"_id name\")\n .lean<Pick<EventListItemType, \"_id\" | \"name\">>()\n .exec(),\n GoogleImportedMarketModel.findById(normalizedId)\n .select(\"_id name\")\n .lean<Pick<EventListItemType, \"_id\" | \"name\">>()\n .exec(),\n ]);\n\n return eventDoc ?? googleImportedDoc;\n}\n","import {\n DateTimeWithPriceType,\n EnumInviteStatus,\n} from \"@timardex/cluemart-shared\";\nimport { DateTimeType } from \"@timardex/cluemart-shared/types\";\n\nimport { SchemaRelationType } from \"src/mongoose/Relation\";\n\ntype EventDateSlot = Pick<DateTimeType, \"startDate\" | \"startTime\">;\n\n/**\n * Returns true when at least one startDate/startTime slot from the previous\n * schedule is absent from the next schedule (i.e. a date was removed).\n */\nexport function didRemoveAnyEventDates(\n previousDateTime: EventDateSlot[] | undefined,\n nextDateTime: EventDateSlot[] | undefined,\n): boolean {\n if (!previousDateTime?.length) {\n return false;\n }\n\n return previousDateTime.some(\n (prev) =>\n !nextDateTime?.some(\n (next) =>\n next.startDate === prev.startDate &&\n next.startTime === prev.startTime,\n ),\n );\n}\n\n/**\n * Helper: Update relationDates based on event's dateTime\n * Marks dates as UNAVAILABLE if they no longer exist in the event's dateTime.\n */\nexport function updateRelationDatesToUnavailable(\n relationDates: SchemaRelationType[\"relationDates\"],\n eventDateTime: DateTimeWithPriceType[] | undefined,\n): SchemaRelationType[\"relationDates\"] {\n return relationDates.map((relationDate) => {\n // Check if this relationDate exists in the event's dateTime\n const existsInEvent =\n eventDateTime?.some(\n (dt) =>\n dt.startDate === relationDate.dateTime.startDate &&\n dt.startTime === relationDate.dateTime.startTime,\n ) ?? false;\n\n return {\n ...relationDate,\n status: existsInEvent\n ? relationDate.status\n : EnumInviteStatus.UNAVAILABLE,\n };\n });\n}\n"],"mappings":"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ACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC/EO,IAAM,mBAA+B;EAC1C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACpLO,IAAM,2BAAuC;EAClD;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC9JO,IAAM,oBAAgC;EAC3C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACnEO,IAAM,sBAAkC;EAC7C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC3FO,IAAM,4BAAwC;EACnD;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;IACF;EACF;AACF;ACpFO,IAAM,uBAAmC;EAC9C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;IACF;EACF;AACF;ACvGO,IAAM,eAA2B;EACtC;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACzEO,IAAM,oBAAgC;EAC3C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACtFO,IAAM,iBAAyC;EACpD,oBAAoB;EACpB,0BAA0B;EAC1B,kBAAkB;EAClB,2BAA2B;EAC3B,mBAAmB;EACnB,+BAA+B;EAC/B,6BAA6B;EAC7B,wBAAwB;EACxB,wBAAwB;EACxB,mBAAmB;AACrB;AAEA,IAAM,0BAA0B,CAAC,eAAuC;AACtE,QAAM,SAAS,WAAW,IAAI,CAAC,cAAc;IAC3C,GAAG;IACH,OAAO,eAAe,SAAS,EAAE;EACnC,EAAE;AACF,SAAO;AACT;AAEO,IAAM,sBAAsB,wBAAwB;EACzD,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;EACH,GAAG;AACL,CAAC;AG5BM,IAAM,oBAAoB;EAC/B;IAAA;;EAA+C,GAAG;IAChD,aAAa;IACb,OAAO;EACT;EACA;IAAA;;EAAgD,GAAG;IACjD,aAAa;IACb,OAAO;EACT;EACA;IAAA;;EAAuD,GAAG;IACxD,aAAa;IACb,OAAO;EACT;EACA;IAAA;;EAA4D,GAAG;IAC7D,aACE;IACF,OAAO;EACT;EACA;IAAA;;EAAmD,GAAG;IACpD,aACE;IACF,OAAO;EACT;EACA;IAAA;;EAA+D,GAAG;IAChE,aACE;IACF,OAAO;EACT;AACF;AAWO,SAAS,sBACd,YACA,WACqB;AACrB,QAAM,SAAS,kBAAkB,UAAU;AAE3C,SAAO;IACL;IACA,YAAY;IACZ,mBAAmB,OAAO;IAC1B;IACA,aAAa,OAAO;EACtB;AACF;;;AGrEO,SAAS,mBACd,oBACe;AACf,QAAM,aAAa,oBAAoB,KAAK,EAAE,YAAY;AAC1D,SAAO,cAAc;AACvB;;;ACKA,eAAsB,sBACpB,SACqB;AACrB,QAAM,EAAE,MAAM,SAAS,OAAO,MAAM,QAAQ,IAAI;AAChD,UAAQ,IAAI,qBAAqB,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AACjE,MAAI;AACF,UAAM,gBAAgB,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC7C;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE;AAGF,UAAM,kBAAkB,WAAW,aAAa;AAChD,YAAQ;AAAA,MACN,WAAW,cAAc,MAAM,sBAAsB,QAAQ,MAAM;AAAA,IACrE;AAEA,WAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;AAAA,EAC7B,SAAS,OAAO;AACd,YAAQ,MAAM,mCAAmC,KAAK;AACtD,WAAO,CAAC;AAAA,EAEV;AACF;;;ACpCA,SAAS,YAA6C;AAKtD,IAAM,OAAO,IAAI,KAAK;AAKtB,SAAS,yBAAyB,SAAoC;AACpE,SAAO,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,KAAK,CAAC,QAAQ,EAAE;AAC7D;AAYA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAGE;AACA,QAAM,WAA8B,CAAC;AACrC,QAAM,gBAA0B,CAAC;AAEjC,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,KAAK,gBAAgB,KAAK,GAAG;AAChC,oBAAc,KAAK,KAAK;AACxB;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,MAAM,EAAE,GAAG,KAAK;AAAA,MAChB,OAAO;AAAA,MACP;AAAA,MACA,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,eAAe,SAAS;AACnC;AAKA,SAAS,oBACP,SACA,OACkD;AAClD,MAAI,eAAe;AACnB,QAAM,eAAyB,CAAC;AAEhC,aAAW,CAAC,aAAa,MAAM,KAAK,QAAQ,QAAQ,GAAG;AACrD,QAAI,OAAO,WAAW,SAAS;AAC7B,YAAM,UAAU,MAAM,WAAW;AACjC,UAAI,SAAS;AACX,cAAM,SAAS,yBAAyB,OAAO;AAC/C,YAAI,OAAO,SAAS,UAAU,uBAAuB;AACnD,uBAAa,KAAK,GAAG,MAAM;AAAA,QAC7B;AACA,gBAAQ,IAAI,2BAA2B;AAAA,UACrC,OAAO,OAAO,SAAS;AAAA,UACvB;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,cAAc,aAAa;AACtC;AAKA,eAAe,UACb,OACA,YAC2D;AAC3D,MAAI;AACF,UAAM,UAAU,MAAM,KAAK,2BAA2B,KAAK;AAC3D,UAAM,EAAE,cAAc,aAAa,IAAI,oBAAoB,SAAS,KAAK;AAEzE,YAAQ;AAAA,MACN,SAAS,aAAa,CAAC,UAAU,YAAY,IAAI,MAAM,MAAM;AAAA,IAC/D;AAEA,WAAO,EAAE,cAAc,aAAa;AAAA,EACtC,SAAS,OAAO;AACd,YAAQ,IAAI,8CAA8C;AAAA,MACxD;AAAA,MACA,WAAW,MAAM;AAAA,MACjB,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,IAC9D,CAAC;AACD,WAAO,EAAE,cAAc,CAAC,GAAG,cAAc,EAAE;AAAA,EAC7C;AACF;AAEA,eAAsB,sBAAsB;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsC;AACpC,QAAM,aAAa,MAAM,eAAe,KAAK,EAAE,QAAQ,EAAE,KAAK,QAAQ,EAAE,CAAC;AACzE,QAAM,aAAa,WAAW,IAAI,CAAC,UAAU,MAAM,KAAK;AAExD,MAAI,CAAC,KAAM;AAEX,QAAM,EAAE,UAAU,cAAc,IAAI,mBAAmB;AAAA,IACrD;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,CAAC;AAGD,MAAI,cAAc,SAAS,GAAG;AAC5B,YAAQ,IAAI,SAAS,cAAc,MAAM,sBAAsB;AAAA,EACjE;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,kDAAkD;AAC9D;AAAA,EACF;AAGA,QAAM,SAAS,KAAK,uBAAuB,QAAQ;AACnD,MAAI,oBAAoB;AACxB,QAAM,kBAA4B,CAAC;AAEnC,aAAW,CAAC,YAAY,KAAK,KAAK,OAAO,QAAQ,GAAG;AAClD,UAAM,EAAE,cAAc,aAAa,IAAI,MAAM;AAAA,MAC3C;AAAA,MACA,aAAa;AAAA,IACf;AACA,yBAAqB;AACrB,oBAAgB,KAAK,GAAG,YAAY;AAAA,EACtC;AAGA,UAAQ;AAAA,IACN,6BAA6B,iBAAiB,IAAI,SAAS,MAAM,kBAAkB,OAAO,MAAM;AAAA,EAClG;AAEA,MAAI,gBAAgB,SAAS,GAAG;AAC9B,YAAQ,IAAI,SAAS,gBAAgB,MAAM,qBAAqB;AAAA,EAClE;AACF;;;ACtJA,SAAS,2BACP,QACS;AACT,MAAI,CAAC,QAAQ,eAAgB,QAAO;AAEpC,UACG,OAAO,WAAW,uBAAuB,UACxC,OAAO,WAAW,uBAAuB,cAC1C,OAAO,gBAAgB,gBAAgB,cACtC,OAAO,gBAAgB,gBAAgB;AAE7C;AAUO,SAAS,yCACd,UACA,MAAY,oBAAI,KAAK,GACrB,QACgC;AAChC,QAAM,aAAa,IAAI;AAAA,KACpB,YAAY,CAAC,GACX;AAAA,MACC,CAAC,YAAY,IAAI,KAAK,QAAQ,UAAU,EAAE,QAAQ,IAAI,IAAI,QAAQ;AAAA,IACpE,EACC,IAAI,CAAC,YAAY,QAAQ,WAAW;AAAA,EACzC;AAEA,MACE,WAAW,IAAI,gBAAgB,UAAU,KACzC,WAAW,IAAI,gBAAgB,eAAe,GAC9C;AACA,QAAI,2BAA2B,MAAM,GAAG;AACtC,aAAO,wBAAwB;AAAA,IACjC;AACA,WAAO,wBAAwB;AAAA,EACjC;AAEA,MAAI,WAAW,IAAI,gBAAgB,eAAe,GAAG;AACnD,WAAO,wBAAwB;AAAA,EACjC;AAEA,SAAO;AACT;AAGO,SAAS,kCAAkC,MAAY,oBAAI,KAAK,GAGrE;AACA,QAAM,cAAc,IAAI;AAAA,IACtB,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,GAAG,CAAC;AAAA,EACrD;AACA,QAAM,YAAY,IAAI;AAAA,IACpB,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,IAAI,GAAG,CAAC;AAAA,EACzD;AACA,SAAO,EAAE,WAAW,YAAY;AAClC;;;ACvBA,SAAS,yBAAyB,UAA0C;AAC1E,SACE,SAAS,iBAAiB,iBAAiB,UAC3C,SAAS,mBAAmB,QAC5B,SAAS,qBAAqB;AAElC;AAEA,eAAe,0CAEb;AACA,SAAQ,MAAM,eAAe,KAAK;AAAA,IAChC,oBAAoB;AAAA,MAClB,YAAY;AAAA,QACV,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,QACnB,cAAc,iBAAiB;AAAA,MACjC;AAAA,IACF;AAAA,IACA,WAAW;AAAA,EACb,CAAC,EACE,OAAO,4CAA4C,EACnD,KAAK,EACL,KAAK;AACV;AAEA,eAAe,yBAAyB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AACF,GAIqB;AACnB,QAAM,QAAQ,MAAM,YAAY,OAAO;AAAA,IACrC,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,YAAY;AAAA,EACd,CAAC,EAAE,KAAK;AAER,SAAO,QAAQ,KAAK;AACtB;AAEA,eAAe,4CACb,mBACA,KACyC;AACzC,QAAM,cAAc,MAAM,UAAU,SAAS,iBAAiB,EAC3D,OAAO,iBAAiB,EACxB,KAAK,EACL,KAAK;AAER,SAAO;AAAA,IACL,aAAa;AAAA,IACb;AAAA,IACA,aAAa;AAAA,EACf;AACF;AAEA,eAAe,yCAAyC;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKkB;AAChB,QAAM,UAAU;AAAA,IACd,MAAM;AAAA,MACJ,YAAY,OAAO,WAAW;AAAA,MAC9B;AAAA,MACA,cAAc,6BAA6B;AAAA,IAC7C;AAAA,IACA,SAAS,cAAc,WAAW,kCAAkC,YAAY;AAAA,IAChF,OAAO;AAAA,IACP,MAAM,qBAAqB;AAAA,IAC3B,SAAS,CAAC,oBAAgC;AAAA,EAC5C;AAEA,MAAI;AACF,UAAM,sBAAsB,OAAO;AACnC,UAAM,sBAAsB,OAAO;AAAA,EACrC,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,qEAAqE,OAAO,WAAW,CAAC;AAAA,MACxF,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AAAA,EACF;AACF;AAEA,eAAe,2BAA2B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMmC;AACjC,QAAM,SAAS,sBAAsB,YAAY,SAAS;AAE1D,QAAM,eAAe,MAAM,eAAe;AAAA,IACxC;AAAA,MACE,KAAK,UAAU;AAAA,MACf,oBAAoB;AAAA,QAClB,YAAY;AAAA,UACV,gBAAgB;AAAA,UAChB,mBAAmB;AAAA,UACnB,YAAY,SAAS;AAAA,UACrB,cAAc,iBAAiB;AAAA,UAC/B,SAAS;AAAA,YACP,MAAM;AAAA,cACJ,YAAY;AAAA,gBACV,WAAW;AAAA,kBACT,MAAM,OAAO;AAAA,kBACb,KAAK,OAAO;AAAA,gBACd;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MACA,WAAW;AAAA,IACb;AAAA,IACA;AAAA,MACE,MAAM,EAAE,eAAe,OAAO,YAAY;AAAA,MAC1C,OAAO,EAAE,gCAAgC,OAAO;AAAA,IAClD;AAAA,EACF,EAAE,KAAK;AAEP,MAAI,aAAa,kBAAkB,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,yCAAyC;AAAA,IAC7C,aAAa,UAAU;AAAA,IACvB,sBAAsB,UAAU,MAAM;AAAA,IACtC,cAAc,SAAS;AAAA,IACvB,aAAa,OAAO;AAAA,EACtB,CAAC;AAED,SAAO;AACT;AAEA,eAAe,+BAA+B;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOmC;AACjC,QAAM,iBAAiB,MAAM,yBAAyB;AAAA,IACpD;AAAA,IACA,YAAY,SAAS;AAAA,IACrB,qBAAqB,SAAS,cAAc;AAAA,EAC9C,CAAC;AACD,MAAI,CAAC,gBAAgB;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,MAAM;AAAA,IACvB,SAAS,cAAc;AAAA,IACvB;AAAA,EACF;AACA,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,SAAO,2BAA2B;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAuBA,eAAsB,wCACpB,MAAY,oBAAI,KAAK,GACmC;AACxD,QAAM,SAAS,kCAAkC,GAAG;AACpD,QAAM,YAAY;AAClB,QAAM,aAAa,MAAM,wCAAwC;AAEjE,MAAI,UAAU;AACd,MAAI,UAAU;AACd,QAAM,YAAY,oBAAI,IAAY;AAElC,aAAW,aAAa,YAAY;AAClC,UAAM,gBAAgB,mBAAmB,UAAU,aAAa;AAChE,QAAI,CAAC,eAAe;AAClB;AAAA,IACF;AAEA,eAAW,YAAY,UAAU,oBAAoB;AACnD,UAAI,CAAC,yBAAyB,QAAQ,GAAG;AACvC;AAAA,MACF;AAEA,YAAM,UAAU,GAAG,OAAO,UAAU,GAAG,CAAC,IAAI,OAAO,SAAS,UAAU,CAAC;AACvE,UAAI,UAAU,IAAI,OAAO,GAAG;AAC1B;AAAA,MACF;AACA,gBAAU,IAAI,OAAO;AAErB,YAAM,UAAU,MAAM,+BAA+B;AAAA,QACnD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,UAAI,YAAY,WAAW;AACzB,mBAAW;AAAA,MACb,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,QAAQ;AAC5B;;;AC9SA,eAAsB,yBAAyB,WAA0B;AACvE,SAAO,eAAe,QAAQ;AAAA,IAC5B,eAAe;AAAA,IACf,WAAW;AAAA,EACb,CAAC,EAAE,KAAK;AACV;;;ACRO,SAAS,6BACd,YACiB;AACjB,QAAM,cAAc,cAAc,CAAC,GAChC,IAAI,CAAC,SAAS,mBAAmB,IAAI,CAAC,EACtC,OAAO,CAAC,SAAgC,SAAS,IAAI;AAExD,SAAO,CAAC,GAAG,IAAI,IAAI,UAAU,CAAC;AAChC;;;AClBA,OAAO,cAAc;AAMd,IAAM,oBAAoB,OAAO;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAMM;AACJ,MAAI;AAEF,UAAM,WAAW,aACb;AAAA;AAAA,MAEA,iBAAiB,MAAM,IAAI,UAAU,IAAI,MAAM,qDAAqD,OAAO;AAAA;AAE/G,UAAM,SAAS,QAAQ,QAAQ;AAE/B,UAAM,iBAAiB,aAAa,kBAAkB;AACtD,YAAQ;AAAA,MACN,GAAG,cAAc;AAAA,IACnB;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,gCAAgC,GAAG;AACjD,UAAM;AAAA,EACR;AACF;;;ACnBA,eAAsB,0BACpB,QACA,QACA;AACA,MAAI;AAEF,UAAM,oBAAoB,MAAM,kBAAkB,KAAK;AAAA,MACrD;AAAA,IACF,CAAC,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC;AAGzB,UAAM,CAAC,OAAO,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,MACxC,kBAAkB,eAAe,EAAE,OAAO,CAAC;AAAA,MAC3C,kBAAkB,eAAe,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,IAC5D,CAAC;AAGD,WAAO,qDAA4C;AAAA,MACjD,kBAAkB;AAAA,MAClB,wBAAwB;AAAA,IAC1B,CAAC;AAED,WAAO,iEAAkD;AAAA,MACvD,uBAAuB,EAAE,OAAO,QAAQ,OAAO;AAAA,IACjD,CAAC;AAED,YAAQ,IAAI,2CAA2C,OAAO,MAAM,CAAC,EAAE;AAAA,EACzE,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,kDAAkD,OAAO,MAAM,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACF;AAWA,eAAsB,YAAY;AAAA,EAChC;AAAA,EACA;AACF,GAGqB;AACnB,MAAI;AACF,UAAM,sBAAsB,OAAO;AAEnC,UAAM,gBAAgB,MAAM,sBAAsB,OAAO;AAGzD,UAAM,YAAY,QAAQ,QAAQ,WAAW,KAAK,cAAc,SAAS;AAEzE,QAAI,QAAQ;AACV,iBAAW,UAAU,eAAe;AAClC,cAAM,0BAA0B,QAAQ,MAAM;AAAA,MAChD;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,yBAAyB,KAAK;AAC5C,WAAO;AAAA,EACT;AACF;;;AChFA,eAAsB,mBAAkC;AACtD,QAAM,MAAM,oBAAI,KAAK;AAGrB,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC;AAAA,MACE,KAAK;AAAA,QACH,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE;AAAA,QAC5B,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE;AAAA,QAC1B,EAAE,OAAO,EAAE,KAAK,CAAC,UAAU,MAAM,EAAE,EAAE;AAAA,MACvC;AAAA,MACA,QAAQ,EAAE,KAAK,aAAa,OAAO;AAAA,IACrC;AAAA,IACA,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAGA,QAAM,gBAAgB,MAAM,QAAQ;AAAA,IAClC,EAAE,KAAK,EAAE,MAAM,IAAI,GAAG,QAAQ,EAAE,KAAK,aAAa,QAAQ,EAAE;AAAA,IAC5D,EAAE,MAAM,EAAE,QAAQ,aAAa,QAAQ,EAAE;AAAA,EAC3C;AAGA,QAAM,eAAe,MAAM,QAAQ;AAAA,IACjC;AAAA,MACE,KAAK,EAAE,KAAK,IAAI;AAAA,MAChB,OAAO,EAAE,MAAM,IAAI;AAAA,MACnB,QAAQ,EAAE,KAAK,aAAa,OAAO;AAAA,IACrC;AAAA,IACA,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAGA,QAAM,eAAe,MAAM,QAAQ;AAAA,IACjC,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,QAAQ,EAAE,KAAK,aAAa,OAAO,EAAE;AAAA,IAC5D,EAAE,MAAM,EAAE,QAAQ,aAAa,OAAO,EAAE;AAAA,EAC1C;AAEA,UAAQ;AAAA,IACN,uCAAkC,cAAc,aAAa,aAAa,cAAc,aAAa,YAAY,aAAa,aAAa,YAAY,aAAa,aAAa;AAAA,EACnL;AACF;;;AC3CA,eAAsB,+BACpB,QACA,aACe;AACf,MAAI;AAIF,UAAM,OAAO,MAAM,UAAU,SAAS,MAAM,EACzC,OAAO,QAAQ,EACf,KAAK,EACL,KAAK;AAER,QAAI,CAAC,MAAM,QAAQ;AACjB,cAAQ,KAAK,6CAA6C,MAAM,EAAE;AAClE;AAAA,IACF;AAKA,UAAM,SAAS,MAAM,YAAY,SAAS,KAAK,MAAM,EAClD,KAAuB,EACvB,KAAK;AAER,QAAI,CAAC,QAAQ;AACX,cAAQ,KAAK,0CAA0C,KAAK,MAAM,EAAE;AACpE;AAAA,IACF;AAKA,UAAM,aAAwC,CAAC;AAE/C,UAAM,mBAAmB,gBAAgB,gBAAgB;AAEzD,QAAI,kBAAkB;AACpB,iBAAW,eAAe;AAAA,QACxB,WAAW;AAAA,QACX,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAEA,iBAAW,WAAW;AAAA,QACpB,QAAQ;AAAA,QACR,cAAc,OAAO,UAAU,gBAAgB,CAAC;AAAA,MAClD;AAEA,iBAAW,WAAW;AAAA,QACpB,QAAQ;AAAA,QACR,cAAc,OAAO,UAAU,gBAAgB,CAAC;AAAA,MAClD;AAAA,IACF;AAOA,eAAW,UAAU,OAAO,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW;AAAA,MAC/D,GAAG;AAAA,MACH,QAAQ,mBAAmB,QAAQ,IAAI;AAAA,IACzC,EAAE;AAKF,UAAM,YAAY,UAAU,EAAE,KAAK,OAAO,IAAI,GAAG,EAAE,MAAM,WAAW,CAAC;AAAA,EACvE,SAAS,OAAO;AACd,YAAQ,MAAM,4CAA4C,KAAK;AAAA,EACjE;AACF;;;AC7EA,OAAOC,eAAc;AAKd,SAAS,gBAAgB,OAAwB;AACtD,SAAOA,UAAS,MAAM,SAAS,QAAQ,KAAK;AAC9C;AAMO,SAAS,0BAA0B,KAAe;AACvD,MAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,WAAO;AAAA,EACT;AAEA,MAAI,eAAeA,UAAS,MAAM,UAAU;AAC1C,WAAO,IAAI,SAAS;AAAA,EACtB;AAEA,MAAI,eAAe,MAAM;AACvB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI,IAAI,yBAAyB;AAAA,EAC1C;AAEA,MAAI,OAAO,QAAQ,UAAU;AAC3B,UAAM,YAAiB,CAAC;AACxB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,gBAAU,GAAG,IAAI,0BAA0B,KAAK;AAAA,IAClD;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;ACjCA,OAAOC,YAAW;AAClB,OAAOC,wBAAuB;AAC9B,OAAO,aAAa;AAKpBC,OAAM,OAAOC,kBAAiB;AAC9BD,OAAM,OAAO,OAAO;AAQb,SAAS,qBACd,eACA,KACqB;AACrB,QAAM,kBAAkB,cAAc,KAAK,KAAK,QAAQ,IAAI;AAC5D,MAAI,kBAAkB,KAAK,mBAAmB,GAAG;AAC/C,WAAO,oBAAoB;AAAA,EAC7B;AAEA,MAAI,cAAc,OAAO,KAAK,KAAK,GAAG;AACpC,WAAO,oBAAoB;AAAA,EAC7B;AAEA,MAAI,cAAc,OAAO,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG;AAClD,WAAO,oBAAoB;AAAA,EAC7B;AAEA,MAAI,cAAc,OAAO,KAAK,SAAS,GAAG;AACxC,WAAO,oBAAoB;AAAA,EAC7B;AAEA,MAAI,cAAc,OAAO,IAAI,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG;AACvD,WAAO,oBAAoB;AAAA,EAC7B;AAEA,SAAO,oBAAoB;AAC7B;AAOO,SAAS,2BACd,UACA,MAAmBA,OAAM,GACtB;AAEH,QAAM,iBAAiB,GAAG,UAAU,IAAI,UAAU;AAClD,QAAM,gBAAgBA;AAAA,IACpB,GAAG,SAAS,SAAS,IAAI,SAAS,SAAS;AAAA,IAC3C;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAcA;AAAA,IAClB,GAAG,SAAS,OAAO,IAAI,SAAS,OAAO;AAAA,IACvC;AAAA,IACA;AAAA,EACF;AAGA,MAAI,CAAC,cAAc,QAAQ,KAAK,CAAC,YAAY,QAAQ,GAAG;AACtD,WAAO;AAAA,MACL,GAAG;AAAA,MACH,YAAY,oBAAoB;AAAA,IAClC;AAAA,EACF;AAEA,MAAI,YAAY,SAAS,aAAa,GAAG;AACvC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,YAAY,oBAAoB;AAAA,IAClC;AAAA,EACF;AAEA,MAAI;AAEJ,MAAI,YAAY,QAAQ,GAAG,GAAG;AAC5B,QAAI,cAAc,QAAQ,GAAG,GAAG;AAE9B,mBAAa,qBAAqB,eAAe,GAAG;AAAA,IACtD,OAAO;AAEL,mBAAa,oBAAoB;AAAA,IACnC;AAAA,EACF,OAAO;AAEL,iBAAa,oBAAoB;AAAA,EACnC;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,EACF;AACF;AAEA,IAAM,uBAAuB;AAAA,EAC3B,UAAU,EAAE,KAAK,CAAC,GAAG,OAAO,QAAQ;AAAA,EACpC,WAAW;AACb;AAEA,IAAM,oBAAoB;AA6B1B,SAAS,yBACP,QACA,SACS;AACT,MAAI,OAAO,WAAW,QAAQ,QAAQ;AACpC,WAAO;AAAA,EACT;AAEA,SAAO,OAAO;AAAA,IACZ,CAAC,MAAM,UAAU,KAAK,eAAe,QAAQ,KAAK,EAAE;AAAA,EACtD;AACF;AAEA,eAAe,4BACb,OACA,KACiB;AACjB,MAAI,UAAU;AACd,QAAM,SAAS,MACZ,KAAK,oBAAoB,EACzB,OAAO,EAAE,KAAK,GAAG,UAAU,EAAE,CAAC,EAC9B,KAAK,EACL,OAAO,EAAE,WAAW,kBAAkB,CAAC;AAE1C,MAAI,UAAiC,CAAC;AAEtC,QAAM,kBAAkB,YAA2B;AACjD,QAAI,CAAC,QAAQ,QAAQ;AACnB;AAAA,IACF;AAEA,UAAM,MAAM,UAAU,OAAO;AAC7B,eAAW,QAAQ;AACnB,cAAU,CAAC;AAAA,EACb;AAEA,MAAI;AACF,qBAAiB,OAAO,QAAQ;AAC9B,YAAM,WAAW,IAAI,SAAS;AAAA,QAAI,CAAC,SACjC,2BAA2B,MAAM,GAAG;AAAA,MACtC;AAEA,UAAI,CAAC,yBAAyB,IAAI,UAAU,QAAQ,GAAG;AACrD;AAAA,MACF;AAEA,cAAQ,KAAK;AAAA,QACX,WAAW;AAAA,UACT,QAAQ,EAAE,KAAK,IAAI,IAAI;AAAA,UACvB,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE;AAAA,QAC/B;AAAA,MACF,CAAC;AAED,UAAI,QAAQ,UAAU,mBAAmB;AACvC,cAAM,gBAAgB;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,gBAAgB;AAAA,EACxB,UAAE;AACA,UAAM,OAAO,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,EAC5C;AAEA,SAAO;AACT;AAKA,eAAsB,iCAAgD;AACpE,QAAM,MAAMA,OAAM;AAClB,QAAM,CAAC,YAAY,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IAClD,4BAA4B,YAAsC,GAAG;AAAA,IACrE;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACF,CAAC;AAED,UAAQ;AAAA,IACN,kDAA6C,UAAU,qCAAqC,WAAW;AAAA,EACzG;AACF;;;AC9MA,eAAsB,8BACpB,YACwB;AACxB,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,0BAA0B,UAAU;AAEzD,QAAM,CAAC,UAAU,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,IACtD,WAAW,SAAS,YAAY,EAC7B,OAAO,UAAU,EACjB,KAA8C,EAC9C,KAAK;AAAA,IACR,0BAA0B,SAAS,YAAY,EAC5C,OAAO,UAAU,EACjB,KAA8C,EAC9C,KAAK;AAAA,EACV,CAAC;AAED,SAAO,YAAY;AACrB;;;ACzBO,SAAS,uBACd,kBACA,cACS;AACT,MAAI,CAAC,kBAAkB,QAAQ;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,iBAAiB;AAAA,IACtB,CAAC,SACC,CAAC,cAAc;AAAA,MACb,CAAC,SACC,KAAK,cAAc,KAAK,aACxB,KAAK,cAAc,KAAK;AAAA,IAC5B;AAAA,EACJ;AACF;AAMO,SAAS,iCACd,eACA,eACqC;AACrC,SAAO,cAAc,IAAI,CAAC,iBAAiB;AAEzC,UAAM,gBACJ,eAAe;AAAA,MACb,CAAC,OACC,GAAG,cAAc,aAAa,SAAS,aACvC,GAAG,cAAc,aAAa,SAAS;AAAA,IAC3C,KAAK;AAEP,WAAO;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,gBACJ,aAAa,SACb,iBAAiB;AAAA,IACvB;AAAA,EACF,CAAC;AACH;","names":["EnumEventDateStatus","dayjs","EnumInviteStatus","mongoose","dayjs","customParseFormat","dayjs","customParseFormat"]}
1
+ {"version":3,"sources":["../../src/service/promoCode/constants.ts","../../src/service/affiliate/activeAffiliateCodeExists.ts","../../node_modules/@timardex/cluemart-shared/src/generated/graphql.ts","../../node_modules/@timardex/cluemart-shared/src/utils/mapArrayToOptions.ts","../../node_modules/@timardex/cluemart-shared/src/utils/date.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/relationShareTypes.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/constants.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/joinShareDescriptionSections.ts","../../node_modules/@timardex/cluemart-shared/src/sharing/normalizeShareDescription.ts","../../node_modules/@timardex/cluemart-shared/src/types/subscription.ts","../../node_modules/@timardex/cluemart-shared/src/auth/permissions/permissions.ts","../../node_modules/@timardex/cluemart-shared/src/auth/permissions/rolePermissions.ts","../../node_modules/@timardex/cluemart-shared/src/auth/permissions/helpers.ts","../../node_modules/@timardex/cluemart-shared/src/auth/permissions/adminAppAccess.ts","../../node_modules/@timardex/cluemart-shared/src/types/global.ts","../../node_modules/@timardex/cluemart-shared/src/types/userAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/types/eventAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/types/vendorAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/types/partnerAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/types/game/dailyClue.ts","../../node_modules/@timardex/cluemart-shared/src/types/game/global.ts","../../node_modules/@timardex/cluemart-shared/src/types/schoolAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/types/affiliateAdapters.ts","../../node_modules/@timardex/cluemart-shared/src/enums/clientEnums.ts","../../node_modules/@timardex/cluemart-shared/src/utils/dailyClueGame.ts","../../node_modules/@timardex/cluemart-shared/src/utils/utils.ts","../../node_modules/@timardex/cluemart-shared/src/utils/resourceImage.ts","../../node_modules/@timardex/cluemart-shared/src/utils/school.ts","../../node_modules/@timardex/cluemart-shared/src/utils/eventDateStatus.ts","../../node_modules/@timardex/cluemart-shared/src/calendar/eventCalendar.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/clothingAndFashion.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/electronicsAndTechnology.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/foodAndBeverages.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/handmadeAndLocalProducts.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/healthAndWellness.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/homeGardenHousehold.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/petProductsAndAnimalGoods.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/serviceAndExperience.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/toysChildren.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/vintageAndAntique.ts","../../node_modules/@timardex/cluemart-shared/src/formFields/categories/index.ts","../../node_modules/@timardex/cluemart-shared/src/eventStallholders/eventStallholderFilters.ts","../../node_modules/@timardex/cluemart-shared/src/vendorEvents/vendorEventFilters.ts","../../node_modules/@timardex/cluemart-shared/src/utils/affiliate.ts","../../node_modules/@timardex/cluemart-shared/src/utils/irdNumber.ts","../../node_modules/@timardex/cluemart-shared/src/utils/toUserFacingErrorMessage.ts","../../src/service/promoCode/normalizePromoCode.ts","../../src/service/saveNotificationsInDb.ts","../../src/service/sendPushNotifications.ts","../../src/service/affiliate/vendorSubscriptionRewards.ts","../../src/service/affiliate/awardAffiliateVendorSubscriptionRewards.ts","../../src/service/affiliate/findAffiliateByPromoCode.ts","../../src/service/affiliate/normalizeAffiliatePromoCodes.ts","../../src/service/database.ts","../../src/service/notifyUsers.ts","../../src/service/updateAdStatus.ts","../../src/service/vendor.ts","../../src/service/objectIdToString.ts","../../src/service/event/updateAllEventDateTimeStatuses.ts","../../src/service/event/findEventOrImportedMarketById.ts","../../src/service/relations.ts"],"sourcesContent":["/** Matches partial unique indexes for promo/affiliate codes on active documents. */\nexport const ACTIVE_NOT_DELETED_FILTER = {\n active: true,\n deletedAt: null,\n} as const;\n","import { AffiliateModel } from \"src/mongoose\";\n\nimport { ACTIVE_NOT_DELETED_FILTER } from \"../promoCode/constants\";\n\n/**\n * Checks whether an affiliate code is already taken by an active, non-deleted affiliate.\n *\n * Used when generating new affiliate codes so collisions are avoided before insert.\n * Matches the partial unique index on `affiliateCode` for active documents.\n *\n * @param affiliateCode - Candidate affiliate promo code to check.\n * @returns `true` when an active, non-deleted affiliate already uses this code.\n */\nexport async function activeAffiliateCodeExists(\n affiliateCode: string,\n): Promise<boolean> {\n const existing = await AffiliateModel.exists({\n ...ACTIVE_NOT_DELETED_FILTER,\n affiliateCode,\n }).exec();\n\n return existing !== null;\n}\n","import { PosterAssetId } from '../images';\nexport type Maybe<T> = T | null;\nexport type InputMaybe<T> = Maybe<T>;\n/** All built-in and custom scalars, mapped to their actual values */\nexport type Scalars = {\n ID: { input: string; output: string; }\n String: { input: string; output: string; }\n Boolean: { input: boolean; output: boolean; }\n Int: { input: number; output: number; }\n Float: { input: number; output: number; }\n Date: { input: string; output: string; }\n PosterAssetId: { input: PosterAssetId; output: PosterAssetId; }\n Upload: { input: any; output: any; }\n};\n\n/** Activity enum type */\nexport enum ActivityEnumType {\n Favorite = 'FAVORITE',\n Going = 'GOING',\n Interested = 'INTERESTED',\n Present = 'PRESENT',\n View = 'VIEW'\n}\n\n/** Ad input type */\nexport type AdInputType = {\n active: Scalars['Boolean']['input'];\n end: Scalars['Date']['input'];\n resource: AdResourceInputType;\n showOn: Array<InputMaybe<AdShowOnEnum>>;\n start: Scalars['Date']['input'];\n status: AdStatusTypeEnum;\n targetRegion: Array<InputMaybe<Scalars['String']['input']>>;\n};\n\n/** Ad resource input type */\nexport type AdResourceInputType = {\n adDescription: Scalars['String']['input'];\n adImage: Scalars['String']['input'];\n adStyle: AdStyleEnum;\n adTitle: Scalars['String']['input'];\n adType: AdTypeEnum;\n resourceId: Scalars['String']['input'];\n resourceName: Scalars['String']['input'];\n resourceRegion: Scalars['String']['input'];\n resourceSlug: Scalars['String']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Ad resource type */\nexport type AdResourceType = {\n adDescription: Scalars['String']['output'];\n adImage: Scalars['String']['output'];\n adStyle: AdStyleEnum;\n adTitle: Scalars['String']['output'];\n adType: AdTypeEnum;\n resourceId: Scalars['String']['output'];\n resourceName: Scalars['String']['output'];\n resourceRegion: Scalars['String']['output'];\n resourceSlug: Scalars['String']['output'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Ad show on enum */\nexport enum AdShowOnEnum {\n EventsPage = 'Events_page',\n FrontPage = 'Front_page',\n PartnersPage = 'Partners_page',\n VendorsPage = 'Vendors_page'\n}\n\n/** Ad status type enum */\nexport enum AdStatusTypeEnum {\n Active = 'Active',\n Expired = 'Expired',\n Paused = 'Paused'\n}\n\n/** Ad style enum */\nexport enum AdStyleEnum {\n Bloom = 'Bloom',\n Rise = 'Rise'\n}\n\n/** Ad type */\nexport type AdType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n clicks?: Maybe<Scalars['Int']['output']>;\n createdAt: Scalars['Date']['output'];\n end: Scalars['Date']['output'];\n impressions?: Maybe<Scalars['Int']['output']>;\n resource: AdResourceType;\n showOn: Array<Maybe<AdShowOnEnum>>;\n start: Scalars['Date']['output'];\n status: AdStatusTypeEnum;\n targetRegion: Array<Maybe<Scalars['String']['output']>>;\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Ad type enum */\nexport enum AdTypeEnum {\n Free = 'Free',\n Sponsored = 'Sponsored'\n}\n\nexport type AddParticipantToChatResponse = {\n data?: Maybe<ChatType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AddUserFavouriteResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AddUserGoingResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AddUserInterestResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AddUserPresentResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AdminPermanentlyDeleteResourceResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AdminResendUserVerificationEmailResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AdminUpdateResourceInputType = {\n active: Scalars['Boolean']['input'];\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Admin update resource type */\nexport type AdminUpdateResourceType = {\n active: Scalars['Boolean']['output'];\n resourceId: Scalars['ID']['output'];\n resourceType: ResourceTypeEnum;\n};\n\nexport type AdminUpdateResourceTypeResponse = {\n data?: Maybe<AdminUpdateResourceType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AffiliateBankAccountDetailsInputType = {\n accountHolderName: Scalars['String']['input'];\n accountNumber: Scalars['String']['input'];\n};\n\n/** Affiliate bank account details type */\nexport type AffiliateBankAccountDetailsType = {\n accountHolderName: Scalars['String']['output'];\n accountNumber: Scalars['String']['output'];\n};\n\nexport type AffiliateContactDetailsInputType = {\n mobilePhone: Scalars['String']['input'];\n};\n\n/** Affiliate contact details type */\nexport type AffiliateContactDetailsType = {\n mobilePhone: Scalars['String']['output'];\n};\n\nexport type AffiliateDetailsInputType = {\n bankAccountDetails: AffiliateBankAccountDetailsInputType;\n contactDetails: AffiliateContactDetailsInputType;\n irdNumber: Scalars['String']['input'];\n location: LocationInputType;\n participantType: AffiliateParticipantTypeEnum;\n socialMedia?: InputMaybe<Array<InputMaybe<SocialMediaInputType>>>;\n termsAgreement: TermsAgreementInputType;\n};\n\n/** Affiliate details type */\nexport type AffiliateDetailsType = {\n bankAccountDetails: AffiliateBankAccountDetailsType;\n contactDetails: AffiliateContactDetailsType;\n email: Scalars['String']['output'];\n firstName: Scalars['String']['output'];\n irdNumber: Scalars['String']['output'];\n lastName: Scalars['String']['output'];\n location: LocationType;\n participantType: AffiliateParticipantTypeEnum;\n socialMedia?: Maybe<Array<SocialMediaType>>;\n termsAgreement: TermsAgreementType;\n};\n\n/** Affiliate participant type enum */\nexport enum AffiliateParticipantTypeEnum {\n Company = 'COMPANY',\n Individual = 'INDIVIDUAL',\n SoleTrader = 'SOLE_TRADER'\n}\n\n/** Affiliate resource type */\nexport type AffiliateResourceType = {\n resourceActive: Scalars['Boolean']['output'];\n resourceDeletedAt?: Maybe<Scalars['Date']['output']>;\n resourceId: Scalars['ID']['output'];\n resourceName: Scalars['String']['output'];\n resourceOwner: OwnerType;\n resourceType: ResourceTypeEnum;\n rewards: Array<AffiliateRewardType>;\n};\n\n/** Affiliate reward type enum */\nexport enum AffiliateRewardEnumType {\n ActiveEventWithVendorRegistrations = 'ACTIVE_EVENT_WITH_VENDOR_REGISTRATIONS',\n ActiveVendorBonusReward = 'ACTIVE_VENDOR_BONUS_REWARD',\n ActiveVendorProSubscription = 'ACTIVE_VENDOR_PRO_SUBSCRIPTION',\n ActiveVendorStandardSubscription = 'ACTIVE_VENDOR_STANDARD_SUBSCRIPTION',\n NewEventRegistration = 'NEW_EVENT_REGISTRATION',\n NewVendorRegistration = 'NEW_VENDOR_REGISTRATION'\n}\n\n/** Affiliate reward type */\nexport type AffiliateRewardType = {\n createdAt: Scalars['Date']['output'];\n redeemedAt?: Maybe<Scalars['Date']['output']>;\n rewardDescription: Scalars['String']['output'];\n rewardType: AffiliateRewardEnumType;\n rewardValue: Scalars['Int']['output'];\n};\n\n/** Affiliate type */\nexport type AffiliateType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n affiliateBonusRewards: Array<AffiliateRewardType>;\n affiliateCode?: Maybe<Scalars['String']['output']>;\n affiliateDetails?: Maybe<AffiliateDetailsType>;\n affiliateResources: Array<AffiliateResourceType>;\n approvedAt?: Maybe<Scalars['Date']['output']>;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n overallPoints: Scalars['Int']['output'];\n owner: OwnerType;\n redeemHistory: Array<RedeemHistoryType>;\n updatedAt?: Maybe<Scalars['Date']['output']>;\n};\n\nexport type AppSettingsInputType = {\n appVersion: Scalars['String']['input'];\n isOfflineMode: Scalars['Boolean']['input'];\n minimumRedeemableAffiliatePoints: Scalars['Int']['input'];\n};\n\n/** App settings type */\nexport type AppSettingsType = {\n _id: Scalars['String']['output'];\n appVersion: Scalars['String']['output'];\n createdAt: Scalars['Date']['output'];\n isOfflineMode: Scalars['Boolean']['output'];\n key: Scalars['String']['output'];\n minimumRedeemableAffiliatePoints: Scalars['Int']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\nexport type AssignAffiliateBonusRewardResponse = {\n data?: Maybe<AffiliateType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type AssignAffiliateResourceRewardResponse = {\n data?: Maybe<AffiliateType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Auth Payload Type */\nexport type AuthPayloadType = {\n refreshToken?: Maybe<Scalars['String']['output']>;\n token: Scalars['String']['output'];\n user: UserType;\n};\n\n/** Base game input type */\nexport type BaseGameInputType = {\n dailyClue?: InputMaybe<DailyClueBaseGameInputType>;\n gameTitle: Scalars['String']['input'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['ID']['input'];\n miniQuiz?: InputMaybe<PuzzleBaseGameInputType>;\n oddOneOut?: InputMaybe<PuzzleBaseGameInputType>;\n};\n\n/** Base game type */\nexport type BaseGameType = {\n dailyClue?: Maybe<DailyClueBaseGameType>;\n gameTitle: Scalars['String']['output'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['ID']['output'];\n miniQuiz?: Maybe<PuzzleBaseGameType>;\n oddOneOut?: Maybe<PuzzleBaseGameType>;\n};\n\n/** Billing period enum type */\nexport enum BillingPeriodEnumType {\n MonthlyCancelAnytime = 'monthly_cancel_anytime',\n YearlyAnnualBilled = 'yearly_annual_billed',\n YearlyMonthlyBilled = 'yearly_monthly_billed'\n}\n\nexport type CancelSubscriptionResponse = {\n data?: Maybe<Scalars['String']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Category input type */\nexport type CategoryInputType = {\n id: Scalars['String']['input'];\n name: Scalars['String']['input'];\n subcategories: Array<InputMaybe<SubcategoryInputType>>;\n};\n\n/** Category type */\nexport type CategoryType = {\n id: Scalars['String']['output'];\n name: Scalars['String']['output'];\n subcategories?: Maybe<Array<SubcategoryType>>;\n};\n\n/** Message input type */\nexport type ChatMessageInputType = {\n content: Scalars['String']['input'];\n replyToMessageId?: InputMaybe<Scalars['String']['input']>;\n senderId: Scalars['String']['input'];\n};\n\n/** Chat message reaction */\nexport type ChatMessageReactionType = {\n createdAt: Scalars['Date']['output'];\n userId: Scalars['String']['output'];\n};\n\n/** Chat message reply preview */\nexport type ChatMessageReplyPreviewType = {\n contentPreview: Scalars['String']['output'];\n senderId: Scalars['String']['output'];\n senderName: Scalars['String']['output'];\n};\n\n/** Chat message seen status */\nexport type ChatMessageSeenType = {\n seenAt: Scalars['Date']['output'];\n userId: Scalars['String']['output'];\n};\n\n/** Chat message type */\nexport type ChatMessageType = {\n _id: Scalars['ID']['output'];\n content: Scalars['String']['output'];\n createdAt: Scalars['Date']['output'];\n likedBy?: Maybe<Array<Maybe<ChatMessageReactionType>>>;\n replyPreview?: Maybe<ChatMessageReplyPreviewType>;\n replyToMessageId?: Maybe<Scalars['ID']['output']>;\n seenBy?: Maybe<Array<Maybe<ChatMessageSeenType>>>;\n senderId: Scalars['String']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Participant type */\nexport type ChatParticipantType = {\n active: Scalars['Boolean']['output'];\n userAvatar?: Maybe<Scalars['String']['output']>;\n userEmail: Scalars['String']['output'];\n userId: Scalars['String']['output'];\n userName: Scalars['String']['output'];\n};\n\n/** Chat report reason enum */\nexport enum ChatReportReasonEnum {\n HarassmentOrBullying = 'Harassment_or_Bullying',\n HateSpeech = 'Hate_Speech',\n InappropriateContent = 'Inappropriate_Content',\n Other = 'Other',\n SpamOrScam = 'Spam_or_Scam',\n ViolenceOrDangerousBehavior = 'Violence_or_Dangerous_Behavior'\n}\n\n/** Chat type */\nexport type ChatType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n chatDescription?: Maybe<Scalars['String']['output']>;\n chatName: Scalars['String']['output'];\n chatType: ChatTypeEnum;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n /** List of messages in the chat */\n messages?: Maybe<Array<Maybe<ChatMessageType>>>;\n /** List of participants in the chat */\n participants: Array<Maybe<ChatParticipantType>>;\n region?: Maybe<Scalars['String']['output']>;\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Chat type enum */\nexport enum ChatTypeEnum {\n Group = 'group',\n Private = 'private',\n Relation = 'relation'\n}\n\n/** Stripe Checkout Session result */\nexport type CheckoutSessionResultType = {\n checkoutUrl: Scalars['String']['output'];\n sessionId: Scalars['ID']['output'];\n};\n\n/** Contact details input type */\nexport type ContactDetailsInputType = {\n email?: InputMaybe<Scalars['String']['input']>;\n landlinePhone?: InputMaybe<Scalars['String']['input']>;\n mobilePhone?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Contact details type */\nexport type ContactDetailsType = {\n email?: Maybe<Scalars['String']['output']>;\n landlinePhone?: Maybe<Scalars['String']['output']>;\n mobilePhone?: Maybe<Scalars['String']['output']>;\n};\n\n/** Contact us input type */\nexport type ContactUsInputType = {\n email: Scalars['String']['input'];\n firstName: Scalars['String']['input'];\n lastName: Scalars['String']['input'];\n message: Scalars['String']['input'];\n};\n\nexport type ContactUsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Content data input type */\nexport type ContentDataInputType = {\n game?: InputMaybe<BaseGameInputType>;\n images?: InputMaybe<Array<InputMaybe<ResourceImageInputType>>>;\n imagesUpload?: InputMaybe<Array<InputMaybe<ResourceImageUploadInputType>>>;\n list?: InputMaybe<ListContentDataInputType>;\n textarea?: InputMaybe<TextareaContentDataInputType>;\n video?: InputMaybe<VideoContentDataInputType>;\n};\n\nexport type CrawlGoogleMarketsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateAdResponse = {\n data?: Maybe<AdType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Input for creating multiple notifications for multiple users */\nexport type CreateBulkNotificationInput = {\n data?: InputMaybe<NotificationDataTypeInput>;\n message: Scalars['String']['input'];\n title: Scalars['String']['input'];\n type?: InputMaybe<NotificationEnumType>;\n userIds: Array<Scalars['ID']['input']>;\n};\n\nexport type CreateBulkNotificationsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateCheckoutSessionResponse = {\n data?: Maybe<CheckoutSessionResultType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateCustomerPortalResponse = {\n data?: Maybe<CustomerPortalResultType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateEventInfoResponse = {\n data?: Maybe<EventInfoType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateEventResponse = {\n data?: Maybe<EventType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreatePartnerResponse = {\n data?: Maybe<PartnerType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreatePostResponse = {\n data?: Maybe<PostType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreatePosterResponse = {\n data?: Maybe<PosterType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreatePrivateChatResponse = {\n data?: Maybe<ChatType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreatePushTokenResponse = {\n data?: Maybe<PushTokenType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateRelationResponse = {\n data?: Maybe<RelationType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateResourceActivityResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateSchoolResponse = {\n data?: Maybe<SchoolType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateUnregisteredVendorResponse = {\n data?: Maybe<UnregisteredVendorType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateVendorInfoResponse = {\n data?: Maybe<VendorInfoType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type CreateVendorResponse = {\n data?: Maybe<VendorType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Stripe Customer Portal result */\nexport type CustomerPortalResultType = {\n portalUrl: Scalars['String']['output'];\n};\n\n/** Daily clue base game input type */\nexport type DailyClueBaseGameInputType = {\n gameDate: GameDateInputType;\n gameSolution: Scalars['String']['input'];\n};\n\nexport type DailyClueBaseGameType = {\n gameDate: GameDateType;\n gameSolution: Scalars['String']['output'];\n};\n\n/** Daily clue game data type */\nexport type DailyClueGameDataType = {\n gameFields?: Maybe<DailyClueBaseGameType>;\n lastFoundDate?: Maybe<Scalars['Date']['output']>;\n letterInfo: DailyClueLetterType;\n points: Scalars['Int']['output'];\n streak: Scalars['Int']['output'];\n};\n\n/** Daily clue letter type */\nexport type DailyClueLetterType = {\n collected?: Maybe<Array<Maybe<Scalars['String']['output']>>>;\n solutionShuffled: Array<Maybe<Scalars['String']['output']>>;\n todaysClue?: Maybe<Scalars['String']['output']>;\n todaysLetter?: Maybe<Scalars['String']['output']>;\n todaysPlacement?: Maybe<Scalars['String']['output']>;\n};\n\n/** Date time input type */\nexport type DateTimeInputType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['input'];\n endTime: Scalars['String']['input'];\n startDate: Scalars['String']['input'];\n startTime: Scalars['String']['input'];\n};\n\n/** Date time type */\nexport type DateTimeType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['output'];\n endTime: Scalars['String']['output'];\n startDate: Scalars['String']['output'];\n startTime: Scalars['String']['output'];\n};\n\n/** Date time with price input type */\nexport type DateTimeWithPriceInputType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['input'];\n endTime: Scalars['String']['input'];\n stallTypes?: InputMaybe<Array<InputMaybe<StallTypeInputType>>>;\n startDate: Scalars['String']['input'];\n startTime: Scalars['String']['input'];\n};\n\nexport type DeleteAdResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteAffiliateResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteAllNotificationsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteChatResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteEventResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteNotificationResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeletePartnerResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeletePostResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteRelationResponse = {\n data?: Maybe<RelationType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteSchoolResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteUnregisteredVendorResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteUserResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type DeleteVendorResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Depth width type */\nexport type DepthWidthType = {\n depth: Scalars['String']['output'];\n width: Scalars['String']['output'];\n};\n\n/** Event date status enum type */\nexport enum EventDateStatusEnumType {\n Canceled = 'Canceled',\n Ended = 'Ended',\n Invalid = 'Invalid',\n NextWeek = 'Next_Week',\n Rescheduled = 'Rescheduled',\n Started = 'Started',\n StartingSoon = 'Starting_Soon',\n ThisWeek = 'This_Week',\n Today = 'Today',\n Tomorrow = 'Tomorrow',\n Upcoming = 'Upcoming'\n}\n\n/** Event Date time with price type */\nexport type EventDateTimeWithPriceType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['output'];\n endTime: Scalars['String']['output'];\n stallTypes: Array<StallTypeType>;\n startDate: Scalars['String']['output'];\n startTime: Scalars['String']['output'];\n};\n\n/** Event type enum */\nexport enum EventEnumType {\n Expo = 'Expo',\n Fair = 'Fair',\n Festival = 'Festival',\n Market = 'Market'\n}\n\n/** Event info input type */\nexport type EventInfoInputType = {\n applicationDeadlineHours: Scalars['Float']['input'];\n dateTime: Array<InputMaybe<DateTimeWithPriceInputType>>;\n eventId: Scalars['ID']['input'];\n packInTime: Scalars['Float']['input'];\n paymentDueHours: Scalars['Float']['input'];\n paymentInfo: Array<InputMaybe<PaymentInfoInputType>>;\n refundPolicy: Array<InputMaybe<RefundPolicyInputType>>;\n requirements?: InputMaybe<Array<InputMaybe<RequirementInputType>>>;\n};\n\n/** Event info type */\nexport type EventInfoType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n applicationDeadlineHours: Scalars['Float']['output'];\n dateTime: Array<EventDateTimeWithPriceType>;\n eventId: Scalars['ID']['output'];\n packInTime: Scalars['Float']['output'];\n paymentDueHours: Scalars['Float']['output'];\n paymentInfo: Array<PaymentInfoType>;\n refundPolicy: Array<RefundPolicyType>;\n requirements?: Maybe<Array<RequirementType>>;\n};\n\n/** Event input type */\nexport type EventInputType = {\n active: Scalars['Boolean']['input'];\n claimed?: InputMaybe<Scalars['Boolean']['input']>;\n contactDetails?: InputMaybe<ContactDetailsInputType>;\n cover: ResourceImageInputType;\n coverUpload?: InputMaybe<ResourceImageUploadInputType>;\n dateTime: Array<InputMaybe<DateTimeInputType>>;\n description: Scalars['String']['input'];\n eventType: EventEnumType;\n googlePlaceId?: InputMaybe<Scalars['String']['input']>;\n images?: InputMaybe<Array<InputMaybe<ResourceImageInputType>>>;\n imagesUpload?: InputMaybe<Array<InputMaybe<ResourceImageUploadInputType>>>;\n location: LocationInputType;\n logo?: InputMaybe<ResourceImageInputType>;\n logoUpload?: InputMaybe<ResourceImageUploadInputType>;\n name: Scalars['String']['input'];\n nzbn: Scalars['String']['input'];\n promoCodes?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n provider?: InputMaybe<Scalars['String']['input']>;\n rainOrShine: Scalars['Boolean']['input'];\n region: Scalars['String']['input'];\n socialMedia?: InputMaybe<Array<InputMaybe<SocialMediaInputType>>>;\n tags: Array<InputMaybe<Scalars['String']['input']>>;\n termsAgreement: TermsAgreementInputType;\n};\n\n/** Event list item type */\nexport type EventListItemType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n approvedAt?: Maybe<Scalars['Date']['output']>;\n claimed: Scalars['Boolean']['output'];\n cover?: Maybe<ResourceImageType>;\n createdAt: Scalars['Date']['output'];\n dateTime: Array<DateTimeType>;\n deletedAt?: Maybe<Scalars['Date']['output']>;\n description?: Maybe<Scalars['String']['output']>;\n eventType: EventEnumType;\n googlePlaceId?: Maybe<Scalars['String']['output']>;\n images?: Maybe<Array<ResourceImageType>>;\n location: LocationType;\n logo?: Maybe<ResourceImageType>;\n name: Scalars['String']['output'];\n rainOrShine: Scalars['Boolean']['output'];\n rating?: Maybe<Scalars['Float']['output']>;\n region: Scalars['String']['output'];\n relations?: Maybe<Array<ResourceRelationType>>;\n reviewCount?: Maybe<Scalars['Int']['output']>;\n slug: Scalars['String']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Reference to an event with ID and start date */\nexport type EventReferenceType = {\n dateTime: DateTimeType;\n resourceId: Scalars['ID']['output'];\n};\n\n/** Event type */\nexport type EventType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n adIds?: Maybe<Array<Maybe<Scalars['ID']['output']>>>;\n approvedAt?: Maybe<Scalars['Date']['output']>;\n claimed: Scalars['Boolean']['output'];\n contactDetails?: Maybe<ContactDetailsType>;\n cover: ResourceImageType;\n createdAt: Scalars['Date']['output'];\n dateTime: Array<DateTimeType>;\n deletedAt?: Maybe<Scalars['Date']['output']>;\n description: Scalars['String']['output'];\n eventInfoId?: Maybe<Scalars['ID']['output']>;\n eventType: EventEnumType;\n googlePlaceId?: Maybe<Scalars['String']['output']>;\n images?: Maybe<Array<ResourceImageType>>;\n location: LocationType;\n logo?: Maybe<ResourceImageType>;\n name: Scalars['String']['output'];\n nzbn: Scalars['String']['output'];\n owner: OwnerType;\n posterUsage?: Maybe<PosterUsageType>;\n promoCodes?: Maybe<Array<Scalars['String']['output']>>;\n provider?: Maybe<Scalars['String']['output']>;\n rainOrShine: Scalars['Boolean']['output'];\n rating?: Maybe<Scalars['Float']['output']>;\n region: Scalars['String']['output'];\n relatedPost?: Maybe<RelatedPostType>;\n relations?: Maybe<Array<ResourceRelationType>>;\n reviewCount?: Maybe<Scalars['Int']['output']>;\n sharePublic?: Maybe<SocialShareResourceType>;\n shareRelation?: Maybe<SocialShareResourceType>;\n slug: Scalars['String']['output'];\n socialMedia?: Maybe<Array<SocialMediaType>>;\n tags: Array<Scalars['String']['output']>;\n termsAgreement?: Maybe<TermsAgreementType>;\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Food flavor enum */\nexport enum FoodFlavorEnumType {\n NotApplicable = 'Not_Applicable',\n Salty = 'Salty',\n Savoury = 'Savoury',\n Spicy = 'Spicy',\n Sweet = 'Sweet'\n}\n\n/** Game data type */\nexport type GameDataType = {\n dailyClue?: Maybe<DailyClueGameDataType>;\n miniQuiz?: Maybe<PuzzleGameDataType>;\n oddOneOut?: Maybe<PuzzleGameDataType>;\n};\n\nexport type GameDateInputType = {\n endDate: Scalars['Date']['input'];\n startDate: Scalars['Date']['input'];\n};\n\nexport type GameDateType = {\n endDate: Scalars['Date']['output'];\n startDate: Scalars['Date']['output'];\n};\n\n/** Game document type */\nexport type GameDocType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n games?: Maybe<Array<Maybe<GameType>>>;\n owner: OwnerType;\n points: Scalars['Int']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Game history type */\nexport type GameHistoryType = {\n createdAt: Scalars['Date']['output'];\n gameDate: GameDateType;\n gameStatus: GameStatusEnumType;\n gameTitle: Scalars['String']['output'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['String']['output'];\n overallGamePoints: Scalars['Int']['output'];\n pointsEarned: Scalars['Int']['output'];\n};\n\n/** Game leaderboard type */\nexport type GameLeaderboardType = {\n gameHistory?: Maybe<Array<Maybe<GameHistoryType>>>;\n overallPoints: Scalars['Int']['output'];\n owner: OwnerType;\n};\n\n/** Game status enum */\nexport enum GameStatusEnumType {\n GameCompleted = 'GAME_COMPLETED',\n GameInProgress = 'GAME_IN_PROGRESS',\n GameLeft = 'GAME_LEFT',\n GameStarted = 'GAME_STARTED'\n}\n\n/** Game entry type */\nexport type GameType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n createdAt: Scalars['Date']['output'];\n gameData?: Maybe<GameDataType>;\n gameHistory?: Maybe<Array<Maybe<GameHistoryType>>>;\n gameTitle: Scalars['String']['output'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['ID']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Game type enum */\nexport enum GameTypeEnumType {\n DailyClue = 'dailyClue',\n MiniQuiz = 'miniQuiz',\n OddOneOut = 'oddOneOut'\n}\n\n/** Invite enum */\nexport enum InviteEnumType {\n Accepted = 'Accepted',\n Completed = 'Completed',\n Expired = 'Expired',\n NoStatus = 'No_Status',\n Pending = 'Pending',\n Rejected = 'Rejected',\n Unavailable = 'Unavailable'\n}\n\nexport type LastUpdateByInputType = {\n resourceId: Scalars['String']['input'];\n userEmail: Scalars['String']['input'];\n};\n\nexport type LastUpdateByType = {\n resourceId?: Maybe<Scalars['String']['output']>;\n userEmail?: Maybe<Scalars['String']['output']>;\n};\n\nexport type LeaveGameResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Licences enum */\nexport enum LicencesEnumType {\n ProEvent = 'pro_event',\n ProPlusEvent = 'pro_plus_event',\n ProPlusVendor = 'pro_plus_vendor',\n ProVendor = 'pro_vendor',\n StandardAffiliate = 'standard_affiliate',\n StandardEvent = 'standard_event',\n StandardPartner = 'standard_partner',\n StandardSchool = 'standard_school',\n StandardVendor = 'standard_vendor'\n}\n\n/** List content data input type */\nexport type ListContentDataInputType = {\n items: Array<InputMaybe<ListItemInputType>>;\n title?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** List content data type */\nexport type ListContentDataType = {\n items: Array<Maybe<ListItemType>>;\n title?: Maybe<Scalars['String']['output']>;\n};\n\n/** List item input type */\nexport type ListItemInputType = {\n text: Scalars['String']['input'];\n};\n\n/** List item type */\nexport type ListItemType = {\n text: Scalars['String']['output'];\n};\n\n/** Location geo input type */\nexport type LocationGeoInputType = {\n coordinates: Array<Scalars['Float']['input']>;\n type?: Scalars['String']['input'];\n};\n\n/** Location geo type */\nexport type LocationGeoType = {\n coordinates: Array<Scalars['Float']['output']>;\n type: Scalars['String']['output'];\n};\n\n/** Location input type */\nexport type LocationInputType = {\n city: Scalars['String']['input'];\n country: Scalars['String']['input'];\n fullAddress: Scalars['String']['input'];\n geo: LocationGeoInputType;\n latitude: Scalars['Float']['input'];\n longitude: Scalars['Float']['input'];\n region: Scalars['String']['input'];\n};\n\n/** Location type */\nexport type LocationType = {\n city: Scalars['String']['output'];\n country: Scalars['String']['output'];\n fullAddress: Scalars['String']['output'];\n geo: LocationGeoType;\n latitude: Scalars['Float']['output'];\n longitude: Scalars['Float']['output'];\n region: Scalars['String']['output'];\n};\n\n/** Login input type */\nexport type LoginInputType = {\n email: Scalars['String']['input'];\n isAdminPage?: InputMaybe<Scalars['Boolean']['input']>;\n password: Scalars['String']['input'];\n platform?: InputMaybe<OsPlatformEnumType>;\n};\n\nexport type LoginResponse = {\n data?: Maybe<AuthPayloadType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type LogoutResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type MarkAllNotificationsReadResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type MarkChatMessagesSeenResponse = {\n data?: Maybe<ChatType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type MarkNotificationReadResponse = {\n data?: Maybe<Notification>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Marketing material request input type */\nexport type MarketingMaterialRequestInputType = {\n posterName: Scalars['PosterAssetId']['input'];\n resourceId: Scalars['String']['input'];\n};\n\nexport type MarketingMaterialRequestResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Max min type */\nexport type MaxMinType = {\n max: Scalars['String']['output'];\n min: Scalars['String']['output'];\n};\n\nexport type Mutation = {\n /** Add participant to an existing chat */\n addParticipantToChat?: Maybe<AddParticipantToChatResponse>;\n /** Add a user favourite resource */\n addUserFavouriteResource?: Maybe<AddUserFavouriteResourceResponse>;\n /** Add a user going resource */\n addUserGoingResource?: Maybe<AddUserGoingResourceResponse>;\n /** Add a user interest resource */\n addUserInterestResource?: Maybe<AddUserInterestResourceResponse>;\n /** Add a user present resource */\n addUserPresentResource?: Maybe<AddUserPresentResourceResponse>;\n /** Permanently delete a resource */\n adminPermanentlyDeleteResource?: Maybe<AdminPermanentlyDeleteResourceResponse>;\n /** Resend user verification email */\n adminResendUserVerificationEmail?: Maybe<AdminResendUserVerificationEmailResponse>;\n /** Admin update resource type */\n adminUpdateResourceType?: Maybe<AdminUpdateResourceTypeResponse>;\n /** Assign a bonus reward to an affiliate (super admin) */\n assignAffiliateBonusReward?: Maybe<AssignAffiliateBonusRewardResponse>;\n /** Assign a reward to an existing affiliate resource (super admin) */\n assignAffiliateResourceReward?: Maybe<AssignAffiliateResourceRewardResponse>;\n /** Cancel the current user's subscription */\n cancelSubscription?: Maybe<CancelSubscriptionResponse>;\n /** Send a message through the contact us form */\n contactUs?: Maybe<ContactUsResponse>;\n /** Crawl Markets from Google */\n crawlGoogleMarkets?: Maybe<CrawlGoogleMarketsResponse>;\n /** Create a new ad */\n createAd?: Maybe<CreateAdResponse>;\n /** Creates multiple notifications */\n createBulkNotifications?: Maybe<CreateBulkNotificationsResponse>;\n /** Create a Stripe checkout session for subscription. Monthly available for Standard/Pro only. Yearly available for Standard/Pro/ProPlus. Defaults to monthly cancel-anytime if not specified. */\n createCheckoutSession?: Maybe<CreateCheckoutSessionResponse>;\n /** Create a Stripe Customer Portal session. Allows customers to manage their subscription, payment methods, and billing history. */\n createCustomerPortal?: Maybe<CreateCustomerPortalResponse>;\n /** Create a new event */\n createEvent?: Maybe<CreateEventResponse>;\n /** Create a new event info */\n createEventInfo?: Maybe<CreateEventInfoResponse>;\n /** Create a new partner */\n createPartner?: Maybe<CreatePartnerResponse>;\n /** Create a new post */\n createPost?: Maybe<CreatePostResponse>;\n /** Create a poster for a event or vendor */\n createPoster?: Maybe<CreatePosterResponse>;\n /** Create a private chat with another user */\n createPrivateChat?: Maybe<CreatePrivateChatResponse>;\n /** Create a push token for push notifications */\n createPushToken?: Maybe<CreatePushTokenResponse>;\n /** Create relation */\n createRelation?: Maybe<CreateRelationResponse>;\n /** Create or update resource activity */\n createResourceActivity?: Maybe<CreateResourceActivityResponse>;\n /** Create a new school */\n createSchool?: Maybe<CreateSchoolResponse>;\n /** Create a new unregistered vendor */\n createUnregisteredVendor?: Maybe<CreateUnregisteredVendorResponse>;\n /** Create a new vendor */\n createVendor?: Maybe<CreateVendorResponse>;\n /** Create a new vendor info */\n createVendorInfo?: Maybe<CreateVendorInfoResponse>;\n /** Delete an ad */\n deleteAd?: Maybe<DeleteAdResponse>;\n /** Delete an affiliate */\n deleteAffiliate?: Maybe<DeleteAffiliateResponse>;\n /** Deletes all notifications */\n deleteAllNotifications?: Maybe<DeleteAllNotificationsResponse>;\n /** Delete a chat by ID */\n deleteChat?: Maybe<DeleteChatResponse>;\n /** Delete an event */\n deleteEvent?: Maybe<DeleteEventResponse>;\n /** Deletes a notification */\n deleteNotification?: Maybe<DeleteNotificationResponse>;\n /** Delete a partner */\n deletePartner?: Maybe<DeletePartnerResponse>;\n /** Delete a post */\n deletePost?: Maybe<DeletePostResponse>;\n /** Delete relation */\n deleteRelation?: Maybe<DeleteRelationResponse>;\n /** Delete a school */\n deleteSchool?: Maybe<DeleteSchoolResponse>;\n /** Delete an unregistered vendor */\n deleteUnregisteredVendor?: Maybe<DeleteUnregisteredVendorResponse>;\n /** Delete a user */\n deleteUser?: Maybe<DeleteUserResponse>;\n /** Delete a vendor */\n deleteVendor?: Maybe<DeleteVendorResponse>;\n /** Leave an active game */\n leaveGame?: Maybe<LeaveGameResponse>;\n /** Login mutation */\n login?: Maybe<LoginResponse>;\n /** Logout mutation */\n logout?: Maybe<LogoutResponse>;\n /** Marks all notifications as read */\n markAllNotificationsRead?: Maybe<MarkAllNotificationsReadResponse>;\n /** Mark messages as seen */\n markChatMessagesSeen?: Maybe<MarkChatMessagesSeenResponse>;\n /** Marks a notification as read */\n markNotificationRead?: Maybe<MarkNotificationReadResponse>;\n /** Redeem available affiliate reward points */\n redeemAffiliateReward?: Maybe<RedeemAffiliateRewardResponse>;\n /** Redeem a reward */\n redeemReward?: Maybe<RedeemRewardResponse>;\n /** Refresh token mutation */\n refreshToken?: Maybe<RefreshTokenResponse>;\n /** Register mutation */\n register?: Maybe<RegisterResponse>;\n /** Remove a participant from a chat */\n removeParticipantFromChat?: Maybe<RemoveParticipantFromChatResponse>;\n /** Remove a user favourite resource */\n removeUserFavouriteResource?: Maybe<RemoveUserFavouriteResourceResponse>;\n /** Remove a user going resource */\n removeUserGoingResource?: Maybe<RemoveUserGoingResourceResponse>;\n /** Remove a user interest resource */\n removeUserInterestResource?: Maybe<RemoveUserInterestResourceResponse>;\n /** Remove a user present resource */\n removeUserPresentResource?: Maybe<RemoveUserPresentResourceResponse>;\n /** Report a user in a chat */\n reportChatUser?: Maybe<ReportChatUserResponse>;\n /** Request marketing material for a school */\n requestMarketingMaterial?: Maybe<MarketingMaterialRequestResponse>;\n /** Request password reset mutation */\n requestPasswordReset?: Maybe<RequestPasswordResetResponse>;\n /** Reset password mutation */\n resetPassword?: Maybe<ResetPasswordResponse>;\n /** Select a standard package for a user (local licence) */\n selectStandardPackage?: Maybe<SelectStandardPackageResponse>;\n /** Send a message in a chat */\n sendChatMessage?: Maybe<SendChatMessageResponse>;\n /** Start a new game */\n startGame?: Maybe<StartGameResponse>;\n /** Toggle a message like */\n toggleChatMessageLike?: Maybe<ToggleChatMessageLikeResponse>;\n /** Unlink an unregistered vendor from an inviter by removing the corresponding invitation */\n unlinkUnregisteredVendorByInviterId?: Maybe<UnlinkUnregisteredVendorByInviterIdResponse>;\n /** Update an ad */\n updateAd?: Maybe<UpdateAdResponse>;\n /** Update affiliate profile details */\n updateAffiliateDetails?: Maybe<UpdateAffiliateDetailsResponse>;\n /** Update application settings */\n updateAppSettings?: Maybe<UpdateAppSettingsResponse>;\n /** Update the daily clue game with a found letter */\n updateDailyClueGame?: Maybe<UpdateDailyClueGameResponse>;\n /** Update an event */\n updateEvent?: Maybe<UpdateEventResponse>;\n /** Update event info */\n updateEventInfo?: Maybe<UpdateEventInfoResponse>;\n /** Update Google imported markets */\n updateGoogleImportedMarkets?: Maybe<UpdateGoogleImportedMarketsResponse>;\n /** Update a partner */\n updatePartner?: Maybe<UpdatePartnerResponse>;\n /** Update a post */\n updatePost?: Maybe<UpdatePostResponse>;\n /** Update the puzzle game with answered questions */\n updatePuzzleGame?: Maybe<UpdatePuzzleGameResponse>;\n /** Update status for relation */\n updateRelation?: Maybe<UpdateRelationResponse>;\n /** Update a school */\n updateSchool?: Maybe<UpdateSchoolResponse>;\n /** Update user subscription to a different plan and/or billing period. Monthly available for Standard/Pro only. Yearly available for Standard/Pro/ProPlus. Defaults to monthly cancel-anytime if not specified. */\n updateSubscriptionPlan?: Maybe<UpdateSubscriptionPlanResponse>;\n /** Update an unregistered vendor */\n updateUnregisteredVendor?: Maybe<UpdateUnregisteredVendorResponse>;\n /** Update a user */\n updateUser?: Maybe<UpdateUserResponse>;\n /** Update a vendor */\n updateVendor?: Maybe<UpdateVendorResponse>;\n /** Update a vendor info */\n updateVendorInfo?: Maybe<UpdateVendorInfoResponse>;\n /** Validate verification token mutation */\n validateVerificationToken?: Maybe<ValidateVerificationTokenResponse>;\n};\n\n\nexport type MutationAddParticipantToChatArgs = {\n chatId: Scalars['ID']['input'];\n userId: Scalars['ID']['input'];\n};\n\n\nexport type MutationAddUserFavouriteResourceArgs = {\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n\nexport type MutationAddUserGoingResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationAddUserInterestResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationAddUserPresentResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationAdminPermanentlyDeleteResourceArgs = {\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n\nexport type MutationAdminResendUserVerificationEmailArgs = {\n userId: Scalars['ID']['input'];\n};\n\n\nexport type MutationAdminUpdateResourceTypeArgs = {\n input: AdminUpdateResourceInputType;\n};\n\n\nexport type MutationAssignAffiliateBonusRewardArgs = {\n affiliateId: Scalars['ID']['input'];\n rewardType: AffiliateRewardEnumType;\n};\n\n\nexport type MutationAssignAffiliateResourceRewardArgs = {\n affiliateId: Scalars['ID']['input'];\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n rewardType: AffiliateRewardEnumType;\n};\n\n\nexport type MutationContactUsArgs = {\n input: ContactUsInputType;\n};\n\n\nexport type MutationCreateAdArgs = {\n input: AdInputType;\n};\n\n\nexport type MutationCreateBulkNotificationsArgs = {\n input: CreateBulkNotificationInput;\n};\n\n\nexport type MutationCreateCheckoutSessionArgs = {\n billingPeriod?: InputMaybe<BillingPeriodEnumType>;\n planId: LicencesEnumType;\n};\n\n\nexport type MutationCreateCustomerPortalArgs = {\n returnUrl?: InputMaybe<Scalars['String']['input']>;\n};\n\n\nexport type MutationCreateEventArgs = {\n input: EventInputType;\n};\n\n\nexport type MutationCreateEventInfoArgs = {\n input: EventInfoInputType;\n};\n\n\nexport type MutationCreatePartnerArgs = {\n input: PartnerInputType;\n};\n\n\nexport type MutationCreatePostArgs = {\n input: PostInputType;\n};\n\n\nexport type MutationCreatePosterArgs = {\n input: PosterInputType;\n};\n\n\nexport type MutationCreatePrivateChatArgs = {\n userId: Scalars['ID']['input'];\n};\n\n\nexport type MutationCreatePushTokenArgs = {\n input: PushTokenInput;\n};\n\n\nexport type MutationCreateRelationArgs = {\n input: RelationInputType;\n};\n\n\nexport type MutationCreateResourceActivityArgs = {\n input: ResourceActivityInputType;\n};\n\n\nexport type MutationCreateSchoolArgs = {\n input: SchoolInputType;\n};\n\n\nexport type MutationCreateUnregisteredVendorArgs = {\n input: UnregisteredVendorInputType;\n};\n\n\nexport type MutationCreateVendorArgs = {\n input: VendorInputType;\n};\n\n\nexport type MutationCreateVendorInfoArgs = {\n input: VendorInfoInputType;\n};\n\n\nexport type MutationDeleteAdArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteAffiliateArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteChatArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteEventArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteNotificationArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeletePartnerArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeletePostArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteRelationArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteSchoolArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteUnregisteredVendorArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationDeleteUserArgs = {\n email: Scalars['String']['input'];\n};\n\n\nexport type MutationDeleteVendorArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationLeaveGameArgs = {\n _id: Scalars['ID']['input'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['String']['input'];\n};\n\n\nexport type MutationLoginArgs = {\n input: LoginInputType;\n};\n\n\nexport type MutationMarkChatMessagesSeenArgs = {\n chatId: Scalars['ID']['input'];\n messageIds: Array<InputMaybe<Scalars['ID']['input']>>;\n};\n\n\nexport type MutationMarkNotificationReadArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type MutationRedeemAffiliateRewardArgs = {\n affiliateId: Scalars['ID']['input'];\n};\n\n\nexport type MutationRedeemRewardArgs = {\n input: RedeemRewardInputType;\n};\n\n\nexport type MutationRefreshTokenArgs = {\n input: RefreshTokenInputType;\n};\n\n\nexport type MutationRegisterArgs = {\n input: RegisterInputType;\n};\n\n\nexport type MutationRemoveParticipantFromChatArgs = {\n chatId: Scalars['ID']['input'];\n userId: Scalars['ID']['input'];\n};\n\n\nexport type MutationRemoveUserFavouriteResourceArgs = {\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n\nexport type MutationRemoveUserGoingResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationRemoveUserInterestResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationRemoveUserPresentResourceArgs = {\n input: UserActivityInputType;\n};\n\n\nexport type MutationReportChatUserArgs = {\n input: ReportChatUserInputType;\n};\n\n\nexport type MutationRequestMarketingMaterialArgs = {\n input: MarketingMaterialRequestInputType;\n};\n\n\nexport type MutationRequestPasswordResetArgs = {\n input: RequestPasswordResetInputType;\n};\n\n\nexport type MutationResetPasswordArgs = {\n input: ResetPasswordInputType;\n};\n\n\nexport type MutationSelectStandardPackageArgs = {\n selectedLicence: LicencesEnumType;\n};\n\n\nexport type MutationSendChatMessageArgs = {\n _id: Scalars['ID']['input'];\n input: ChatMessageInputType;\n};\n\n\nexport type MutationStartGameArgs = {\n input: BaseGameInputType;\n};\n\n\nexport type MutationToggleChatMessageLikeArgs = {\n chatId: Scalars['ID']['input'];\n messageId: Scalars['ID']['input'];\n};\n\n\nexport type MutationUnlinkUnregisteredVendorByInviterIdArgs = {\n _id: Scalars['ID']['input'];\n inviterId: Scalars['ID']['input'];\n};\n\n\nexport type MutationUpdateAdArgs = {\n _id: Scalars['ID']['input'];\n input: AdInputType;\n};\n\n\nexport type MutationUpdateAffiliateDetailsArgs = {\n affiliateId: Scalars['ID']['input'];\n input: AffiliateDetailsInputType;\n};\n\n\nexport type MutationUpdateAppSettingsArgs = {\n input: AppSettingsInputType;\n};\n\n\nexport type MutationUpdateDailyClueGameArgs = {\n _id: Scalars['ID']['input'];\n foundLetter: Scalars['String']['input'];\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['String']['input'];\n};\n\n\nexport type MutationUpdateEventArgs = {\n _id: Scalars['ID']['input'];\n input: EventInputType;\n};\n\n\nexport type MutationUpdateEventInfoArgs = {\n _id: Scalars['ID']['input'];\n input: EventInfoInputType;\n};\n\n\nexport type MutationUpdatePartnerArgs = {\n _id: Scalars['ID']['input'];\n input: PartnerInputType;\n};\n\n\nexport type MutationUpdatePostArgs = {\n _id: Scalars['ID']['input'];\n input: PostInputType;\n};\n\n\nexport type MutationUpdatePuzzleGameArgs = {\n _id: Scalars['ID']['input'];\n answeredQuestions: Array<PuzzleAnsweredQuestionInputType>;\n gameType: GameTypeEnumType;\n gameTypeId: Scalars['String']['input'];\n};\n\n\nexport type MutationUpdateRelationArgs = {\n _id: Scalars['ID']['input'];\n input: RelationInputType;\n};\n\n\nexport type MutationUpdateSchoolArgs = {\n _id: Scalars['ID']['input'];\n input: SchoolInputType;\n};\n\n\nexport type MutationUpdateSubscriptionPlanArgs = {\n billingPeriod?: InputMaybe<BillingPeriodEnumType>;\n newPlanId: LicencesEnumType;\n};\n\n\nexport type MutationUpdateUnregisteredVendorArgs = {\n _id: Scalars['ID']['input'];\n input: UnregisteredVendorInputType;\n};\n\n\nexport type MutationUpdateUserArgs = {\n _id: Scalars['ID']['input'];\n input: UserInputType;\n};\n\n\nexport type MutationUpdateVendorArgs = {\n _id: Scalars['ID']['input'];\n input: VendorInputType;\n};\n\n\nexport type MutationUpdateVendorInfoArgs = {\n _id: Scalars['ID']['input'];\n input: VendorInfoInputType;\n};\n\n\nexport type MutationValidateVerificationTokenArgs = {\n input: ValidateVerificationTokenInputType;\n};\n\n/** A notification object */\nexport type Notification = {\n _id: Scalars['ID']['output'];\n createdAt?: Maybe<Scalars['Date']['output']>;\n data?: Maybe<NotificationDataType>;\n isRead?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n title?: Maybe<Scalars['String']['output']>;\n type?: Maybe<NotificationEnumType>;\n updatedAt?: Maybe<Scalars['Date']['output']>;\n userId: Scalars['ID']['output'];\n};\n\n/** Notification count information */\nexport type NotificationCount = {\n total?: Maybe<Scalars['Int']['output']>;\n unread?: Maybe<Scalars['Int']['output']>;\n};\n\n/** Data related to the notification */\nexport type NotificationDataType = {\n resourceId?: Maybe<Scalars['String']['output']>;\n resourceName?: Maybe<Scalars['String']['output']>;\n resourceType?: Maybe<NotificationResourceEnumType>;\n};\n\n/** Input for notification data */\nexport type NotificationDataTypeInput = {\n resourceId?: InputMaybe<Scalars['String']['input']>;\n resourceName?: InputMaybe<Scalars['String']['input']>;\n resourceType?: InputMaybe<NotificationResourceEnumType>;\n};\n\n/** Notification enum type */\nexport enum NotificationEnumType {\n Chat = 'chat',\n Event = 'event',\n Relation = 'relation',\n System = 'system',\n Vendor = 'vendor'\n}\n\n/** Notification resource enum type */\nexport enum NotificationResourceEnumType {\n AffiliateRewardReceived = 'affiliate_reward_received',\n ApprovedAffiliate = 'approved_affiliate',\n ApprovedEvent = 'approved_event',\n ApprovedPartner = 'approved_partner',\n ApprovedSchool = 'approved_school',\n ApprovedVendor = 'approved_vendor',\n CreatedEvent = 'created_event',\n CreatedPartner = 'created_partner',\n CreatedVendor = 'created_vendor',\n DailyClueGame = 'daily_clue_game',\n DeactivatedEvent = 'deactivated_event',\n DeactivatedPartner = 'deactivated_partner',\n DeactivatedVendor = 'deactivated_vendor',\n DeclinedEvent = 'declined_event',\n DeclinedPartner = 'declined_partner',\n DeclinedVendor = 'declined_vendor',\n DowngradedEvent = 'downgraded_event',\n DowngradedPartner = 'downgraded_partner',\n DowngradedVendor = 'downgraded_vendor',\n EventInviteVendor = 'event_invite_vendor',\n EventStartingSoon = 'event_starting_soon',\n EventUpdateRelationToVendor = 'event_update_relation_to_vendor',\n ExpirationReminderEvent = 'expiration_reminder_event',\n ExpirationReminderPartner = 'expiration_reminder_partner',\n ExpirationReminderVendor = 'expiration_reminder_vendor',\n NewChatMessage = 'new_chat_message',\n NewPostCreated = 'new_post_created',\n RegisteredUserBySchoolCode = 'registered_user_by_school_code',\n SystemAlert = 'system_alert',\n VendorApplicationToEvent = 'vendor_application_to_event',\n VendorUpdateRelationToEvent = 'vendor_update_relation_to_event'\n}\n\n/** Operating system enum */\nexport enum OsPlatformEnumType {\n Android = 'android',\n Ios = 'ios',\n Web = 'web'\n}\n\n/** Owner type */\nexport type OwnerType = {\n email: Scalars['String']['output'];\n userId: Scalars['ID']['output'];\n};\n\n/** Partner type enum */\nexport enum PartnerEnumType {\n CharityPartner = 'Charity_Partner',\n MediaPartner = 'Media_Partner',\n SupportingPartner = 'Supporting_Partner'\n}\n\n/** Partner input type */\nexport type PartnerInputType = {\n active: Scalars['Boolean']['input'];\n contactDetails?: InputMaybe<ContactDetailsInputType>;\n cover: ResourceImageInputType;\n coverUpload?: InputMaybe<ResourceImageUploadInputType>;\n description: Scalars['String']['input'];\n images?: InputMaybe<Array<InputMaybe<ResourceImageInputType>>>;\n imagesUpload?: InputMaybe<Array<InputMaybe<ResourceImageUploadInputType>>>;\n location: LocationInputType;\n logo?: InputMaybe<ResourceImageInputType>;\n logoUpload?: InputMaybe<ResourceImageUploadInputType>;\n name: Scalars['String']['input'];\n nzbn: Scalars['String']['input'];\n partnerType: PartnerEnumType;\n promoCodes?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n region: Scalars['String']['input'];\n socialMedia?: InputMaybe<Array<InputMaybe<SocialMediaInputType>>>;\n termsAgreement: TermsAgreementInputType;\n};\n\n/** Partner type */\nexport type PartnerType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n adIds?: Maybe<Array<Maybe<Scalars['ID']['output']>>>;\n approvedAt?: Maybe<Scalars['Date']['output']>;\n contactDetails?: Maybe<ContactDetailsType>;\n cover: ResourceImageType;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n description: Scalars['String']['output'];\n images?: Maybe<Array<ResourceImageType>>;\n location: LocationType;\n logo?: Maybe<ResourceImageType>;\n name: Scalars['String']['output'];\n nzbn: Scalars['String']['output'];\n owner: OwnerType;\n partnerType: PartnerEnumType;\n posterUsage?: Maybe<PosterUsageType>;\n promoCodes?: Maybe<Array<Scalars['String']['output']>>;\n rating?: Maybe<Scalars['Float']['output']>;\n region: Scalars['String']['output'];\n relatedPost?: Maybe<RelatedPostType>;\n reviewCount?: Maybe<Scalars['Int']['output']>;\n sharePublic?: Maybe<SocialShareResourceType>;\n slug: Scalars['String']['output'];\n socialMedia?: Maybe<Array<SocialMediaType>>;\n termsAgreement?: Maybe<TermsAgreementType>;\n updatedAt: Scalars['Date']['output'];\n};\n\nexport type PaymentInfoInputType = {\n accountHolderName?: InputMaybe<Scalars['String']['input']>;\n accountNumber?: InputMaybe<Scalars['String']['input']>;\n link?: InputMaybe<Scalars['String']['input']>;\n paymentMethod: PaymentMethodEnumType;\n};\n\nexport type PaymentInfoType = {\n accountHolderName?: Maybe<Scalars['String']['output']>;\n accountNumber?: Maybe<Scalars['String']['output']>;\n link?: Maybe<Scalars['String']['output']>;\n paymentMethod: PaymentMethodEnumType;\n};\n\n/** Payment method enum type */\nexport enum PaymentMethodEnumType {\n BankTransfer = 'bank_transfer',\n Cash = 'cash',\n Eftpos = 'eftpos',\n Paypal = 'paypal',\n Stripe = 'stripe'\n}\n\n/** Content data type */\nexport type PostContentData = {\n game?: Maybe<BaseGameType>;\n images?: Maybe<Array<Maybe<ResourceImageType>>>;\n list?: Maybe<ListContentDataType>;\n textarea?: Maybe<TextareaContentDataType>;\n video?: Maybe<VideoContentDataType>;\n};\n\n/** Post content enum */\nexport enum PostContentEnum {\n Game = 'game',\n Image = 'image',\n List = 'list',\n Textarea = 'textarea',\n Video = 'video'\n}\n\n/** Post content input type */\nexport type PostContentInputType = {\n contentData?: InputMaybe<ContentDataInputType>;\n contentOrder: Scalars['Float']['input'];\n contentType: PostContentEnum;\n};\n\n/** Post content type */\nexport type PostContentType = {\n contentData?: Maybe<PostContentData>;\n contentOrder: Scalars['Float']['output'];\n contentType: PostContentEnum;\n};\n\n/** Post input type */\nexport type PostInputType = {\n active: Scalars['Boolean']['input'];\n caption: Scalars['String']['input'];\n content: Array<InputMaybe<PostContentInputType>>;\n cover?: InputMaybe<ResourceImageInputType>;\n coverUpload?: InputMaybe<ResourceImageUploadInputType>;\n notifyUsers?: InputMaybe<Scalars['Boolean']['input']>;\n postType: PostTypeEnum;\n resource?: InputMaybe<PostResourceInputType>;\n tags?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n title: Scalars['String']['input'];\n};\n\n/** Post resource input type */\nexport type PostResourceInputType = {\n resourceId: Scalars['String']['input'];\n resourceRegion: Scalars['String']['input'];\n resourceSlug: Scalars['String']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Post resource type */\nexport type PostResourceType = {\n resourceId: Scalars['String']['output'];\n resourceRegion: Scalars['String']['output'];\n resourceSlug: Scalars['String']['output'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Post type */\nexport type PostType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n approvedAt?: Maybe<Scalars['Date']['output']>;\n caption: Scalars['String']['output'];\n content: Array<Maybe<PostContentType>>;\n cover?: Maybe<ResourceImageType>;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n postType: PostTypeEnum;\n resource?: Maybe<PostResourceType>;\n sharePublic?: Maybe<SocialShareResourceType>;\n slug: Scalars['String']['output'];\n tags?: Maybe<Array<Maybe<Scalars['String']['output']>>>;\n title: Scalars['String']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Post type enum */\nexport enum PostTypeEnum {\n ClueBites = 'clue_bites',\n MarketFaces = 'market_faces',\n PlayAndWin = 'play_and_win'\n}\n\n/** Poster input type */\nexport type PosterInputType = {\n description: Scalars['String']['input'];\n posterName: Scalars['PosterAssetId']['input'];\n resourceId: Scalars['String']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Poster type */\nexport type PosterType = {\n posterUsage?: Maybe<PosterUsageType>;\n};\n\n/** Poster usage type */\nexport type PosterUsageType = {\n count: Scalars['Int']['output'];\n month: Scalars['String']['output'];\n};\n\n/** Input type for push token */\nexport type PushTokenInput = {\n platform: OsPlatformEnumType;\n token?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Push token response type */\nexport type PushTokenType = {\n success: Scalars['Boolean']['output'];\n};\n\n/** Puzzle answer input type */\nexport type PuzzleAnswerInputType = {\n answer: Scalars['String']['input'];\n answerId: Scalars['ID']['input'];\n correct: Scalars['Boolean']['input'];\n};\n\n/** Puzzle question answer type */\nexport type PuzzleAnswerType = {\n answer: Scalars['String']['output'];\n answerId: Scalars['ID']['output'];\n correct: Scalars['Boolean']['output'];\n};\n\n/** Puzzle answered question input type */\nexport type PuzzleAnsweredQuestionInputType = {\n questionId: Scalars['ID']['input'];\n selectedAnswerId: Scalars['ID']['input'];\n};\n\n/** Puzzle answered question type */\nexport type PuzzleAnsweredQuestionType = {\n questionId: Scalars['ID']['output'];\n selectedAnswerId: Scalars['ID']['output'];\n};\n\n/** Puzzle base game input type */\nexport type PuzzleBaseGameInputType = {\n questions: Array<PuzzleQuestionInputType>;\n};\n\n/** Puzzle base game type */\nexport type PuzzleBaseGameType = {\n questions: Array<PuzzleQuestionType>;\n};\n\n/** Puzzle game data type */\nexport type PuzzleGameDataType = {\n answeredQuestions: Array<PuzzleAnsweredQuestionType>;\n gameFields?: Maybe<PuzzleBaseGameType>;\n points: Scalars['Int']['output'];\n streak: Scalars['Int']['output'];\n};\n\n/** Puzzle question input type */\nexport type PuzzleQuestionInputType = {\n answers: Array<PuzzleAnswerInputType>;\n question: Scalars['String']['input'];\n questionId: Scalars['ID']['input'];\n};\n\n/** Puzzle question type */\nexport type PuzzleQuestionType = {\n answers: Array<PuzzleAnswerType>;\n question: Scalars['String']['output'];\n questionId: Scalars['ID']['output'];\n};\n\nexport type Query = {\n /** Get a single ad by ID */\n ad?: Maybe<AdType>;\n /** Get all ads */\n ads?: Maybe<Array<Maybe<AdType>>>;\n /** Get ads by region */\n adsByRegion?: Maybe<Array<Maybe<AdType>>>;\n /** Get an affiliate by ID */\n affiliate?: Maybe<AffiliateType>;\n /** Get all affiliates */\n affiliates?: Maybe<Array<AffiliateType>>;\n /** Get application settings */\n appSettings: AppSettingsType;\n /** Get a chat by ID */\n chat?: Maybe<ChatType>;\n /** Get all group chats for a region */\n chatsByRegion?: Maybe<Array<ChatType>>;\n /** Get a event by ID */\n event?: Maybe<EventType>;\n /** Get event by Google Place ID */\n eventByPlaceId?: Maybe<EventListItemType>;\n /** Get a shareable event by public URL slug */\n eventBySlug?: Maybe<EventListItemType>;\n /** Get event info by event ID */\n eventInfo?: Maybe<EventInfoType>;\n /** Get all relations for a specific event */\n eventRelations?: Maybe<Array<Maybe<RelationType>>>;\n /** Get all events */\n events?: Maybe<Array<EventListItemType>>;\n /** Get events by region */\n eventsByRegion?: Maybe<Array<EventListItemType>>;\n /** Get events near a specific location */\n eventsNearMe?: Maybe<Array<EventListItemType>>;\n /** Search events */\n eventsSearch?: Maybe<Array<EventListItemType>>;\n /** Get a game by ID */\n game?: Maybe<GameDocType>;\n /** Get the game leaderboard */\n gameLeaderboard?: Maybe<Array<Maybe<GameLeaderboardType>>>;\n /** Get all games */\n games?: Maybe<Array<GameDocType>>;\n /** Get all available subscription plans with pricing. Applies discounts for new users (25% off annual) and testers (50% off annual). Discounts exclude cancel-anytime plans and are valid for one year only. */\n getSubscriptionPlans: SubscriptionPlansResponseType;\n /** Get the current user's subscription status */\n getSubscriptionStatus: SubscriptionStatusType;\n /** Get notification count for a user */\n notificationCount?: Maybe<NotificationCount>;\n /** Get a partner by ID */\n partner?: Maybe<PartnerType>;\n /** Get a shareable partner by public URL slug */\n partnerBySlug?: Maybe<PartnerType>;\n /** Get all partners */\n partners?: Maybe<Array<PartnerType>>;\n /** Get partners by region */\n partnersByRegion?: Maybe<Array<PartnerType>>;\n /** Search partners */\n partnersSearch?: Maybe<Array<PartnerType>>;\n /** Get a post by ID */\n post?: Maybe<PostType>;\n /** Get a shareable post by public URL slug and post type */\n postBySlug?: Maybe<PostType>;\n /** Get all posts */\n posts?: Maybe<Array<PostType>>;\n /** Get posts by type */\n postsByType?: Maybe<Array<PostType>>;\n /** Get a relation by ID */\n relation?: Maybe<RelationType>;\n /** Get relation for a specific vendor and event combination */\n relationByEventAndVendor?: Maybe<RelationType>;\n /** Get resource activities by resource ID and type */\n resourceActivity?: Maybe<ResourceActivityType>;\n /** Get all connections for a specific resource */\n resourceConnections?: Maybe<ResourceConnections>;\n /** Get a school by ID */\n school?: Maybe<SchoolReturnType>;\n /** Get all schools */\n schools?: Maybe<Array<SchoolType>>;\n /** Get an unregistered vendor by ID */\n unregisteredVendor?: Maybe<UnregisteredVendorType>;\n /** Get all unregistered vendors */\n unregisteredVendors?: Maybe<Array<UnregisteredVendorType>>;\n /** Get unregistered vendors by inviter ID (only includes invitations from that inviter) */\n unregisteredVendorsByInviterId?: Maybe<Array<UnregisteredVendorType>>;\n /** Get a user by ID */\n user?: Maybe<UserType>;\n /** Get all activities of the user */\n userActivities?: Maybe<UserActivities>;\n /** Get all chats for a user */\n userChats?: Maybe<Array<ChatType>>;\n /** Get all events owned by the user */\n userEvents: Array<Maybe<EventType>>;\n /** Get user notifications with pagination */\n userNotifications?: Maybe<Array<Maybe<Notification>>>;\n /** Get the partners of the user */\n userPartners: Array<Maybe<PartnerType>>;\n /** Get all resources owned by the user */\n userResources?: Maybe<UserResources>;\n /** Get the vendors of the user */\n userVendors: Array<Maybe<VendorType>>;\n /** Get all users */\n users?: Maybe<Array<UserType>>;\n /** Get a vendor by ID */\n vendor?: Maybe<VendorType>;\n /** Get a shareable vendor by public URL slug */\n vendorBySlug?: Maybe<VendorType>;\n /** Get a vendor info by vendor ID */\n vendorInfo?: Maybe<VendorInfoType>;\n /** Get all relations for a specific vendor */\n vendorRelations?: Maybe<Array<Maybe<RelationType>>>;\n /** Search vendors */\n vendorSearch?: Maybe<Array<VendorType>>;\n /** Get all vendors */\n vendors?: Maybe<Array<VendorType>>;\n /** Get vendors by Region */\n vendorsByRegion?: Maybe<Array<VendorType>>;\n};\n\n\nexport type QueryAdArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryAdsByRegionArgs = {\n region: Scalars['String']['input'];\n status?: InputMaybe<AdStatusTypeEnum>;\n};\n\n\nexport type QueryAffiliateArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryChatArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryChatsByRegionArgs = {\n region: Scalars['String']['input'];\n};\n\n\nexport type QueryEventArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryEventByPlaceIdArgs = {\n googlePlaceId: Scalars['String']['input'];\n};\n\n\nexport type QueryEventBySlugArgs = {\n slug: Scalars['String']['input'];\n};\n\n\nexport type QueryEventInfoArgs = {\n eventId: Scalars['ID']['input'];\n};\n\n\nexport type QueryEventRelationsArgs = {\n eventId: Scalars['ID']['input'];\n};\n\n\nexport type QueryEventsArgs = {\n dateStatus?: InputMaybe<EventDateStatusEnumType>;\n};\n\n\nexport type QueryEventsByRegionArgs = {\n dateStatus?: InputMaybe<EventDateStatusEnumType>;\n options?: InputMaybe<ResourcesByRegionOptions>;\n region: Scalars['String']['input'];\n};\n\n\nexport type QueryEventsNearMeArgs = {\n dateStatus?: InputMaybe<EventDateStatusEnumType>;\n latitude: Scalars['Float']['input'];\n longitude: Scalars['Float']['input'];\n radius?: InputMaybe<Scalars['Int']['input']>;\n};\n\n\nexport type QueryEventsSearchArgs = {\n dateStatus?: InputMaybe<EventDateStatusEnumType>;\n region: Scalars['String']['input'];\n search?: InputMaybe<Scalars['String']['input']>;\n tags?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n};\n\n\nexport type QueryGameArgs = {\n _id?: InputMaybe<Scalars['ID']['input']>;\n};\n\n\nexport type QueryPartnerArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryPartnerBySlugArgs = {\n slug: Scalars['String']['input'];\n};\n\n\nexport type QueryPartnersByRegionArgs = {\n options?: InputMaybe<ResourcesByRegionOptions>;\n region: Scalars['String']['input'];\n};\n\n\nexport type QueryPartnersSearchArgs = {\n region: Scalars['String']['input'];\n search: Scalars['String']['input'];\n};\n\n\nexport type QueryPostArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryPostBySlugArgs = {\n postType: PostTypeEnum;\n slug: Scalars['String']['input'];\n};\n\n\nexport type QueryPostsByTypeArgs = {\n postType: PostTypeEnum;\n};\n\n\nexport type QueryRelationArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryRelationByEventAndVendorArgs = {\n eventId: Scalars['ID']['input'];\n vendorId: Scalars['ID']['input'];\n};\n\n\nexport type QueryResourceActivityArgs = {\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n\nexport type QueryResourceConnectionsArgs = {\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n\nexport type QuerySchoolArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryUnregisteredVendorArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryUnregisteredVendorsByInviterIdArgs = {\n inviterId: Scalars['ID']['input'];\n};\n\n\nexport type QueryUserArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryUserNotificationsArgs = {\n limit?: InputMaybe<Scalars['Int']['input']>;\n offset?: InputMaybe<Scalars['Int']['input']>;\n};\n\n\nexport type QueryUserResourcesArgs = {\n userId: Scalars['ID']['input'];\n};\n\n\nexport type QueryVendorArgs = {\n _id: Scalars['ID']['input'];\n};\n\n\nexport type QueryVendorBySlugArgs = {\n slug: Scalars['String']['input'];\n};\n\n\nexport type QueryVendorInfoArgs = {\n vendorId: Scalars['ID']['input'];\n};\n\n\nexport type QueryVendorRelationsArgs = {\n vendorId: Scalars['ID']['input'];\n};\n\n\nexport type QueryVendorSearchArgs = {\n region: Scalars['String']['input'];\n search: Scalars['String']['input'];\n};\n\n\nexport type QueryVendorsByRegionArgs = {\n options?: InputMaybe<ResourcesByRegionOptions>;\n region: Scalars['String']['input'];\n};\n\nexport type RedeemAffiliateRewardResponse = {\n data?: Maybe<AffiliateType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Redeem history type */\nexport type RedeemHistoryType = {\n paidAt?: Maybe<Scalars['Date']['output']>;\n redeemedAt: Scalars['Date']['output'];\n rewardValue: Scalars['Int']['output'];\n};\n\n/** Redeem reward input type */\nexport type RedeemRewardInputType = {\n reward: RewardEnumType;\n};\n\nexport type RedeemRewardResponse = {\n data?: Maybe<RedeemRewardResponseType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Redeem reward response data */\nexport type RedeemRewardResponseType = {\n userId: Scalars['ID']['output'];\n};\n\n/** Redeemed reward type */\nexport type RedeemedRewardType = {\n name: RewardEnumType;\n pointsUsed: Scalars['Int']['output'];\n redeemedAt: Scalars['Date']['output'];\n};\n\n/** Refresh token input type */\nexport type RefreshTokenInputType = {\n refreshToken: Scalars['String']['input'];\n};\n\n/** Refresh token payload type */\nexport type RefreshTokenPayloadType = {\n refreshToken: Scalars['String']['output'];\n token: Scalars['String']['output'];\n};\n\nexport type RefreshTokenResponse = {\n data?: Maybe<RefreshTokenPayloadType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RefundPolicyInputType = {\n category: Scalars['String']['input'];\n label: Scalars['String']['input'];\n value: Scalars['Boolean']['input'];\n};\n\nexport type RefundPolicyType = {\n category: Scalars['String']['output'];\n label: Scalars['String']['output'];\n value: Scalars['Boolean']['output'];\n};\n\n/** Register input type */\nexport type RegisterInputType = {\n email: Scalars['String']['input'];\n firstName: Scalars['String']['input'];\n lastName: Scalars['String']['input'];\n password: Scalars['String']['input'];\n platform?: InputMaybe<OsPlatformEnumType>;\n preferredRegion: Scalars['String']['input'];\n promoCode?: InputMaybe<Scalars['String']['input']>;\n termsAgreement: TermsAgreementInputType;\n};\n\nexport type RegisterResponse = {\n data?: Maybe<AuthPayloadType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Related post type */\nexport type RelatedPostType = {\n postActive: Scalars['Boolean']['output'];\n postId: Scalars['ID']['output'];\n postSlug: Scalars['String']['output'];\n postType: PostTypeEnum;\n};\n\n/** Relation date input type */\nexport type RelationDateInputType = {\n dateTime: RelationDateTimeInputType;\n lastUpdateBy: LastUpdateByInputType;\n paymentReference?: InputMaybe<Scalars['String']['input']>;\n status: InviteEnumType;\n};\n\n/** Relation date time input type */\nexport type RelationDateTimeInputType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['input'];\n endTime: Scalars['String']['input'];\n stallType: StallTypeInputType;\n startDate: Scalars['String']['input'];\n startTime: Scalars['String']['input'];\n};\n\n/** Relation date time type */\nexport type RelationDateTimeType = {\n dateStatus: EventDateStatusEnumType;\n endDate: Scalars['String']['output'];\n endTime: Scalars['String']['output'];\n stallType?: Maybe<StallTypeType>;\n startDate: Scalars['String']['output'];\n startTime: Scalars['String']['output'];\n};\n\n/** Relation date type */\nexport type RelationDateType = {\n dateTime: RelationDateTimeType;\n lastUpdateBy?: Maybe<LastUpdateByType>;\n paymentReference?: Maybe<Scalars['String']['output']>;\n status: InviteEnumType;\n};\n\n/** Relation input type */\nexport type RelationInputType = {\n active: Scalars['Boolean']['input'];\n chatId?: InputMaybe<Scalars['ID']['input']>;\n eventId: Scalars['ID']['input'];\n lastUpdateBy: ResourceTypeEnum;\n relationDates: Array<InputMaybe<RelationDateInputType>>;\n relationType: RelationTypeEnum;\n vendorId: Scalars['ID']['input'];\n};\n\n/** Relation type */\nexport type RelationType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n chatId: Scalars['ID']['output'];\n createdAt?: Maybe<Scalars['Date']['output']>;\n deletedAt?: Maybe<Scalars['Date']['output']>;\n eventId: Scalars['ID']['output'];\n lastUpdateBy: ResourceTypeEnum;\n relationDates: Array<RelationDateType>;\n relationType: RelationTypeEnum;\n updatedAt?: Maybe<Scalars['Date']['output']>;\n vendorId: Scalars['ID']['output'];\n};\n\n/** Resource relation type enum */\nexport enum RelationTypeEnum {\n EventInviteVendor = 'event_invite_vendor',\n EventUpdateRelationToVendor = 'event_update_relation_to_vendor',\n VendorApplicationToEvent = 'vendor_application_to_event',\n VendorUpdateRelationToEvent = 'vendor_update_relation_to_event'\n}\n\nexport type RemoveParticipantFromChatPayload = {\n region?: Maybe<Scalars['String']['output']>;\n success: Scalars['Boolean']['output'];\n};\n\nexport type RemoveParticipantFromChatResponse = {\n data?: Maybe<RemoveParticipantFromChatPayload>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RemoveUserFavouriteResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RemoveUserGoingResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RemoveUserInterestResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RemoveUserPresentResourceResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Report chat reason input type */\nexport type ReportChatReasonInputType = {\n details?: InputMaybe<Scalars['String']['input']>;\n reasonType: ChatReportReasonEnum;\n};\n\n/** Report chat reason type */\nexport type ReportChatReasonType = {\n details?: Maybe<Scalars['String']['output']>;\n reasonType: ChatReportReasonEnum;\n};\n\n/** Report chat user input type */\nexport type ReportChatUserInputType = {\n chatId: Scalars['ID']['input'];\n reason: ReportChatReasonInputType;\n reportedUserId: Scalars['ID']['input'];\n};\n\nexport type ReportChatUserResponse = {\n data?: Maybe<ReportChatUserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Report chat user type */\nexport type ReportChatUserType = {\n _id: Scalars['ID']['output'];\n chatId: Scalars['ID']['output'];\n createdAt: Scalars['Date']['output'];\n reason: ReportChatReasonType;\n reportedUserId: Scalars['ID']['output'];\n reporterUserId: Scalars['ID']['output'];\n resolved: Scalars['Boolean']['output'];\n updatedAt?: Maybe<Scalars['Date']['output']>;\n};\n\n/** Request password reset input type */\nexport type RequestPasswordResetInputType = {\n email: Scalars['String']['input'];\n};\n\nexport type RequestPasswordResetResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type RequirementInputType = {\n category: Scalars['String']['input'];\n label: Scalars['String']['input'];\n value: Scalars['Boolean']['input'];\n};\n\nexport type RequirementType = {\n category: Scalars['String']['output'];\n label: Scalars['String']['output'];\n value: Scalars['Boolean']['output'];\n};\n\n/** Reset password input type */\nexport type ResetPasswordInputType = {\n confirmPassword: Scalars['String']['input'];\n email: Scalars['String']['input'];\n password: Scalars['String']['input'];\n};\n\nexport type ResetPasswordResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Resource view entry type */\nexport type ResourceActivityEntryType = {\n activityType: ActivityEnumType;\n dateStatus?: Maybe<EventDateStatusEnumType>;\n location?: Maybe<LocationGeoType>;\n startDate?: Maybe<Scalars['String']['output']>;\n startTime?: Maybe<Scalars['String']['output']>;\n timestamp?: Maybe<Scalars['Date']['output']>;\n userAgent?: Maybe<OsPlatformEnumType>;\n userId?: Maybe<Scalars['String']['output']>;\n};\n\n/** Resource activity input type */\nexport type ResourceActivityInputType = {\n activity: ResourceViewEntryInputType;\n resourceId: Scalars['ID']['input'];\n resourceType: ResourceTypeEnum;\n};\n\n/** Resource activities type */\nexport type ResourceActivityType = {\n _id: Scalars['ID']['output'];\n activity?: Maybe<Array<Maybe<ResourceActivityEntryType>>>;\n resourceId: Scalars['ID']['output'];\n resourceType: ResourceTypeEnum;\n};\n\nexport type ResourceConnections = {\n /** List of events connected to the resource */\n events?: Maybe<Array<Maybe<EventListItemType>>>;\n /** List of vendors connected to the resource */\n vendors?: Maybe<Array<Maybe<VendorType>>>;\n};\n\n/** Resource image input type */\nexport type ResourceImageInputType = {\n active?: InputMaybe<Scalars['Boolean']['input']>;\n source?: InputMaybe<Scalars['String']['input']>;\n title?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Resource image type */\nexport type ResourceImageType = {\n active: Scalars['Boolean']['output'];\n fullUrl?: Maybe<Scalars['String']['output']>;\n source: Scalars['String']['output'];\n title: Scalars['String']['output'];\n};\n\n/** Resource image upload input type */\nexport type ResourceImageUploadInputType = {\n source?: InputMaybe<Scalars['Upload']['input']>;\n title?: InputMaybe<Scalars['String']['input']>;\n};\n\nexport type ResourceRelationType = {\n relationDates: Array<RelationDateType>;\n relationId: Scalars['ID']['output'];\n};\n\n/** Resource type enum */\nexport enum ResourceTypeEnum {\n Affiliate = 'affiliate',\n Event = 'event',\n Partner = 'partner',\n School = 'school',\n Vendor = 'vendor'\n}\n\n/** Resource view entry input type */\nexport type ResourceViewEntryInputType = {\n activityType: ActivityEnumType;\n location?: InputMaybe<LocationGeoInputType>;\n startDate?: InputMaybe<Scalars['String']['input']>;\n startTime?: InputMaybe<Scalars['String']['input']>;\n userAgent?: InputMaybe<OsPlatformEnumType>;\n userId?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Resources by region options */\nexport type ResourcesByRegionOptions = {\n limit?: InputMaybe<Scalars['Int']['input']>;\n offset?: InputMaybe<Scalars['Int']['input']>;\n onlyClaimed?: InputMaybe<Scalars['Boolean']['input']>;\n};\n\n/** Reward enum */\nexport enum RewardEnumType {\n MysteryDeluxeBox = 'mystery_deluxe_box',\n MysteryJoyBox = 'mystery_joy_box',\n MysterySqueezeBox = 'mystery_squeeze_box'\n}\n\n/** School campaign type */\nexport type SchoolCampaignType = {\n endDate: Scalars['Date']['output'];\n name: Scalars['String']['output'];\n startDate: Scalars['Date']['output'];\n};\n\n/** School input type */\nexport type SchoolInputType = {\n active: Scalars['Boolean']['input'];\n contactDetails: ContactDetailsInputType;\n location: LocationInputType;\n logo?: InputMaybe<ResourceImageInputType>;\n logoUpload?: InputMaybe<ResourceImageUploadInputType>;\n name: Scalars['String']['input'];\n region: Scalars['String']['input'];\n socialMedia?: InputMaybe<Array<InputMaybe<SocialMediaInputType>>>;\n studentCount: Scalars['Int']['input'];\n termsAgreement: TermsAgreementInputType;\n};\n\n/** School registered user type */\nexport type SchoolRegisteredUserType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n avatar?: Maybe<ResourceImageType>;\n email: Scalars['String']['output'];\n firstName: Scalars['String']['output'];\n lastName: Scalars['String']['output'];\n};\n\n/** School return type */\nexport type SchoolReturnType = {\n school: SchoolType;\n users?: Maybe<Array<SchoolRegisteredUserType>>;\n};\n\n/** School type */\nexport type SchoolType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n approvedAt?: Maybe<Scalars['Date']['output']>;\n campaigns?: Maybe<Array<SchoolCampaignType>>;\n contactDetails: ContactDetailsType;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n location: LocationType;\n logo?: Maybe<ResourceImageType>;\n name: Scalars['String']['output'];\n overallPoints?: Maybe<Scalars['Int']['output']>;\n owner: OwnerType;\n ranking?: Maybe<Scalars['Int']['output']>;\n region: Scalars['String']['output'];\n schoolCode: Scalars['String']['output'];\n socialMedia?: Maybe<Array<SocialMediaType>>;\n studentCount: Scalars['Int']['output'];\n termsAgreement: TermsAgreementType;\n updatedAt: Scalars['Date']['output'];\n};\n\n/** Select standard package result data */\nexport type SelectStandardPackageDataType = {\n licences?: Maybe<Array<UserLicenceType>>;\n userId: Scalars['ID']['output'];\n};\n\nexport type SelectStandardPackageResponse = {\n data?: Maybe<SelectStandardPackageDataType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type SendChatMessageResponse = {\n data?: Maybe<ChatType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Social media enum type */\nexport enum SocialMediaEnumType {\n Facebook = 'facebook',\n Instagram = 'instagram',\n Tiktok = 'tiktok',\n Twitter = 'twitter',\n Website = 'website',\n Youtube = 'youtube'\n}\n\n/** Social media input type */\nexport type SocialMediaInputType = {\n link: Scalars['String']['input'];\n name: SocialMediaEnumType;\n};\n\n/** Social media type */\nexport type SocialMediaType = {\n link: Scalars['String']['output'];\n name: SocialMediaEnumType;\n};\n\n/** Social share resource type */\nexport type SocialShareResourceType = {\n qrCode: ResourceImageType;\n socialImage: ResourceImageType;\n};\n\n/** Stall type input type */\nexport type StallTypeInputType = {\n label: Scalars['String']['input'];\n price: Scalars['Float']['input'];\n stallCapacity: Scalars['Float']['input'];\n};\n\n/** Stall type object */\nexport type StallTypeType = {\n label: Scalars['String']['output'];\n price: Scalars['Float']['output'];\n stallCapacity: Scalars['Float']['output'];\n};\n\nexport type StartGameResponse = {\n data?: Maybe<GameDocType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Stripe subscription type */\nexport type StripeSubscriptionType = {\n currentPlan?: Maybe<LicencesEnumType>;\n customerId?: Maybe<Scalars['String']['output']>;\n endDate?: Maybe<Scalars['Date']['output']>;\n startDate?: Maybe<Scalars['Date']['output']>;\n status?: Maybe<SubscriptionStatusEnumType>;\n subscriptionId?: Maybe<Scalars['String']['output']>;\n};\n\n/** Subcategory input type */\nexport type SubcategoryInputType = {\n id: Scalars['String']['input'];\n items?: InputMaybe<Array<InputMaybe<SubcategoryItemInputType>>>;\n name?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Subcategory item input type */\nexport type SubcategoryItemInputType = {\n id?: InputMaybe<Scalars['String']['input']>;\n name?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Subcategory item type */\nexport type SubcategoryItemType = {\n id?: Maybe<Scalars['String']['output']>;\n name?: Maybe<Scalars['String']['output']>;\n};\n\n/** Subcategory type */\nexport type SubcategoryType = {\n id: Scalars['String']['output'];\n items?: Maybe<Array<SubcategoryItemType>>;\n name?: Maybe<Scalars['String']['output']>;\n};\n\n/** The root subscription type */\nexport type Subscription = {\n /** Subscribe to chat messages */\n getChatMessage?: Maybe<ChatType>;\n /** Subscribe to notification count updates */\n getNotificationCount?: Maybe<NotificationCount>;\n /** Subscribe to user notifications */\n getUserNotifications?: Maybe<Array<Maybe<Notification>>>;\n};\n\n/** Subscription plan with pricing, discounts, and Stripe metadata */\nexport type SubscriptionPlanType = {\n /** Billing period (monthly, yearly_annual_billed, yearly_monthly_billed) */\n billingPeriod: BillingPeriodEnumType;\n /** Plan description */\n description?: Maybe<Scalars['String']['output']>;\n /** Whether this plan is eligible for discount (annual plans only) */\n isEligibleForDiscount: Scalars['Boolean']['output'];\n /** Licence type (standard_vendor, pro_event, etc.) */\n licenceType: LicencesEnumType;\n /** Plan name (e.g., 'Standard Vendor') */\n name: Scalars['String']['output'];\n /** Pricing details with discounts applied */\n pricing: SubscriptionPricingType;\n /** Stripe price ID */\n stripePriceId: Scalars['String']['output'];\n /** Stripe product ID */\n stripeProductId: Scalars['String']['output'];\n};\n\n/** Response containing subscription plans with pricing */\nexport type SubscriptionPlansResponseType = {\n /** Type of discount applied: 'none', 'new_user', or 'tester' */\n appliedDiscountType: Scalars['String']['output'];\n /** Whether the user is a new user (no previous subscriptions) */\n isNewUser: Scalars['Boolean']['output'];\n /** Whether the user is a tester */\n isTester: Scalars['Boolean']['output'];\n /** List of available subscription plans */\n plans: Array<SubscriptionPlanType>;\n};\n\n/** Pricing details for a subscription plan */\nexport type SubscriptionPricingType = {\n /** Base price in cents */\n basePrice: Scalars['Int']['output'];\n /** Currency code (e.g., 'nzd', 'usd') */\n currency: Scalars['String']['output'];\n /** Discount amount in cents (if applicable) */\n discountAmount?: Maybe<Scalars['Int']['output']>;\n /** Discount percentage (if applicable) */\n discountPercent?: Maybe<Scalars['Float']['output']>;\n /** Final price after discount in cents (if applicable) */\n discountedPrice?: Maybe<Scalars['Int']['output']>;\n /** Formatted base price (e.g., '$99.00') */\n formattedBasePrice: Scalars['String']['output'];\n /** Formatted discounted price (e.g., '$74.25') */\n formattedDiscountedPrice?: Maybe<Scalars['String']['output']>;\n /** Whether a discount is applied */\n hasDiscount: Scalars['Boolean']['output'];\n};\n\n/** Subscription status enum type */\nexport enum SubscriptionStatusEnumType {\n Active = 'active',\n Cancelled = 'cancelled',\n Inactive = 'inactive',\n NoSubscription = 'no_subscription',\n PastDue = 'past_due',\n Trialing = 'trialing'\n}\n\n/** Subscription status information */\nexport type SubscriptionStatusType = {\n currentPlan?: Maybe<LicencesEnumType>;\n priceId?: Maybe<Scalars['String']['output']>;\n status?: Maybe<SubscriptionStatusEnumType>;\n subscriptionId?: Maybe<Scalars['String']['output']>;\n};\n\n/** Terms agreement input type */\nexport type TermsAgreementInputType = {\n appBuildNumber: Scalars['String']['input'];\n appId: Scalars['String']['input'];\n appVersion: Scalars['String']['input'];\n brand: Scalars['String']['input'];\n deviceName: Scalars['String']['input'];\n installationId: Scalars['String']['input'];\n manufacturer: Scalars['String']['input'];\n modelName: Scalars['String']['input'];\n osName: Scalars['String']['input'];\n osVersion: Scalars['String']['input'];\n termVersion: Scalars['String']['input'];\n timestamp: Scalars['String']['input'];\n};\n\n/** Terms agreement type */\nexport type TermsAgreementType = {\n appBuildNumber: Scalars['String']['output'];\n appId: Scalars['String']['output'];\n appVersion: Scalars['String']['output'];\n brand: Scalars['String']['output'];\n deviceName: Scalars['String']['output'];\n installationId: Scalars['String']['output'];\n manufacturer: Scalars['String']['output'];\n modelName: Scalars['String']['output'];\n osName: Scalars['String']['output'];\n osVersion: Scalars['String']['output'];\n termVersion: Scalars['String']['output'];\n timestamp: Scalars['String']['output'];\n};\n\n/** Textarea content data input type */\nexport type TextareaContentDataInputType = {\n data: Scalars['String']['input'];\n title?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Textarea content data type */\nexport type TextareaContentDataType = {\n data: Scalars['String']['output'];\n title?: Maybe<Scalars['String']['output']>;\n};\n\nexport type ToggleChatMessageLikeResponse = {\n data?: Maybe<ChatType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UnlinkUnregisteredVendorByInviterIdResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Unregistered vendor input type */\nexport type UnregisteredVendorInputType = {\n categoryIds: Array<InputMaybe<Scalars['String']['input']>>;\n dateTime: Array<InputMaybe<DateTimeInputType>>;\n email?: InputMaybe<Scalars['String']['input']>;\n inviterId: Scalars['ID']['input'];\n name: Scalars['String']['input'];\n region: Scalars['String']['input'];\n};\n\n/** Unregistered vendor invitation type */\nexport type UnregisteredVendorInvitationType = {\n dateTime: Array<DateTimeType>;\n inviterId: Scalars['ID']['output'];\n};\n\n/** Unregistered vendor type */\nexport type UnregisteredVendorType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n categoryIds: Array<Scalars['String']['output']>;\n claimed: Scalars['Boolean']['output'];\n claimedAt?: Maybe<Scalars['Date']['output']>;\n claimedByUserId?: Maybe<Scalars['ID']['output']>;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n email?: Maybe<Scalars['String']['output']>;\n invitations: Array<UnregisteredVendorInvitationType>;\n name: Scalars['String']['output'];\n region: Scalars['String']['output'];\n updatedAt: Scalars['Date']['output'];\n};\n\nexport type UpdateAdResponse = {\n data?: Maybe<AdType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateAffiliateDetailsResponse = {\n data?: Maybe<AffiliateType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateAppSettingsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateDailyClueGameResponse = {\n data?: Maybe<GameDocType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateEventInfoResponse = {\n data?: Maybe<EventInfoType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateEventResponse = {\n data?: Maybe<EventType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateGoogleImportedMarketsResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdatePartnerResponse = {\n data?: Maybe<PartnerType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdatePostResponse = {\n data?: Maybe<PostType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdatePuzzleGameResponse = {\n data?: Maybe<GameDocType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateRelationResponse = {\n data?: Maybe<RelationType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateSchoolResponse = {\n data?: Maybe<SchoolType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateSubscriptionPlanResponse = {\n data?: Maybe<SubscriptionStatusType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateUnregisteredVendorResponse = {\n data?: Maybe<UnregisteredVendorType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateUserResponse = {\n data?: Maybe<UserType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateVendorInfoResponse = {\n data?: Maybe<VendorInfoType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UpdateVendorResponse = {\n data?: Maybe<VendorType>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\nexport type UserActivities = {\n /** Favourites of the user */\n favourites?: Maybe<UserFavourites>;\n /** Going events of the user */\n going?: Maybe<UserGoingEvents>;\n /** Interested events of the user */\n interested?: Maybe<UserInterestedEvents>;\n /** Present events of the user */\n present?: Maybe<UserPresentEvents>;\n};\n\n/** User activity input type */\nexport type UserActivityInputType = {\n dateTime: DateTimeInputType;\n resourceId: Scalars['ID']['input'];\n};\n\n/** User activity type */\nexport type UserActivityType = {\n favourites?: Maybe<UserFavouritesType>;\n going?: Maybe<UserGoingType>;\n interested?: Maybe<UserInterestedType>;\n present?: Maybe<UserPresentType>;\n};\n\nexport type UserFavourites = {\n /** List of favourite events */\n events?: Maybe<Array<Maybe<EventListItemType>>>;\n /** List of favourite partners */\n partners?: Maybe<Array<Maybe<PartnerType>>>;\n /** List of favourite vendors */\n vendors?: Maybe<Array<Maybe<VendorType>>>;\n};\n\n/** User favourites type */\nexport type UserFavouritesType = {\n /** List of favourite events */\n events?: Maybe<Array<Scalars['ID']['output']>>;\n /** List of favourite partners */\n partners?: Maybe<Array<Scalars['ID']['output']>>;\n /** List of favourite vendors */\n vendors?: Maybe<Array<Scalars['ID']['output']>>;\n};\n\nexport type UserGoingEvents = {\n /** List of going events */\n events?: Maybe<Array<Maybe<EventListItemType>>>;\n};\n\n/** User going events */\nexport type UserGoingType = {\n events?: Maybe<Array<EventReferenceType>>;\n};\n\n/** User input type */\nexport type UserInputType = {\n active: Scalars['Boolean']['input'];\n avatar?: InputMaybe<ResourceImageInputType>;\n avatarUpload?: InputMaybe<ResourceImageUploadInputType>;\n email: Scalars['String']['input'];\n firstName: Scalars['String']['input'];\n isTester: Scalars['Boolean']['input'];\n lastName: Scalars['String']['input'];\n location?: InputMaybe<LocationInputType>;\n password?: InputMaybe<Scalars['String']['input']>;\n platform?: InputMaybe<OsPlatformEnumType>;\n preferredRegion: Scalars['String']['input'];\n termsAgreement?: InputMaybe<TermsAgreementInputType>;\n};\n\nexport type UserInterestedEvents = {\n /** List of interested events */\n events?: Maybe<Array<Maybe<EventListItemType>>>;\n};\n\n/** User interested events */\nexport type UserInterestedType = {\n events?: Maybe<Array<EventReferenceType>>;\n};\n\n/** User licence type */\nexport type UserLicenceType = {\n expiryDate: Scalars['Date']['output'];\n issuedDate: Scalars['Date']['output'];\n licenceType: LicencesEnumType;\n prevLicenceType?: Maybe<LicencesEnumType>;\n};\n\nexport type UserPresentEvents = {\n /** List of present events */\n events?: Maybe<Array<Maybe<EventListItemType>>>;\n};\n\n/** User present events */\nexport type UserPresentType = {\n events?: Maybe<Array<EventReferenceType>>;\n};\n\nexport type UserResource = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n logo: Scalars['String']['output'];\n name: Scalars['String']['output'];\n resourceType: ResourceTypeEnum;\n};\n\nexport type UserResources = {\n /** List of resources owned by the user */\n resources?: Maybe<Array<UserResource>>;\n};\n\n/** User role enum type */\nexport enum UserRoleEnumType {\n Admin = 'admin',\n Affiliate = 'affiliate',\n Customer = 'customer',\n Event = 'event',\n Partner = 'partner',\n School = 'school',\n SuperAdmin = 'super_admin',\n Vendor = 'vendor'\n}\n\n/** User type */\nexport type UserType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n affiliate?: Maybe<Scalars['ID']['output']>;\n avatar?: Maybe<ResourceImageType>;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n email: Scalars['String']['output'];\n events?: Maybe<Array<Scalars['ID']['output']>>;\n firstName: Scalars['String']['output'];\n game?: Maybe<Scalars['ID']['output']>;\n isTester: Scalars['Boolean']['output'];\n lastName: Scalars['String']['output'];\n licences?: Maybe<Array<UserLicenceType>>;\n location?: Maybe<LocationType>;\n overallPoints?: Maybe<Scalars['Int']['output']>;\n partner?: Maybe<Scalars['ID']['output']>;\n permissions: Array<Scalars['String']['output']>;\n platform?: Maybe<OsPlatformEnumType>;\n preferredRegion: Scalars['String']['output'];\n promoCodes?: Maybe<Array<Scalars['String']['output']>>;\n redeemedRewards?: Maybe<Array<RedeemedRewardType>>;\n roles: Array<UserRoleEnumType>;\n school?: Maybe<Scalars['ID']['output']>;\n stripe?: Maybe<StripeSubscriptionType>;\n termsAgreement?: Maybe<TermsAgreementType>;\n updatedAt: Scalars['Date']['output'];\n userActivity?: Maybe<UserActivityType>;\n vendor?: Maybe<Scalars['ID']['output']>;\n};\n\n/** Verify password reset token input type */\nexport type ValidateVerificationTokenInputType = {\n email: Scalars['String']['input'];\n verificationToken: Scalars['String']['input'];\n verificationType?: InputMaybe<VerificationEnumType>;\n};\n\nexport type ValidateVerificationTokenResponse = {\n data?: Maybe<Scalars['Boolean']['output']>;\n message?: Maybe<Scalars['String']['output']>;\n};\n\n/** Vendor apply form price range input type */\nexport type VendorApplyFormPriceRangeInputType = {\n max: Scalars['String']['input'];\n min: Scalars['String']['input'];\n};\n\n/** Vendor apply form stall size input type */\nexport type VendorApplyFormStallSizeInputType = {\n depth: Scalars['String']['input'];\n width: Scalars['String']['input'];\n};\n\n/** Vendor attributes input type */\nexport type VendorAttributesInputType = {\n details?: InputMaybe<Scalars['String']['input']>;\n isRequired: Scalars['Boolean']['input'];\n};\n\n/** Vendor attributes type */\nexport type VendorAttributesType = {\n details?: Maybe<Scalars['String']['output']>;\n isRequired: Scalars['Boolean']['output'];\n};\n\n/** Vendor availability input type */\nexport type VendorAvailabilityInputType = {\n corporate?: InputMaybe<Scalars['Boolean']['input']>;\n private?: InputMaybe<Scalars['Boolean']['input']>;\n school?: InputMaybe<Scalars['Boolean']['input']>;\n};\n\n/** Vendor availability type */\nexport type VendorAvailabilityType = {\n corporate: Scalars['Boolean']['output'];\n private: Scalars['Boolean']['output'];\n school: Scalars['Boolean']['output'];\n};\n\n/** Vendor calendar input type */\nexport type VendorCalendarInputType = {\n active?: InputMaybe<Scalars['Boolean']['input']>;\n calendarData?: InputMaybe<Array<InputMaybe<VendorLocationsInputType>>>;\n};\n\n/** Vendor calendar type */\nexport type VendorCalendarType = {\n active?: Maybe<Scalars['Boolean']['output']>;\n calendarData?: Maybe<Array<VendorLocationsType>>;\n};\n\n/** Vendor compliance input type */\nexport type VendorComplianceInputType = {\n foodBeverageLicense?: InputMaybe<Scalars['Boolean']['input']>;\n liabilityInsurance?: InputMaybe<Scalars['Boolean']['input']>;\n};\n\n/** Vendor compliance type */\nexport type VendorComplianceType = {\n foodBeverageLicense: Scalars['Boolean']['output'];\n liabilityInsurance: Scalars['Boolean']['output'];\n};\n\n/** Vendor Date time type */\nexport type VendorDateTimeType = {\n dateStatus?: Maybe<EventDateStatusEnumType>;\n endDate?: Maybe<Scalars['String']['output']>;\n endTime?: Maybe<Scalars['String']['output']>;\n startDate?: Maybe<Scalars['String']['output']>;\n startTime?: Maybe<Scalars['String']['output']>;\n};\n\n/** Vendor type enum */\nexport enum VendorEnumType {\n Shop = 'Shop',\n Stallholder = 'Stallholder'\n}\n\n/** Vendor info input type */\nexport type VendorInfoInputType = {\n compliance?: InputMaybe<VendorComplianceInputType>;\n documents?: InputMaybe<Array<InputMaybe<ResourceImageInputType>>>;\n documentsUpload?: InputMaybe<Array<InputMaybe<ResourceImageUploadInputType>>>;\n product: VendorProductInputType;\n requirements?: InputMaybe<VendorRequirementsInputType>;\n stallInfo: VendorStallInfoInputType;\n vendorId: Scalars['String']['input'];\n};\n\n/** Vendor info type */\nexport type VendorInfoType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n compliance?: Maybe<VendorComplianceType>;\n documents?: Maybe<Array<ResourceImageType>>;\n product: VendorProductType;\n requirements?: Maybe<VendorRequirementsType>;\n stallInfo: VendorStallInfoType;\n vendorId: Scalars['ID']['output'];\n};\n\n/** Vendor input type */\nexport type VendorInputType = {\n active: Scalars['Boolean']['input'];\n availability?: InputMaybe<VendorAvailabilityInputType>;\n calendar?: InputMaybe<VendorCalendarInputType>;\n categories: Array<InputMaybe<CategoryInputType>>;\n claimed?: InputMaybe<Scalars['Boolean']['input']>;\n contactDetails?: InputMaybe<ContactDetailsInputType>;\n cover: ResourceImageInputType;\n coverUpload?: InputMaybe<ResourceImageUploadInputType>;\n description: Scalars['String']['input'];\n foodTruck: Scalars['Boolean']['input'];\n images?: InputMaybe<Array<InputMaybe<ResourceImageInputType>>>;\n imagesUpload?: InputMaybe<Array<InputMaybe<ResourceImageUploadInputType>>>;\n logo?: InputMaybe<ResourceImageInputType>;\n logoUpload?: InputMaybe<ResourceImageUploadInputType>;\n name: Scalars['String']['input'];\n products?: InputMaybe<VendorProductsInputType>;\n promoCodes?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n region: Scalars['String']['input'];\n socialMedia?: InputMaybe<Array<InputMaybe<SocialMediaInputType>>>;\n termsAgreement: TermsAgreementInputType;\n unregisteredVendorId?: InputMaybe<Scalars['ID']['input']>;\n vendorType: VendorEnumType;\n};\n\n/** Vendor location date time input type */\nexport type VendorLocationDateTimeInputType = {\n dateStatus?: InputMaybe<EventDateStatusEnumType>;\n endDate?: InputMaybe<Scalars['String']['input']>;\n endTime?: InputMaybe<Scalars['String']['input']>;\n startDate?: InputMaybe<Scalars['String']['input']>;\n startTime?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Vendor locations input type */\nexport type VendorLocationsInputType = {\n dateTime?: InputMaybe<VendorLocationDateTimeInputType>;\n description?: InputMaybe<Scalars['String']['input']>;\n location?: InputMaybe<LocationInputType>;\n resourceCover?: InputMaybe<Scalars['String']['input']>;\n resourceId?: InputMaybe<Scalars['String']['input']>;\n resourceLogo?: InputMaybe<Scalars['String']['input']>;\n resourceName?: InputMaybe<Scalars['String']['input']>;\n resourceType?: InputMaybe<ResourceTypeEnum>;\n};\n\n/** Vendor locations type */\nexport type VendorLocationsType = {\n dateTime?: Maybe<VendorDateTimeType>;\n description?: Maybe<Scalars['String']['output']>;\n location?: Maybe<LocationType>;\n};\n\n/** Vendor product input type */\nexport type VendorProductInputType = {\n foodFlavors: Array<InputMaybe<FoodFlavorEnumType>>;\n packaging: Array<InputMaybe<Scalars['String']['input']>>;\n priceRange: VendorApplyFormPriceRangeInputType;\n producedIn?: InputMaybe<Array<InputMaybe<Scalars['String']['input']>>>;\n};\n\n/** Vendor product list input type */\nexport type VendorProductListInputType = {\n description?: InputMaybe<Scalars['String']['input']>;\n name: Scalars['String']['input'];\n price: Scalars['Float']['input'];\n priceUnit: Scalars['String']['input'];\n productGroups?: InputMaybe<Array<Scalars['String']['input']>>;\n};\n\n/** Vendor product list type */\nexport type VendorProductListType = {\n description?: Maybe<Scalars['String']['output']>;\n name: Scalars['String']['output'];\n price: Scalars['Float']['output'];\n priceUnit: Scalars['String']['output'];\n productGroups?: Maybe<Array<Scalars['String']['output']>>;\n};\n\n/** Vendor product type */\nexport type VendorProductType = {\n foodFlavors: Array<FoodFlavorEnumType>;\n packaging?: Maybe<Array<Scalars['String']['output']>>;\n priceRange: MaxMinType;\n producedIn?: Maybe<Array<Scalars['String']['output']>>;\n};\n\n/** Vendor products input type */\nexport type VendorProductsInputType = {\n active?: InputMaybe<Scalars['Boolean']['input']>;\n productsList?: InputMaybe<Array<InputMaybe<VendorProductListInputType>>>;\n};\n\n/** Vendor products type */\nexport type VendorProductsType = {\n active?: Maybe<Scalars['Boolean']['output']>;\n productsList?: Maybe<Array<VendorProductListType>>;\n};\n\n/** Vendor requirements input type */\nexport type VendorRequirementsInputType = {\n electricity: VendorAttributesInputType;\n gazebo: VendorAttributesInputType;\n table: VendorAttributesInputType;\n};\n\n/** Vendor requirements type */\nexport type VendorRequirementsType = {\n electricity: VendorAttributesType;\n gazebo: VendorAttributesType;\n table: VendorAttributesType;\n};\n\n/** Vendor stall info input type */\nexport type VendorStallInfoInputType = {\n size: VendorApplyFormStallSizeInputType;\n};\n\n/** Vendor stall info type */\nexport type VendorStallInfoType = {\n size: DepthWidthType;\n};\n\n/** Vendor type */\nexport type VendorType = {\n _id: Scalars['ID']['output'];\n active: Scalars['Boolean']['output'];\n adIds?: Maybe<Array<Maybe<Scalars['ID']['output']>>>;\n approvedAt?: Maybe<Scalars['Date']['output']>;\n availability?: Maybe<VendorAvailabilityType>;\n calendar?: Maybe<VendorCalendarType>;\n categories: Array<CategoryType>;\n claimed: Scalars['Boolean']['output'];\n contactDetails?: Maybe<ContactDetailsType>;\n cover: ResourceImageType;\n createdAt: Scalars['Date']['output'];\n deletedAt?: Maybe<Scalars['Date']['output']>;\n description: Scalars['String']['output'];\n foodTruck: Scalars['Boolean']['output'];\n images?: Maybe<Array<ResourceImageType>>;\n logo?: Maybe<ResourceImageType>;\n name: Scalars['String']['output'];\n owner: OwnerType;\n posterUsage?: Maybe<PosterUsageType>;\n products?: Maybe<VendorProductsType>;\n promoCodes?: Maybe<Array<Scalars['String']['output']>>;\n rating?: Maybe<Scalars['Float']['output']>;\n region: Scalars['String']['output'];\n relatedPost?: Maybe<RelatedPostType>;\n relations?: Maybe<Array<ResourceRelationType>>;\n reviewCount?: Maybe<Scalars['Int']['output']>;\n sharePublic?: Maybe<SocialShareResourceType>;\n shareRelation?: Maybe<SocialShareResourceType>;\n slug: Scalars['String']['output'];\n socialMedia?: Maybe<Array<SocialMediaType>>;\n termsAgreement?: Maybe<TermsAgreementType>;\n unregisteredVendorId?: Maybe<Scalars['ID']['output']>;\n updatedAt: Scalars['Date']['output'];\n vendorInfoId?: Maybe<Scalars['ID']['output']>;\n vendorType: VendorEnumType;\n};\n\n/** Verification type enum */\nexport enum VerificationEnumType {\n Register = 'register',\n ResetPassword = 'resetPassword'\n}\n\n/** Video content data input type */\nexport type VideoContentDataInputType = {\n source: Scalars['String']['input'];\n title?: InputMaybe<Scalars['String']['input']>;\n};\n\n/** Video content data type */\nexport type VideoContentDataType = {\n source: Scalars['String']['output'];\n title?: Maybe<Scalars['String']['output']>;\n};\n","import { OptionItem } from \"src/types\";\n\n/**\n * Convert an array of strings to an array of objects with label and value properties.\n */\nexport const mapArrayToOptions = (items: string[]): OptionItem[] =>\n items.map((item) => ({\n label: item,\n value: item,\n }));\n","import dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat.js\";\nimport isSameOrAfter from \"dayjs/plugin/isSameOrAfter.js\";\nimport timezone from \"dayjs/plugin/timezone.js\";\nimport utc from \"dayjs/plugin/utc.js\";\n\nimport { EventDateStatusEnumType } from \"../generated/graphql\";\n\nimport { mapArrayToOptions } from \"./mapArrayToOptions\";\n\nexport const dateFormat = \"DD-MM-YYYY\";\nexport const timeFormat = \"HH:mm\";\n\n// Enable custom format parsing\ndayjs.extend(customParseFormat);\ndayjs.extend(utc);\ndayjs.extend(timezone);\ndayjs.extend(isSameOrAfter);\n\nconst NZ_TZ = \"Pacific/Auckland\";\n\nexport function toNZTime(date?: Date | string) {\n return date ? dayjs(date).tz(NZ_TZ) : dayjs().tz(NZ_TZ);\n}\n\n/** Start of the calendar day in Pacific/Auckland (daily games, streaks, etc.). */\nexport function nzStartOfDay(\n input?: Date | string | number | null,\n): dayjs.Dayjs {\n if (input == null) {\n return dayjs().tz(NZ_TZ).startOf(\"day\");\n }\n return dayjs.tz(input, NZ_TZ).startOf(\"day\");\n}\n\ntype DateFormat = \"date\" | \"time\" | \"datetime\";\n\n/**\n * Format a date string to a more readable format.\n * @param dateStr - the date string\n * @param timeStr - optional time string\n * @param display - 'date' | 'time' | 'datetime'\n * @returns formatted string based on display option\n */\nexport const formatDate = (\n dateStr: string,\n display: DateFormat = \"datetime\",\n timeStr?: string,\n) => {\n // Combine date and time into a single string if time is provided\n const dateTimeStr = timeStr ? `${dateStr} ${timeStr}` : dateStr;\n\n // Parse with formats\n const dateTime = timeStr\n ? dayjs(dateTimeStr, `${dateFormat} ${timeFormat}`)\n : dayjs(dateStr, dateFormat);\n\n // Format parts\n const formattedDate = dateTime.format(\"dddd, D MMMM, YYYY\");\n const formattedTime = dateTime.format(\"h:mm a\");\n\n // Return based on display option\n switch (display) {\n case \"date\":\n return formattedDate;\n case \"time\":\n return formattedTime;\n case \"datetime\":\n return `${formattedDate} at ${formattedTime}`;\n default:\n return formattedDate;\n }\n};\n\nexport const getCurrentAndFutureDates = <\n T extends { startDate: string; startTime: string },\n>(\n dates: T[],\n): T[] => {\n const now = dayjs(); // current date and time\n\n return dates.filter((dateObj) => {\n const dateTime = dayjs(\n `${dateObj.startDate} ${dateObj.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n return dateTime.isSameOrAfter(now);\n });\n};\n\nexport const isFutureDatesBeforeThreshold = (\n date: {\n startDate: string;\n startTime: string;\n },\n minHoursFromNow: number,\n): boolean => {\n const threshold = minHoursFromNow\n ? dayjs().add(minHoursFromNow, \"hour\")\n : dayjs().startOf(\"day\");\n\n const dateTime = dayjs(\n `${date.startDate} ${date.startTime}`,\n `${dateFormat} ${timeFormat}`,\n );\n\n return dateTime.isSameOrAfter(threshold);\n};\n\nexport const formatTimestamp = (timestamp: string) => {\n const formattedDate = toNZTime(timestamp).format(dateFormat);\n\n return formatDate(formattedDate, \"date\");\n};\n\nexport const isIsoDateString = (value: unknown): value is string => {\n return typeof value === \"string\" && !Number.isNaN(Date.parse(value));\n};\n\n/**\n * Sort an array of date strings by their proximity to the current date.\n * @param dates - The array of date strings to sort.\n * @returns - The sorted array of date strings.\n */\nexport function sortDatesChronologically<\n T extends { startDate: string; startTime: string },\n>(dates: T[]): T[] {\n if (!dates?.length) {\n return [];\n }\n\n return [...dates].sort((a, b) => {\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const dateA = dayjs(`${a.startDate} ${a.startTime}`, dateTimeFormat);\n const dateB = dayjs(`${b.startDate} ${b.startTime}`, dateTimeFormat);\n return dateA.valueOf() - dateB.valueOf(); // chronological order\n });\n}\n\nexport const futureTimePeriods = mapArrayToOptions(\n Object.values(EventDateStatusEnumType),\n)\n .filter(\n (period) =>\n period.value !== EventDateStatusEnumType.StartingSoon &&\n period.value !== EventDateStatusEnumType.Canceled &&\n period.value !== EventDateStatusEnumType.Rescheduled &&\n period.value !== EventDateStatusEnumType.Started &&\n period.value !== EventDateStatusEnumType.Ended &&\n period.value !== EventDateStatusEnumType.Invalid,\n )\n .map((period) => ({\n label: period.value.replaceAll(\"_\", \" \"),\n value: period.value,\n }));\n","/** Path segments for relation share URLs — must match mobile `RelationTitle` values. */\nexport const RELATION_SHARE_INVITATION = \"invitation\" as const;\nexport const RELATION_SHARE_APPLICATION = \"application\" as const;\n\n/**\n * Bump when relation overlay SVG/layout changes so og:image URLs and API ETags\n * invalidate after deploy. Facebook caches preview JPEGs by exact URL — changing\n * this value forces crawlers to fetch a fresh image after server deploy.\n */\nexport const RELATION_OVERLAY_LAYOUT_VERSION = 43;\n\nexport const RELATION_SHARE_RESOURCE_TYPES = [\n RELATION_SHARE_INVITATION,\n RELATION_SHARE_APPLICATION,\n] as const;\n\nexport type RelationShareResourceType =\n (typeof RELATION_SHARE_RESOURCE_TYPES)[number];\n\nexport function isRelationShareResourceType(\n resourceType: string,\n): resourceType is RelationShareResourceType {\n return (RELATION_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\nexport {\n RELATION_SHARE_APPLICATION,\n RELATION_SHARE_INVITATION,\n RELATION_OVERLAY_LAYOUT_VERSION,\n RELATION_SHARE_RESOURCE_TYPES,\n isRelationShareResourceType,\n type RelationShareResourceType,\n} from \"./relationShareTypes\";\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_WIDTH = 1200;\n\n/** Open Graph recommended preview size (must match admin `SHARE_OG_IMAGE_*`). */\nexport const SOCIAL_IMAGE_HEIGHT = 630;\n\n/** Keep in sync with `CLUEMART_MAIN_DOMAIN_URL` in utils (avoid utils↔sharing import cycle). */\nexport const SHARE_SITE_URL = \"https://cluemart.co.nz\";\n\n/** Calendar marker for invitation market-dates share copy (U+1F4C5 📅). */\nexport const SHARE_CALENDAR_ICON = \"\\u{1F4C5}\";\n\nexport const SHARE_MARKET_DATES_SECTION_HEADING = `${SHARE_CALENDAR_ICON} Market dates:`;\n\n/** Check mark for invitation requirements share copy (U+2705 ✅). */\nexport const SHARE_CHECKMARK_ICON = \"\\u{2705}\";\n\nexport const SHARE_REQUIREMENTS_SECTION_HEADING = `${SHARE_CHECKMARK_ICON} Requirements:`;\n\n/** Information marker for tags share copy (U+2139 ℹ️). */\nexport const SHARE_INFO_ICON = \"\\u{2139}\\u{FE0F}\";\n\nexport const SHARE_TAGS_SECTION_HEADING = `${SHARE_INFO_ICON} Tags:`;\n\n/** Standalone line when `rainOrShine` is true (invitation share + OG section parsing). */\nexport const SHARE_RAIN_OR_SHINE_LINE = \"Rain or Shine\";\n\n/** Standalone line when `foodTruck` is true (application share + OG section parsing). */\nexport const SHARE_FOOD_TRUCK_LINE = \"Food Truck\";\n\n/** Prefix for application compliance line (share text + OG section parsing). */\nexport const SHARE_COMPLIANCE_PREFIX = `${SHARE_CHECKMARK_ICON} Compliance:`;\n\n/** Label tag for application categories share copy (U+1F3F7 🏷️). */\nexport const SHARE_CATEGORY_ICON = \"\\u{1F3F7}\\u{FE0F}\";\n\nexport const SHARE_CATEGORIES_SECTION_HEADING = `${SHARE_CATEGORY_ICON} Categories:`;\n\n/** Straight ruler for application stall-size share copy (U+1F4CF 📏). */\nexport const SHARE_RULER_ICON = \"\\u{1F4CF}\";\n\n/** Prefix for application stall-size line (share text + OG section parsing). */\nexport const SHARE_STALL_SIZE_PREFIX = `${SHARE_RULER_ICON} Stall size:`;\n\n/** Dollar marker for price-range share copy (U+0024 $ — text, not emoji, so it stays black). */\nexport const SHARE_DOLLAR_ICON = \"$\";\n\n/** Prefix for application price-range line (share text + OG section parsing). */\nexport const SHARE_PRICE_RANGE_PREFIX = `${SHARE_DOLLAR_ICON} Price range:`;\n\n/** Alarm clock for invitation application-deadline share copy (U+23F0 ⏰). */\nexport const SHARE_CLOCK_ICON = \"\\u{23F0}\";\n\n/** Prefix for the invitation application-deadline sentence (share text + OG section parsing). */\nexport const SHARE_APPLICATION_DEADLINE_PREFIX = `${SHARE_CLOCK_ICON} Application deadline:`;\n\n/** First line on share sheet messages, Facebook SDK quote, and `og:description`. */\nexport const SHARE_MORE_INFO_LINE = \"Shared from ClueMart\";\n\nexport const DEFAULT_SHARE_OG_IMAGE = `${SHARE_SITE_URL}/assets/logo.webp`;\n\nexport const RESOURCE_SHARE_TYPES = [\n \"market\",\n \"stallholder\",\n \"partner\",\n \"affiliate\",\n \"school\",\n] as const;\n\nexport type ResourceShareType = (typeof RESOURCE_SHARE_TYPES)[number];\n\nexport const POST_SHARE_RESOURCE_TYPES = [\n \"market_faces\",\n \"clue_bites\",\n \"play_and_win\",\n] as const;\n\nexport type PostShareResourceType = (typeof POST_SHARE_RESOURCE_TYPES)[number];\n\nexport type ShareResourceType =\n | ResourceShareType\n | PostShareResourceType\n | RelationShareResourceType;\n\nexport const SHARE_RESOURCE_LABEL: Record<ShareResourceType, string> = {\n [RELATION_SHARE_APPLICATION]: \"Application\",\n [RELATION_SHARE_INVITATION]: \"Invitation\",\n clue_bites: \"Clue Bites\",\n market_faces: \"Market Faces\",\n play_and_win: \"Play & Win\",\n market: \"Market\",\n partner: \"Partner\",\n stallholder: \"Stallholder\",\n affiliate: \"Affiliate\",\n school: \"School\",\n};\n\nexport function isPostShareResourceType(\n resourceType: ShareResourceType,\n): resourceType is PostShareResourceType {\n return (POST_SHARE_RESOURCE_TYPES as readonly string[]).includes(\n resourceType,\n );\n}\n","import { SHARE_MORE_INFO_LINE } from \"./constants\";\n\n/** Blank line between share sections — title, address, dates, URL, etc. */\nexport const SHARE_DESCRIPTION_SECTION_BREAK = \"\\n\\n\";\n\n/**\n * Line break for `og:description` meta content. Use instead of `\\n` — Next.js\n * HTML serialisation collapses literal newlines to spaces; Facebook reads `&#10;`.\n */\nexport const OG_DESCRIPTION_LINE_BREAK = \"&#10;\";\n\n/** Joins OG description sections for Facebook link cards (and Twitter). */\nexport function joinShareOgDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .map((section) => (typeof section === \"string\" ? section.trim() : \"\"))\n .filter((section): section is string => section.length > 0)\n .map((section) => section.replace(/\\n/g, OG_DESCRIPTION_LINE_BREAK))\n .join(OG_DESCRIPTION_LINE_BREAK);\n}\n\nexport function joinShareDescriptionSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return sections\n .filter((section): section is string => Boolean(section))\n .join(SHARE_DESCRIPTION_SECTION_BREAK);\n}\n\n/** Prepends the standard share branding line when assembling display text. */\nexport function appendShareMoreInfoLine(description: string): string {\n const trimmed = description.trim();\n if (trimmed.startsWith(SHARE_MORE_INFO_LINE)) {\n return trimmed;\n }\n if (trimmed.length === 0) {\n return SHARE_MORE_INFO_LINE;\n }\n return joinShareDescriptionSections([SHARE_MORE_INFO_LINE, trimmed]);\n}\n","import { SHARE_MORE_INFO_LINE, type ShareResourceType } from \"./constants\";\nimport {\n joinShareOgDescriptionSections,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\nexport {\n joinShareOgDescriptionSections,\n OG_DESCRIPTION_LINE_BREAK,\n SHARE_DESCRIPTION_SECTION_BREAK,\n} from \"./joinShareDescriptionSections\";\n\n/** Trims share text and collapses spaces per line; preserves `\\n\\n` section breaks. */\nconst LEADING_WHITESPACE = /^[ \\t]*/;\n\nexport function normalizeShareText(value: string | null | undefined): string {\n if (value == null) {\n return \"\";\n }\n return value\n .trim()\n .split(\"\\n\")\n .map((line) => {\n const leading = LEADING_WHITESPACE.exec(line)?.[0] ?? \"\";\n const rest = line\n .slice(leading.length)\n .trim()\n .replace(/[ \\t]{2,}/g, \" \");\n return leading + rest;\n })\n .join(\"\\n\")\n .replace(/\\n{3,}/g, \"\\n\\n\");\n}\n\n/** Branding line placement — first so Facebook truncation does not drop it. */\nexport type ShareMessageFooterPlacement = \"before-body\";\n\nexport function shareMessageFooterPlacementForType(\n _shareType?: ShareResourceType,\n): ShareMessageFooterPlacement {\n return \"before-body\";\n}\n\n/**\n * Ordered sections for share-sheet / Facebook SDK quote text:\n * {@link SHARE_MORE_INFO_LINE} → title → description (full body or post caption).\n */\nexport function buildShareMessageSections(input: {\n title?: string | null;\n description?: string | null;\n shareType?: ShareResourceType;\n}): string[] {\n const title = normalizeShareText(input.title);\n const description = normalizeShareText(input.description);\n\n return [SHARE_MORE_INFO_LINE, title, description].filter(\n (section) => section.length > 0,\n );\n}\n\nexport function splitShareDescriptionSections(value: string): string[] {\n const trimmed = normalizeShareText(value);\n if (!trimmed) {\n return [];\n }\n\n if (/\\n\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n if (/\\n/.test(trimmed)) {\n return trimmed\n .split(/\\n+/)\n .map((section) => normalizeShareText(section))\n .filter((section) => section.length > 0);\n }\n\n return [trimmed];\n}\n\n/**\n * Formats share descriptions for Open Graph / Twitter meta tags.\n * Sections are joined with {@link OG_DESCRIPTION_LINE_BREAK} (`&#10;`).\n */\nexport function normalizeShareOgDescription(value: string): string {\n return joinShareOgDescriptionSections(splitShareDescriptionSections(value));\n}\n\n/**\n * Quote text for Facebook ShareDialog on iOS (`ShareLinkContent.quote`).\n *\n * Title + blank lines + body sections. URL is omitted — `contentUrl` supplies\n * the link card (OG on resource landing pages controls card title/image).\n */\nexport function buildFacebookShareQuote(\n title: string,\n description: string,\n shareType?: ShareResourceType,\n): string | undefined {\n const sections = buildShareMessageSections({ description, shareType, title });\n const quote = sections.join(SHARE_DESCRIPTION_SECTION_BREAK).trim();\n return quote.length > 0 ? quote : undefined;\n}\n\n/**\n * Open Graph / Twitter description from pre-built section strings.\n * Prefer {@link joinShareOgDescriptionSections} when sections are already known.\n */\nexport function buildShareOgDescriptionFromSections(\n sections: ReadonlyArray<string | false | null | undefined>,\n): string {\n return joinShareOgDescriptionSections(sections);\n}\n\n/** Open Graph / Twitter description — body sections only (`og:title` is separate). */\nexport function buildShareOgDescription(\n _title: string,\n description: string,\n): string {\n return joinShareOgDescriptionSections(\n splitShareDescriptionSections(description),\n );\n}\n\n/**\n * Strips emoji and symbols that do not render reliably in overlay SVG fonts.\n * Keeps letters, numbers, whitespace, and common sentence punctuation.\n */\nexport function stripOverlaySubtitleSpecialChars(text: string): string {\n const regex = /[^\\p{L}\\p{M}\\p{N}\\s.,!?'\"&\\-():;/]/gu;\n const result = text\n .replace(/\\p{Extended_Pictographic}/gu, \"\")\n .replace(regex, \"\");\n return result.trim();\n}\n","import { LicencesEnumType } from \"../generated/graphql\";\n\n/** Tier keys in descending priority (highest first) for licence resolution. */\nexport const TIERS_BY_PRIORITY = [\"pro_plus\", \"pro\", \"standard\"] as const;\n\n/** Canonical subscription tier keys — matches Stripe/server tier identifiers. */\nexport type Tier = (typeof TIERS_BY_PRIORITY)[number];\n\n/** User-facing tier labels (decoupled from the Tier discriminant). */\nexport const TIER_DISPLAY_LABELS: Record<Tier, string> = {\n pro: \"Pro\",\n // eslint-disable-next-line camelcase\n pro_plus: \"Pro+ Ads\",\n standard: \"Standard\",\n};\n\n/** Maps licence enums to their canonical tier key. */\nexport const TIER_FROM_LICENCE: Record<LicencesEnumType, Tier> = {\n [LicencesEnumType.ProEvent]: \"pro\",\n [LicencesEnumType.ProPlusEvent]: \"pro_plus\",\n [LicencesEnumType.ProPlusVendor]: \"pro_plus\",\n [LicencesEnumType.ProVendor]: \"pro\",\n [LicencesEnumType.StandardEvent]: \"standard\",\n [LicencesEnumType.StandardPartner]: \"standard\",\n [LicencesEnumType.StandardVendor]: \"standard\",\n [LicencesEnumType.StandardAffiliate]: \"standard\",\n [LicencesEnumType.StandardSchool]: \"standard\",\n};\n","/**\n * Canonical permission IDs. Add new permissions here first, then map them in\n * ROLE_PERMISSIONS. Prefer resource.action names (e.g. users.read).\n */\nexport const PERMISSIONS = [\n \"*\",\n // Dashboards\n \"dashboard.admin\",\n \"dashboard.affiliate\",\n \"dashboard.customer\",\n \"dashboard.event\",\n \"dashboard.partner\",\n \"dashboard.school\",\n \"dashboard.vendor\",\n // Admin: users\n \"users.delete\",\n \"users.read\",\n \"users.update\",\n // Admin: events\n \"events.create\",\n \"events.delete\",\n \"events.read\",\n \"events.update\",\n // Admin: vendors\n \"vendors.create\",\n \"vendors.delete\",\n \"vendors.read\",\n \"vendors.update\",\n // Admin: partners\n \"partners.create\",\n \"partners.delete\",\n \"partners.read\",\n \"partners.update\",\n // Admin: schools\n \"schools.create\",\n \"schools.delete\",\n \"schools.read\",\n \"schools.update\",\n // Admin: affiliates\n \"affiliates.create\",\n \"affiliates.delete\",\n \"affiliates.read\",\n \"affiliates.update\",\n // Admin: ads / posts / settings\n \"ads.create\",\n \"ads.delete\",\n \"ads.read\",\n \"ads.update\",\n \"posts.create\",\n \"posts.delete\",\n \"posts.read\",\n \"posts.update\",\n \"settings.read\",\n \"settings.update\",\n // Product: own-resource management\n \"affiliates.manage_own\",\n \"events.manage_own\",\n \"partners.manage_own\",\n \"schools.manage_own\",\n \"vendors.manage_own\",\n] as const;\n\nexport type Permission = (typeof PERMISSIONS)[number];\n\nexport const WILDCARD_PERMISSION: Permission = \"*\";\n","import { UserRoleEnumType } from \"../../generated/graphql\";\n\nimport type { Permission } from \"./permissions\";\n\nconst ADMIN_PERMISSIONS: readonly Permission[] = [\n \"dashboard.admin\",\n \"events.read\",\n \"events.create\",\n \"events.update\",\n \"events.delete\",\n \"vendors.read\",\n \"vendors.create\",\n \"vendors.update\",\n \"vendors.delete\",\n \"partners.read\",\n \"partners.create\",\n \"partners.update\",\n \"partners.delete\",\n \"schools.read\",\n \"schools.create\",\n \"schools.update\",\n \"schools.delete\",\n \"affiliates.read\",\n \"affiliates.create\",\n \"affiliates.update\",\n \"affiliates.delete\",\n \"ads.read\",\n \"ads.create\",\n \"ads.update\",\n \"ads.delete\",\n \"posts.read\",\n \"posts.create\",\n \"posts.update\",\n \"posts.delete\",\n];\n\n/**\n * Static role → permissions map. Source of truth for access.\n * Permissions for a user = union of all their roles' lists.\n */\nexport const ROLE_PERMISSIONS: Record<UserRoleEnumType, readonly Permission[]> =\n {\n [UserRoleEnumType.SuperAdmin]: [\"*\"],\n [UserRoleEnumType.Admin]: ADMIN_PERMISSIONS,\n [UserRoleEnumType.Customer]: [\"dashboard.customer\"],\n [UserRoleEnumType.Event]: [\"dashboard.event\", \"events.manage_own\"],\n [UserRoleEnumType.Vendor]: [\"dashboard.vendor\", \"vendors.manage_own\"],\n [UserRoleEnumType.Partner]: [\"dashboard.partner\", \"partners.manage_own\"],\n [UserRoleEnumType.School]: [\"dashboard.school\", \"schools.manage_own\"],\n [UserRoleEnumType.Affiliate]: [\n \"dashboard.affiliate\",\n \"affiliates.manage_own\",\n ],\n };\n","import { UserRoleEnumType } from \"../../generated/graphql\";\n\nimport type { Permission } from \"./permissions\";\nimport { WILDCARD_PERMISSION } from \"./permissions\";\nimport { ROLE_PERMISSIONS } from \"./rolePermissions\";\n\nfunction normalizeRoles(\n roles: UserRoleEnumType[] | null | undefined,\n): UserRoleEnumType[] {\n if (!roles?.length) return [];\n return roles;\n}\n\n/**\n * Union of permissions for the given roles (deduped, stable order).\n */\nexport function getPermissionsForRoles(\n roles: UserRoleEnumType[] | null | undefined,\n): Permission[] {\n const seen = new Set<Permission>();\n const result: Permission[] = [];\n\n for (const role of normalizeRoles(roles)) {\n const perms = ROLE_PERMISSIONS[role] ?? [];\n for (const perm of perms) {\n if (!seen.has(perm)) {\n seen.add(perm);\n result.push(perm);\n }\n }\n }\n\n return result;\n}\n\n/**\n * Check a concrete permission list (e.g. authUser.permissions) for a grant.\n * Honours the \"*\" wildcard.\n */\nexport function permissionListIncludes(\n permissions: readonly Permission[] | null | undefined,\n permission: Permission,\n): boolean {\n if (!permissions?.length) return false;\n const set = new Set(permissions);\n if (set.has(WILDCARD_PERMISSION)) return true;\n return set.has(permission);\n}\n\nexport function hasPermission(\n roles: UserRoleEnumType[] | null | undefined,\n permission: Permission,\n): boolean {\n return permissionListIncludes(getPermissionsForRoles(roles), permission);\n}\n\nexport function hasAnyPermission(\n roles: UserRoleEnumType[] | null | undefined,\n permissions: readonly Permission[],\n): boolean {\n if (permissions.length === 0) return false;\n const granted = getPermissionsForRoles(roles);\n return permissions.some((p) => permissionListIncludes(granted, p));\n}\n\nexport function hasAllPermissions(\n roles: UserRoleEnumType[] | null | undefined,\n permissions: readonly Permission[],\n): boolean {\n if (permissions.length === 0) return true;\n const granted = getPermissionsForRoles(roles);\n return permissions.every((p) => permissionListIncludes(granted, p));\n}\n\nexport function hasRole(\n roles: UserRoleEnumType[] | null | undefined,\n role: UserRoleEnumType,\n): boolean {\n return normalizeRoles(roles).includes(role);\n}\n\nexport function hasAnyRole(\n roles: UserRoleEnumType[] | null | undefined,\n checkRoles: readonly UserRoleEnumType[],\n): boolean {\n if (checkRoles.length === 0) return false;\n const set = new Set(normalizeRoles(roles));\n return checkRoles.some((r) => set.has(r));\n}\n","import { UserRoleEnumType } from \"../../generated/graphql\";\n\nimport { hasAnyRole } from \"./helpers\";\n\n/** Roles allowed to sign into the admin web app (CUSTOMER-only is denied). */\nexport const ADMIN_APP_ROLES = [\n UserRoleEnumType.SuperAdmin,\n UserRoleEnumType.Admin,\n UserRoleEnumType.Event,\n UserRoleEnumType.Vendor,\n UserRoleEnumType.Partner,\n UserRoleEnumType.School,\n UserRoleEnumType.Affiliate,\n] as const;\n\nexport type AdminAppRole = (typeof ADMIN_APP_ROLES)[number];\n\nexport type AdminDashboardContext =\n | \"staff\"\n | \"event\"\n | \"vendor\"\n | \"partner\"\n | \"school\"\n | \"affiliate\";\n\nexport type AdminDashboardContextInfo = {\n context: AdminDashboardContext;\n home: string;\n label: string;\n /** Path prefixes that belong to this context (including home). */\n pathPrefixes: readonly string[];\n roles: readonly UserRoleEnumType[];\n};\n\n/**\n * Priority order for default home + role switcher.\n * Staff first, then product roles.\n */\nexport const ADMIN_DASHBOARD_CONTEXTS: readonly AdminDashboardContextInfo[] = [\n {\n context: \"staff\",\n home: \"/dashboard\",\n label: \"Staff\",\n pathPrefixes: [\"/dashboard\"],\n roles: [UserRoleEnumType.SuperAdmin, UserRoleEnumType.Admin],\n },\n {\n context: \"event\",\n home: \"/event/dashboard\",\n label: \"Events\",\n pathPrefixes: [\"/event\"],\n roles: [UserRoleEnumType.Event],\n },\n {\n context: \"vendor\",\n home: \"/vendor/dashboard\",\n label: \"Stallholders\",\n pathPrefixes: [\"/vendor\"],\n roles: [UserRoleEnumType.Vendor],\n },\n {\n context: \"partner\",\n home: \"/partner/dashboard\",\n label: \"Partners\",\n pathPrefixes: [\"/partner\"],\n roles: [UserRoleEnumType.Partner],\n },\n {\n context: \"school\",\n home: \"/school/dashboard\",\n label: \"Schools\",\n pathPrefixes: [\"/school\"],\n roles: [UserRoleEnumType.School],\n },\n {\n context: \"affiliate\",\n home: \"/affiliate/dashboard\",\n label: \"Affiliate\",\n pathPrefixes: [\"/affiliate\"],\n roles: [UserRoleEnumType.Affiliate],\n },\n] as const;\n\nexport function canAccessAdminApp(\n roles: UserRoleEnumType[] | null | undefined,\n): boolean {\n return hasAnyRole(roles, ADMIN_APP_ROLES);\n}\n\nexport function getUsableAdminContexts(\n roles: UserRoleEnumType[] | null | undefined,\n): AdminDashboardContextInfo[] {\n return ADMIN_DASHBOARD_CONTEXTS.filter((ctx) => hasAnyRole(roles, ctx.roles));\n}\n\nexport function getDefaultAdminHome(\n roles: UserRoleEnumType[] | null | undefined,\n preferredContext?: AdminDashboardContext | null,\n): string {\n const usable = getUsableAdminContexts(roles);\n if (usable.length === 0) {\n return \"/login\";\n }\n\n if (preferredContext) {\n const preferred = usable.find((ctx) => ctx.context === preferredContext);\n if (preferred) return preferred.home;\n }\n\n return usable[0].home;\n}\n\nexport function resolveAdminContextFromPath(\n pathname: string,\n): AdminDashboardContext | null {\n for (const ctx of ADMIN_DASHBOARD_CONTEXTS) {\n if (\n ctx.pathPrefixes.some(\n (prefix) => pathname === prefix || pathname.startsWith(`${prefix}/`),\n )\n ) {\n return ctx.context;\n }\n }\n return null;\n}\n\nexport function canAccessAdminPath(\n roles: UserRoleEnumType[] | null | undefined,\n pathname: string,\n): boolean {\n if (!canAccessAdminApp(roles)) return false;\n\n const context = resolveAdminContextFromPath(pathname);\n if (!context) {\n // Authenticated admin-app users may hit non-dashboard admin routes later;\n // unknown paths are allowed only if they have any admin-app role.\n return true;\n }\n\n const info = ADMIN_DASHBOARD_CONTEXTS.find((ctx) => ctx.context === context);\n if (!info) return false;\n return hasAnyRole(roles, info.roles);\n}\n","import {\n Control,\n FieldValues,\n FormState,\n UseFormGetValues,\n UseFormHandleSubmit,\n UseFormReset,\n UseFormSetValue,\n UseFormWatch,\n} from \"react-hook-form\";\n\nimport {\n EventEnumType,\n PostTypeEnum,\n ResourceTypeEnum,\n SocialMediaEnumType,\n LicencesEnumType,\n} from \"../generated/graphql\";\nimport type {\n DateTimeType,\n EventListItemType,\n LocationType,\n ResourceImageType,\n VendorType,\n} from \"../generated/graphql\";\n\nexport type {\n DateTimeType,\n LocationGeoType,\n LocationType,\n ResourceImageType,\n} from \"../generated/graphql\";\nexport const PROMO_CODE_PREFIX = \"CM-\";\n\nexport type PromoCodeType = `${typeof PROMO_CODE_PREFIX}${string}`;\n\nexport type Nullable<T> = {\n [K in keyof T]: T[K] | null | undefined;\n};\n\n/** Uniform mutation response envelope used across GraphQL mutations. */\nexport type MutationResponse<T> = {\n data: T | null;\n message: string | null;\n};\n\nexport type DeviceInfo = {\n appBuildNumber: string;\n appId: string;\n appVersion: string;\n brand: string;\n deviceName: string;\n installationId: string;\n manufacturer: string;\n modelName: string;\n osName: string;\n osVersion: string;\n timestamp: string;\n};\n\nexport type TermsAgreement = DeviceInfo & {\n termVersion: string;\n};\n\nexport type ResourceContactDetailsType = {\n email?: string | null;\n landlinePhone?: string | null;\n mobilePhone?: string | null;\n};\n\n/** Form/UI social links — optional fields. GraphQL `SocialMediaType` requires name+link. */\nexport type SocialMediaType = {\n name?: SocialMediaEnumType;\n link?: string;\n};\n\nexport type UserLicenceType = {\n expiryDate: Date;\n issuedDate: Date;\n licenceType: LicencesEnumType;\n prevLicenceType?: LicencesEnumType | null;\n};\n\nexport type OwnerType = {\n email: string;\n userId: string;\n};\n\nexport interface BaseResourceTypeFormData {\n _id?: string;\n active: boolean;\n contactDetails: ResourceContactDetailsType | null;\n cover: ResourceImageType;\n coverUpload?: ResourceImageType | null;\n description: string;\n images?: ResourceImageType[] | null;\n imagesUpload?: ResourceImageType[] | null;\n logo?: ResourceImageType | null;\n logoUpload?: ResourceImageType | null;\n name: string;\n promoCodes?: PromoCodeType[] | null;\n region: string;\n socialMedia: SocialMediaType[] | null;\n termsAgreement?: TermsAgreement | null;\n}\n\nexport type PosterUsageType = {\n month: string;\n count: number;\n};\n\nexport type RelatedPostType = {\n postActive: boolean;\n postId: string;\n postSlug: string;\n postType: PostTypeEnum;\n};\n\nexport type SocialShareResourceType = {\n qrCode: ResourceImageType;\n socialImage: ResourceImageType;\n};\n\nexport type BaseResourceType = Omit<\n BaseResourceTypeFormData,\n \"_id\" | \"coverUpload\" | \"imagesUpload\" | \"logoUpload\"\n> & {\n _id: string;\n adIds?: string[] | null;\n /** ISO 8601 UTC string — GraphQL Date scalar serializes to string, not JS Date. */\n approvedAt?: string | null;\n createdAt: string;\n deletedAt: string | null;\n owner: OwnerType;\n posterUsage?: PosterUsageType | null;\n rating?: number | null;\n relatedPost?: RelatedPostType | null;\n reviewCount?: number | null;\n slug: string;\n updatedAt: string | null;\n};\n\nexport type Region = {\n latitude: number;\n latitudeDelta: number;\n longitude: number;\n longitudeDelta: number;\n};\n\nexport type ResourceDetails = {\n dateTime: DateTimeType[] | null;\n description: string | null;\n eventStatus?: EventStatusType | null;\n location: LocationType | null;\n resourceCover: ResourceImageType | null;\n resourceId: string;\n resourceLogo: ResourceImageType | null;\n resourceName: string;\n resourceType: ResourceTypeEnum;\n};\n\nexport type GeocodeLocation = Pick<LocationType, \"latitude\" | \"longitude\">;\n\nexport type EventStatusType = {\n claimed: boolean;\n eventType: EventEnumType;\n googlePlaceId?: string | null;\n};\n\nexport interface FormField {\n disabled?: boolean;\n helperText?: string;\n isTextArea?: boolean;\n keyboardType?:\n | \"default\"\n | \"email-address\"\n | \"number-pad\"\n | \"url\"\n | \"decimal-pad\"\n | \"phone-pad\";\n name: string;\n placeholder: string;\n required?: boolean;\n secureTextEntry?: boolean;\n}\n\nexport interface FormDateField {\n dateMode: \"date\" | \"time\";\n helperText?: string;\n name: \"endDate\" | \"endTime\" | \"startDate\" | \"startTime\";\n placeholder: string;\n}\n\nexport interface SubcategoryItems {\n id: string;\n name: string;\n description?: string | null;\n}\n\nexport interface Subcategory {\n id: string;\n name: string;\n items?: SubcategoryItems[] | null;\n}\n\nexport interface Category {\n color?: string | null;\n description?: string | null;\n id: string;\n name: string;\n subcategories: Subcategory[];\n}\n\nexport type OptionItem = {\n value: string;\n label: string;\n};\n\nexport type ImageObjectType = {\n uri: string;\n type: string;\n name: string;\n};\n\nexport interface ResourceConnectionsType {\n events: EventListItemType[] | null;\n vendors: VendorType[] | null;\n}\n\nexport interface CreateFormData<T extends FieldValues> {\n control: Control<T, any>;\n fields: T;\n formState: FormState<T>;\n handleSubmit: UseFormHandleSubmit<T, any>;\n reset: UseFormReset<T>;\n setValue: UseFormSetValue<T>;\n watch: UseFormWatch<T>;\n getValues: UseFormGetValues<T>;\n}\n\nexport interface UseGetResourcesByRegionOptions {\n onlyClaimed?: boolean;\n limit?: number;\n offset?: number;\n}\n","/**\n * Adapters between generated GraphQL User/Auth entity types and form/UI shapes.\n *\n * GraphQL UserType is already the redacted client shape (no password /\n * refreshToken). These converters make remaining differences explicit:\n * branded promo codes, draft-optional terms, permission string → Permission,\n * and entity-to-form bridging (password fields stay empty — never round-trip).\n */\nimport type { Permission } from \"../auth/permissions\";\nimport { PERMISSIONS } from \"../auth/permissions\";\nimport type { UserType } from \"../generated/graphql\";\n\nimport type { PromoCodeType } from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\nimport type { UserFormData } from \"./user\";\n\nconst PERMISSION_SET = new Set<string>(PERMISSIONS);\n\nexport function toUserPermissions(\n permissions: ReadonlyArray<string>,\n): Permission[] {\n return permissions.filter((permission): permission is Permission =>\n PERMISSION_SET.has(permission),\n );\n}\n\nexport function toUserPromoCodes(\n codes: ReadonlyArray<string> | null | undefined,\n): PromoCodeType[] | null {\n if (codes == null) {\n return null;\n }\n return codes.map((code) => {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(\n `Invalid promo code from GraphQL: ${JSON.stringify(code)}`,\n );\n }\n return code as PromoCodeType;\n });\n}\n\n/**\n * Convert a stored GraphQL UserType into UserFormData for profile edit forms.\n *\n * Password / confirmPassword are never populated from the entity (GraphQL\n * UserType does not include them). avatarUpload is a local draft field.\n * termsAgreement is copied field-by-field (form TermsAgreement vs GraphQL\n * TermsAgreementType — same fields, kept explicit like other resources).\n */\nexport function userToFormData(user: UserType): UserFormData {\n return {\n _id: user._id,\n active: user.active,\n avatar: user.avatar ?? null,\n avatarUpload: null,\n confirmPassword: null,\n email: user.email,\n firstName: user.firstName,\n isTester: user.isTester,\n lastName: user.lastName,\n location: user.location ?? null,\n password: null,\n platform: user.platform ?? undefined,\n preferredRegion: user.preferredRegion,\n termsAgreement: user.termsAgreement\n ? {\n appBuildNumber: user.termsAgreement.appBuildNumber,\n appId: user.termsAgreement.appId,\n appVersion: user.termsAgreement.appVersion,\n brand: user.termsAgreement.brand,\n deviceName: user.termsAgreement.deviceName,\n installationId: user.termsAgreement.installationId,\n manufacturer: user.termsAgreement.manufacturer,\n modelName: user.termsAgreement.modelName,\n osName: user.termsAgreement.osName,\n osVersion: user.termsAgreement.osVersion,\n termVersion: user.termsAgreement.termVersion,\n timestamp: user.termsAgreement.timestamp,\n }\n : null,\n };\n}\n","/**\n * Adapters between generated GraphQL Event entity types and form/UI shapes.\n *\n * GraphQL entities are not form state — these converters make the differences\n * explicit (null vs undefined, required vs draft-optional social fields,\n * string categories vs form unions, EventType vs EventListItemType).\n */\nimport { ResourceTypeEnum, SocialMediaEnumType } from \"../generated/graphql\";\nimport type {\n DateTimeType,\n EventInfoType,\n EventListItemType,\n EventType,\n RefundPolicyType,\n RequirementType,\n SocialMediaType as GraphqlSocialMediaType,\n} from \"../generated/graphql\";\n\nimport type {\n DateTimeWithPriceType,\n EventFormData,\n EventInfoFormData,\n PaymentInfoType,\n RefundPolicy,\n Requirement,\n StallType,\n} from \"./event\";\nimport type { PromoCodeType, ResourceDetails, SocialMediaType } from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\n\nfunction toPromoCodeType(code: string): PromoCodeType {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(`Invalid promo code from GraphQL: ${JSON.stringify(code)}`);\n }\n return code as PromoCodeType;\n}\n\nconst REQUIREMENT_CATEGORIES = [\n \"Food Safety\",\n \"Environment\",\n \"Operations\",\n \"Legal & Safety\",\n] as const satisfies readonly Requirement[\"category\"][];\n\nconst REFUND_CATEGORIES = [\n \"Cancelled by Organiser\",\n \"Cancelled by Vendor\",\n] as const satisfies readonly RefundPolicy[\"category\"][];\n\nfunction isRequirementCategory(\n value: string,\n): value is Requirement[\"category\"] {\n return (REQUIREMENT_CATEGORIES as readonly string[]).includes(value);\n}\n\nfunction isRefundCategory(value: string): value is RefundPolicy[\"category\"] {\n return (REFUND_CATEGORIES as readonly string[]).includes(value);\n}\n\nfunction isSocialMediaName(value: string): value is SocialMediaEnumType {\n return (Object.values(SocialMediaEnumType) as string[]).includes(value);\n}\n\n/**\n * GraphQL SocialMediaType requires name+link once stored.\n * Form SocialMediaType keeps both optional so drafts can leave rows blank.\n */\nexport function toFormSocialMedia(\n socialMedia: ReadonlyArray<GraphqlSocialMediaType> | null | undefined,\n): SocialMediaType[] | null {\n if (socialMedia == null) {\n return null;\n }\n return socialMedia.map((item) => ({\n link: item.link,\n name: isSocialMediaName(item.name) ? item.name : undefined,\n }));\n}\n\nexport function toFormRequirement(item: RequirementType): Requirement {\n if (!isRequirementCategory(item.category)) {\n throw new Error(\n `Invalid requirement category from GraphQL: ${JSON.stringify(item.category)}`,\n );\n }\n return {\n category: item.category,\n label: item.label,\n value: item.value,\n };\n}\n\nexport function toFormRefundPolicy(item: RefundPolicyType): RefundPolicy {\n if (!isRefundCategory(item.category)) {\n throw new Error(\n `Invalid refund-policy category from GraphQL: ${JSON.stringify(item.category)}`,\n );\n }\n return {\n category: item.category,\n label: item.label,\n value: item.value,\n };\n}\n\nfunction toFormStallType(stall: {\n label: string;\n price: number;\n stallCapacity: number;\n}): StallType {\n return {\n label: stall.label,\n price: stall.price,\n stallCapacity: stall.stallCapacity,\n };\n}\n\nfunction toFormDateTimeWithPrice(\n row: EventInfoType[\"dateTime\"][number],\n): DateTimeWithPriceType {\n return {\n dateStatus: row.dateStatus,\n endDate: row.endDate,\n endTime: row.endTime,\n stallTypes: row.stallTypes.map(toFormStallType),\n startDate: row.startDate,\n startTime: row.startTime,\n };\n}\n\nfunction toFormPaymentInfo(\n item: EventInfoType[\"paymentInfo\"][number],\n): PaymentInfoType {\n return {\n accountHolderName: item.accountHolderName ?? undefined,\n accountNumber: item.accountNumber ?? undefined,\n link: item.link ?? undefined,\n paymentMethod: item.paymentMethod,\n };\n}\n\n/**\n * Project a full EventType onto EventListItemType fields.\n * Needed because the two GraphQL object types are distinct (__typename) even\n * when the list-item fields are a subset of the full event.\n */\nexport function eventToEventListItem(event: EventType): EventListItemType {\n return {\n _id: event._id,\n active: event.active,\n approvedAt: event.approvedAt,\n claimed: event.claimed,\n cover: event.cover,\n createdAt: event.createdAt,\n dateTime: event.dateTime,\n deletedAt: event.deletedAt,\n description: event.description,\n eventType: event.eventType,\n googlePlaceId: event.googlePlaceId,\n images: event.images ?? undefined,\n location: event.location,\n logo: event.logo,\n name: event.name,\n rainOrShine: event.rainOrShine,\n rating: event.rating,\n region: event.region,\n relations: event.relations ?? undefined,\n reviewCount: event.reviewCount,\n slug: event.slug,\n updatedAt: event.updatedAt,\n };\n}\n\n/**\n * Convert a stored Event entity into EventFormData for create/edit forms.\n * Maps GraphQL nullability (undefined / Maybe) onto form null defaults and\n * converts GraphQL social media into draft-optional form rows.\n */\nexport function eventToFormData(event: EventType): EventFormData {\n return {\n _id: event._id,\n active: event.active,\n claimed: event.claimed,\n contactDetails: event.contactDetails ?? null,\n cover: event.cover,\n coverUpload: null,\n dateTime: event.dateTime,\n description: event.description,\n eventType: event.eventType,\n googlePlaceId: event.googlePlaceId ?? null,\n images: event.images ?? null,\n imagesUpload: null,\n location: event.location,\n logo: event.logo ?? null,\n logoUpload: null,\n name: event.name,\n nzbn: event.nzbn,\n promoCodes: (event.promoCodes ?? [])\n .filter((code): code is string => code != null)\n .map(toPromoCodeType),\n provider: event.provider ?? null,\n rainOrShine: event.rainOrShine,\n region: event.region,\n socialMedia: toFormSocialMedia(event.socialMedia),\n tags: event.tags,\n termsAgreement: event.termsAgreement\n ? {\n appBuildNumber: event.termsAgreement.appBuildNumber,\n appId: event.termsAgreement.appId,\n appVersion: event.termsAgreement.appVersion,\n brand: event.termsAgreement.brand,\n deviceName: event.termsAgreement.deviceName,\n installationId: event.termsAgreement.installationId,\n manufacturer: event.termsAgreement.manufacturer,\n modelName: event.termsAgreement.modelName,\n osName: event.termsAgreement.osName,\n osVersion: event.termsAgreement.osVersion,\n termVersion: event.termsAgreement.termVersion,\n timestamp: event.termsAgreement.timestamp,\n }\n : null,\n };\n}\n\nexport type EventInfoToFormDataOptions = {\n /** Default checkbox rows merged in when editing so unchecked options remain visible. */\n defaultRefundPolicy?: RefundPolicy[];\n defaultRequirements?: Requirement[];\n};\n\n/**\n * Convert EventInfoType into EventInfoFormData, optionally merging default\n * requirement/refund checkbox options that are not yet saved on the server.\n */\nexport function eventInfoToFormData(\n eventInfo: EventInfoType,\n options: EventInfoToFormDataOptions = {},\n): EventInfoFormData {\n const savedRefund = eventInfo.refundPolicy.map(toFormRefundPolicy);\n const savedRequirements = (eventInfo.requirements ?? []).map(\n toFormRequirement,\n );\n\n const refundPolicy = [\n ...savedRefund,\n ...(options.defaultRefundPolicy ?? []).filter(\n (option) => !savedRefund.some((saved) => saved.label === option.label),\n ),\n ];\n\n const requirements = [\n ...savedRequirements,\n ...(options.defaultRequirements ?? []).filter(\n (option) =>\n !savedRequirements.some((saved) => saved.label === option.label),\n ),\n ];\n\n return {\n _id: eventInfo._id,\n applicationDeadlineHours: eventInfo.applicationDeadlineHours,\n dateTime: eventInfo.dateTime.map(toFormDateTimeWithPrice),\n eventId: eventInfo.eventId,\n packInTime: eventInfo.packInTime,\n paymentDueHours: eventInfo.paymentDueHours,\n paymentInfo: eventInfo.paymentInfo.map(toFormPaymentInfo),\n refundPolicy,\n requirements,\n };\n}\n\n/**\n * Build EventInfoFormData when no EventInfo exists yet, seeding date rows\n * from the parent Event's dateTime (empty stallTypes).\n */\nexport function eventToNewEventInfoFormData(\n event: EventType,\n options: {\n defaultRefundPolicy: RefundPolicy[];\n defaultRequirements: Requirement[];\n },\n): EventInfoFormData {\n return {\n applicationDeadlineHours: 48,\n dateTime: event.dateTime.map((date) => ({\n dateStatus: date.dateStatus,\n endDate: date.endDate,\n endTime: date.endTime,\n stallTypes: [],\n startDate: date.startDate,\n startTime: date.startTime,\n })),\n eventId: event._id,\n packInTime: 2,\n paymentDueHours: 24,\n paymentInfo: [],\n refundPolicy: options.defaultRefundPolicy,\n requirements: options.defaultRequirements,\n };\n}\n\n/**\n * Ad picker needs a non-null description string even when EventListItemType\n * omits description (list query field is optional in the schema).\n */\nexport function eventListItemToAdSelectableResource(event: EventListItemType): {\n _id: string;\n cover: NonNullable<EventListItemType[\"cover\"]>;\n description: string;\n images?: EventListItemType[\"images\"];\n logo?: EventListItemType[\"logo\"];\n name: string;\n region: string;\n slug: string;\n} {\n if (event.cover == null) {\n throw new Error(\n `EventListItem ${event._id} is missing cover; cannot build ad resource.`,\n );\n }\n return {\n _id: event._id,\n cover: event.cover,\n description: event.description ?? \"\",\n images: event.images,\n logo: event.logo,\n name: event.name,\n region: event.region,\n slug: event.slug,\n };\n}\n\n/**\n * Map a list-item event into ResourceDetails for mobile action sheets.\n * Coerces GraphQL `description?: string | null` to `string | null`.\n */\nexport function eventListItemToResourceDetails(\n item: EventListItemType,\n resourceType: ResourceTypeEnum,\n): ResourceDetails {\n return {\n dateTime: item.dateTime,\n description: item.description ?? null,\n eventStatus: {\n claimed: item.claimed,\n eventType: item.eventType,\n googlePlaceId: item.googlePlaceId ?? null,\n },\n location: item.location,\n resourceCover: item.cover ?? null,\n resourceId: item._id,\n resourceLogo: item.logo ?? null,\n resourceName: item.name,\n resourceType,\n };\n}\n\n/**\n * Seed create-event form fields from a Google-place EventListItem match.\n * Merges onto caller-supplied defaults (does not invent missing form fields).\n */\nexport function eventListItemToCreateEventFormSeed(\n item: EventListItemType,\n defaults: EventFormData,\n): EventFormData {\n return {\n ...defaults,\n active: item.active,\n claimed: item.claimed,\n dateTime: item.dateTime,\n description: item.description ?? \"\",\n eventType: item.eventType,\n googlePlaceId: item.googlePlaceId,\n location: { ...item.location },\n name: item.name,\n rainOrShine: item.rainOrShine,\n region: item.region,\n };\n}\n\n/**\n * Narrow EventDateTimeWithPriceType rows to DateTimeType for consumers that\n * only need schedule fields (e.g. JSON-LD enrichment).\n */\nexport function eventInfoDateTimeToDateTime(\n rows: EventInfoType[\"dateTime\"],\n): DateTimeType[] {\n return rows.map((row) => ({\n dateStatus: row.dateStatus,\n endDate: row.endDate,\n endTime: row.endTime,\n startDate: row.startDate,\n startTime: row.startTime,\n }));\n}\n","/**\n * Adapters between generated GraphQL Vendor-family entity types and form/UI shapes.\n *\n * GraphQL entities are not form state — these converters make differences\n * explicit (null vs undefined, CategoryType vs form Category, calendar\n * locations vs VendorCalendarData, social dual-shape).\n */\nimport {\n EventDateStatusEnumType,\n ResourceTypeEnum,\n} from \"../generated/graphql\";\nimport type {\n CategoryType,\n SubcategoryType,\n UnregisteredVendorType,\n VendorInfoType,\n VendorLocationsType,\n VendorType,\n} from \"../generated/graphql\";\n\nimport { toFormSocialMedia } from \"./eventAdapters\";\nimport type {\n Category,\n PromoCodeType,\n Subcategory,\n SubcategoryItems,\n} from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\nimport type {\n UnregisteredVendorFormData,\n VendorAttributes,\n VendorCalendarData,\n VendorFormData,\n VendorInfoFormData,\n} from \"./vendor\";\n\nfunction toPromoCodeType(code: string): PromoCodeType {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(`Invalid promo code from GraphQL: ${JSON.stringify(code)}`);\n }\n return code as PromoCodeType;\n}\n\nfunction toFormSubcategoryItem(item: {\n id?: string | null;\n name?: string | null;\n}): SubcategoryItems {\n return {\n id: item.id ?? \"\",\n name: item.name ?? \"\",\n };\n}\n\nfunction toFormSubcategory(sub: SubcategoryType): Subcategory {\n return {\n id: sub.id,\n items: (sub.items ?? []).map(toFormSubcategoryItem),\n name: sub.name ?? \"\",\n };\n}\n\n/**\n * GraphQL CategoryType omits form-only color/description and has optional\n * subcategory trees; form Category requires a subcategories array.\n */\nexport function toFormCategory(category: CategoryType): Category {\n return {\n id: category.id,\n name: category.name,\n subcategories: (category.subcategories ?? []).map(toFormSubcategory),\n };\n}\n\n/**\n * GraphQL VendorLocationsType is a thin calendar row (date/location/description).\n * Form VendorCalendarData extends ResourceDetails-like fields used by the\n * calendar UI — fill resource* fields with empty defaults when hydrating.\n */\nexport function toFormVendorCalendarData(\n row: VendorLocationsType,\n): VendorCalendarData {\n const dateTime = row.dateTime;\n if (dateTime == null) {\n throw new Error(\n \"Vendor calendar row is missing dateTime; cannot build form calendar entry.\",\n );\n }\n\n return {\n dateTime: {\n dateStatus: dateTime.dateStatus ?? EventDateStatusEnumType.Upcoming,\n endDate: dateTime.endDate ?? \"\",\n endTime: dateTime.endTime ?? \"\",\n startDate: dateTime.startDate ?? \"\",\n startTime: dateTime.startTime ?? \"\",\n },\n description: row.description ?? null,\n location: row.location ?? null,\n resourceCover: null,\n resourceId: \"\",\n resourceLogo: null,\n resourceName: \"\",\n resourceType: ResourceTypeEnum.Vendor,\n };\n}\n\nfunction toFormVendorAttributes(item: {\n details?: string | null;\n isRequired: boolean;\n}): VendorAttributes {\n return {\n details: item.details ?? null,\n isRequired: item.isRequired,\n };\n}\n\n/**\n * Convert a stored Vendor entity into VendorFormData for create/edit forms.\n */\nexport function vendorToFormData(vendor: VendorType): VendorFormData {\n return {\n _id: vendor._id,\n active: vendor.active,\n availability: vendor.availability\n ? {\n corporate: vendor.availability.corporate,\n private: vendor.availability.private,\n school: vendor.availability.school,\n }\n : undefined,\n calendar: vendor.calendar\n ? {\n active: vendor.calendar.active ?? null,\n calendarData: (vendor.calendar.calendarData ?? []).map(\n toFormVendorCalendarData,\n ),\n }\n : null,\n categories: vendor.categories.map(toFormCategory),\n claimed: vendor.claimed,\n contactDetails: vendor.contactDetails ?? null,\n cover: vendor.cover,\n coverUpload: null,\n description: vendor.description,\n foodTruck: vendor.foodTruck,\n images: vendor.images ?? null,\n imagesUpload: null,\n logo: vendor.logo ?? null,\n logoUpload: null,\n name: vendor.name,\n products: vendor.products\n ? {\n active: vendor.products.active ?? null,\n productsList: vendor.products.productsList ?? [],\n }\n : null,\n promoCodes: (vendor.promoCodes ?? [])\n .filter((code): code is string => code != null)\n .map(toPromoCodeType),\n region: vendor.region,\n socialMedia: toFormSocialMedia(vendor.socialMedia),\n termsAgreement: vendor.termsAgreement\n ? {\n appBuildNumber: vendor.termsAgreement.appBuildNumber,\n appId: vendor.termsAgreement.appId,\n appVersion: vendor.termsAgreement.appVersion,\n brand: vendor.termsAgreement.brand,\n deviceName: vendor.termsAgreement.deviceName,\n installationId: vendor.termsAgreement.installationId,\n manufacturer: vendor.termsAgreement.manufacturer,\n modelName: vendor.termsAgreement.modelName,\n osName: vendor.termsAgreement.osName,\n osVersion: vendor.termsAgreement.osVersion,\n termVersion: vendor.termsAgreement.termVersion,\n timestamp: vendor.termsAgreement.timestamp,\n }\n : null,\n unregisteredVendorId: vendor.unregisteredVendorId ?? null,\n vendorType: vendor.vendorType,\n };\n}\n\n/**\n * Convert VendorInfoType into VendorInfoFormData.\n * Documents/product arrays are normalized; requirements are optional on GraphQL.\n */\nexport function vendorInfoToFormData(\n vendorInfo: VendorInfoType,\n): VendorInfoFormData {\n return {\n _id: vendorInfo._id,\n compliance: vendorInfo.compliance\n ? {\n foodBeverageLicense: vendorInfo.compliance.foodBeverageLicense,\n liabilityInsurance: vendorInfo.compliance.liabilityInsurance,\n }\n : undefined,\n documents: vendorInfo.documents ?? null,\n documentsUpload: null,\n product: {\n foodFlavors: vendorInfo.product.foodFlavors,\n packaging: vendorInfo.product.packaging ?? [],\n priceRange: {\n max: vendorInfo.product.priceRange.max,\n min: vendorInfo.product.priceRange.min,\n },\n producedIn: vendorInfo.product.producedIn ?? [],\n },\n requirements: vendorInfo.requirements\n ? {\n electricity: toFormVendorAttributes(\n vendorInfo.requirements.electricity,\n ),\n gazebo: toFormVendorAttributes(vendorInfo.requirements.gazebo),\n table: toFormVendorAttributes(vendorInfo.requirements.table),\n }\n : undefined,\n stallInfo: {\n size: {\n depth: vendorInfo.stallInfo.size.depth,\n width: vendorInfo.stallInfo.size.width,\n },\n },\n vendorId: vendorInfo.vendorId,\n };\n}\n\n/**\n * UnregisteredVendor is not a BaseResourceType subset — independent adapter.\n * Form create/edit uses top-level dateTime + inviterId; entity stores invitations[].\n * When hydrating an existing entity for a single-invitation edit flow, prefer\n * the first invitation (call sites that need a specific inviter should filter).\n */\nexport function unregisteredVendorToFormData(\n vendor: UnregisteredVendorType,\n options?: { inviterId?: string },\n): UnregisteredVendorFormData {\n const invitation =\n options?.inviterId != null\n ? vendor.invitations.find((row) => row.inviterId === options.inviterId)\n : vendor.invitations[0];\n\n if (invitation == null) {\n throw new Error(\n `UnregisteredVendor ${vendor._id} has no invitation to hydrate form dateTime/inviterId.`,\n );\n }\n\n return {\n categoryIds: vendor.categoryIds,\n dateTime: invitation.dateTime,\n email: vendor.email ?? null,\n inviterId: invitation.inviterId,\n name: vendor.name,\n region: vendor.region,\n };\n}\n\n/**\n * Ad picker selectable resource projection for Vendor.\n */\nexport function vendorToAdSelectableResource(vendor: VendorType): {\n _id: string;\n cover: VendorType[\"cover\"];\n description: string;\n images?: VendorType[\"images\"];\n logo?: VendorType[\"logo\"];\n name: string;\n region: string;\n slug: string;\n} {\n return {\n _id: vendor._id,\n cover: vendor.cover,\n description: vendor.description,\n images: vendor.images,\n logo: vendor.logo,\n name: vendor.name,\n region: vendor.region,\n slug: vendor.slug,\n };\n}\n","/**\n * Adapters between generated GraphQL Partner entity types and form/UI shapes.\n *\n * GraphQL entities are not form state — these converters make the differences\n * explicit (null vs undefined, required vs draft-optional social fields,\n * promo-code branding).\n */\nimport type { PartnerType } from \"../generated/graphql\";\n\nimport { toFormSocialMedia } from \"./eventAdapters\";\nimport type { PromoCodeType } from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\nimport type { PartnerFormData } from \"./partner\";\n\nfunction toPromoCodeType(code: string): PromoCodeType {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(`Invalid promo code from GraphQL: ${JSON.stringify(code)}`);\n }\n return code as PromoCodeType;\n}\n\n/**\n * Convert a stored Partner entity into PartnerFormData for create/edit forms.\n * Maps GraphQL nullability onto form null defaults and converts GraphQL\n * social media into draft-optional form rows (same dual-shape as Event).\n */\nexport function partnerToFormData(partner: PartnerType): PartnerFormData {\n return {\n _id: partner._id,\n active: partner.active,\n contactDetails: partner.contactDetails ?? null,\n cover: partner.cover,\n coverUpload: null,\n description: partner.description,\n images: partner.images ?? null,\n imagesUpload: null,\n location: partner.location,\n logo: partner.logo ?? null,\n logoUpload: null,\n name: partner.name,\n nzbn: partner.nzbn,\n partnerType: partner.partnerType,\n promoCodes: (partner.promoCodes ?? [])\n .filter((code): code is string => code != null)\n .map(toPromoCodeType),\n region: partner.region,\n socialMedia: toFormSocialMedia(partner.socialMedia),\n termsAgreement: partner.termsAgreement\n ? {\n appBuildNumber: partner.termsAgreement.appBuildNumber,\n appId: partner.termsAgreement.appId,\n appVersion: partner.termsAgreement.appVersion,\n brand: partner.termsAgreement.brand,\n deviceName: partner.termsAgreement.deviceName,\n installationId: partner.termsAgreement.installationId,\n manufacturer: partner.termsAgreement.manufacturer,\n modelName: partner.termsAgreement.modelName,\n osName: partner.termsAgreement.osName,\n osVersion: partner.termsAgreement.osVersion,\n termVersion: partner.termsAgreement.termVersion,\n timestamp: partner.termsAgreement.timestamp,\n }\n : null,\n };\n}\n\n/**\n * Ad picker needs the same selectable shape Event uses. Partner already has\n * required description + cover in the schema; this still normalizes optional\n * images/logo for the shared ad form builder.\n */\nexport function partnerToAdSelectableResource(partner: PartnerType): {\n _id: string;\n cover: PartnerType[\"cover\"];\n description: string;\n images?: PartnerType[\"images\"];\n logo?: PartnerType[\"logo\"];\n name: string;\n region: string;\n slug: string;\n} {\n return {\n _id: partner._id,\n cover: partner.cover,\n description: partner.description,\n images: partner.images,\n logo: partner.logo,\n name: partner.name,\n region: partner.region,\n slug: partner.slug,\n };\n}\n","const OBJECT_ID_PATH_SEGMENT = \"[a-f0-9]{24}\";\nconst OBJECT_ID_PATH_SEGMENT_END = `${OBJECT_ID_PATH_SEGMENT}$`;\n\nexport const gameScreenIdentifierList = [\n {\n clue: \"Where your actions turn into a timeline.\",\n id: \"activities\",\n match: \"/profile/activities\",\n },\n {\n clue: \"Where conversations happen without speaking.\",\n id: \"chat\",\n match: \"/profile/chat\",\n },\n {\n clue: \"The place to redefine who you are.\",\n id: \"edit-profile\",\n match: \"/profile/edit-profile\",\n },\n {\n clue: \"A single moment worth showing up for.\",\n id: \"single-event\",\n match: new RegExp(`^/events/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"What’s happening around you, right now.\",\n id: \"events-near-me\",\n match: \"/events/events-near-me\",\n },\n {\n clue: \"Where events appear as pins on a map.\",\n id: \"events-map\",\n match: \"/events/events-map\",\n },\n {\n clue: \"A collection of events worth attending.\",\n id: \"events\",\n match: \"/events\",\n },\n {\n clue: \"What’s happening in a wider area — not just nearby.\",\n id: \"events-region\",\n match: /^\\/events\\/region\\/[^/]+$/,\n },\n {\n clue: \"Where fun becomes a challenge.\",\n id: \"games\",\n match: \"/games\",\n },\n {\n clue: \"Your starting point for everything.\",\n id: \"home\",\n match: \"/\",\n },\n {\n clue: \"Where the app whispers what you shouldn’t miss.\",\n id: \"notifications\",\n match: \"/notifications\",\n },\n {\n clue: \"Where you fine-tune your experience.\",\n id: \"options\",\n match: \"/options\",\n },\n {\n clue: \"An organisation or creator supporting the community.\",\n id: \"single-partner\",\n match: new RegExp(`^/partners/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Organisations and creators supporting the community.\",\n id: \"partners\",\n match: \"/partners\",\n },\n {\n clue: \"A single published post in full view.\",\n id: \"single-visitor-post\",\n match: new RegExp(`^/visitors/post/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Your identity, on display.\",\n id: \"profile\",\n match: \"/profile\",\n },\n {\n clue: \"One stallholder offering something valuable.\",\n id: \"single-vendor\",\n match: new RegExp(`^/vendors/${OBJECT_ID_PATH_SEGMENT_END}`),\n },\n {\n clue: \"Where every stallholder waits under the right category.\",\n id: \"vendors\",\n match: \"/vendors\",\n },\n {\n clue: \"Where you browse articles and posts from around the platform.\",\n id: \"visitors\",\n match: \"/visitors\",\n },\n] as const;\n\nexport type GamePlacement = (typeof gameScreenIdentifierList)[number][\"id\"];\nexport type GamePlacementClue = (typeof gameScreenIdentifierList)[number][\"clue\"];\n\n/**\n * Runtime shape used by dailyClueGame.ts utilities.\n * Uses JS Date (not ISO string) because it's constructed at runtime.\n * Distinct from the generated DailyClueGameDataType which uses string dates.\n */\nexport type DailyClueGameData = {\n points: number;\n streak: number;\n gameFields: {\n gameDate: { startDate: Date; endDate: Date };\n gameSolution: string;\n };\n lastFoundDate: Date | null;\n letterInfo: {\n collected: string[] | null;\n solutionShuffled: string[];\n todaysClue: GamePlacementClue | null;\n todaysLetter: string | null;\n todaysPlacement: GamePlacement | null;\n };\n};\n","import { GameTypeEnumType } from \"../../generated/graphql\";\n\nexport const gameTypeToDisplayName: Record<GameTypeEnumType, string> = {\n [GameTypeEnumType.DailyClue]: \"Daily Clue\",\n [GameTypeEnumType.MiniQuiz]: \"Mini Quiz\",\n [GameTypeEnumType.OddOneOut]: \"Odd One Out\",\n};\n","/**\n * Adapters between generated GraphQL School entity types and form/UI shapes.\n *\n * GraphQL entities are not form state — these converters make differences\n * explicit (null vs undefined, required vs draft-optional social fields).\n */\nimport type { SchoolType } from \"../generated/graphql\";\n\nimport { toFormSocialMedia } from \"./eventAdapters\";\nimport type { PromoCodeType } from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\nimport type { SchoolFormData } from \"./school\";\n\nexport function toSchoolCode(code: string): PromoCodeType {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(\n `Invalid school code from GraphQL: ${JSON.stringify(code)}`,\n );\n }\n return code as PromoCodeType;\n}\n\n/**\n * GraphQL SchoolCampaignType dates are ISO strings (Date scalar serialize).\n * Countdown UI / period helpers historically used JS Date — convert explicitly.\n */\nexport function schoolCampaignDatesToJsDates(campaign: {\n endDate: string;\n name: string;\n startDate: string;\n}): { endDate: Date; name: string; startDate: Date } {\n return {\n endDate: new Date(campaign.endDate),\n name: campaign.name,\n startDate: new Date(campaign.startDate),\n };\n}\n\nexport function schoolToFormData(school: SchoolType): SchoolFormData {\n return {\n _id: school._id,\n active: school.active,\n contactDetails: school.contactDetails ?? null,\n location: school.location,\n logo: school.logo ?? null,\n logoUpload: null,\n name: school.name,\n region: school.region,\n socialMedia: toFormSocialMedia(school.socialMedia),\n studentCount: school.studentCount,\n termsAgreement: school.termsAgreement\n ? {\n appBuildNumber: school.termsAgreement.appBuildNumber,\n appId: school.termsAgreement.appId,\n appVersion: school.termsAgreement.appVersion,\n brand: school.termsAgreement.brand,\n deviceName: school.termsAgreement.deviceName,\n installationId: school.termsAgreement.installationId,\n manufacturer: school.termsAgreement.manufacturer,\n modelName: school.termsAgreement.modelName,\n osName: school.termsAgreement.osName,\n osVersion: school.termsAgreement.osVersion,\n termVersion: school.termsAgreement.termVersion,\n timestamp: school.termsAgreement.timestamp,\n }\n : null,\n };\n}\n","/**\n * Adapters between generated GraphQL Affiliate entity types and form/UI shapes.\n *\n * GraphQL entities are not form state — these converters make differences\n * explicit (null vs undefined, required vs draft-optional social fields,\n * branded affiliate codes, ISO Date strings vs JS Date).\n */\nimport { AffiliateParticipantTypeEnum } from \"../generated/graphql\";\nimport type { AffiliateType, LocationType } from \"../generated/graphql\";\n\nimport type { AffiliateFormData } from \"./affiliate\";\nimport { toFormSocialMedia } from \"./eventAdapters\";\nimport type { PromoCodeType } from \"./global\";\nimport { PROMO_CODE_PREFIX } from \"./global\";\n\nconst emptyLocation: LocationType = {\n city: \"\",\n country: \"\",\n fullAddress: \"\",\n geo: {\n coordinates: [0, 0],\n type: \"Point\",\n },\n latitude: 0,\n longitude: 0,\n region: \"\",\n};\n\nexport function toAffiliateCode(code: string): PromoCodeType {\n if (!code.startsWith(PROMO_CODE_PREFIX)) {\n throw new Error(\n `Invalid affiliate code from GraphQL: ${JSON.stringify(code)}`,\n );\n }\n return code as PromoCodeType;\n}\n\n/**\n * Convert a stored Affiliate entity into AffiliateFormData for create/edit forms.\n *\n * GraphQL AffiliateDetailsType requires bank/contact/IRD/location/participant\n * once stored; form drafts allow empty strings. Social media is draft-optional\n * (same dual-shape as Event/Partner). GraphQL termsAgreement is NonNull once\n * stored; form keeps it optional until agreed.\n *\n * Bank account fields are copied as-is (same exposure as the GraphQL query —\n * no extra fields, no masking added or removed).\n */\nexport function affiliateToFormData(\n affiliate: Pick<AffiliateType, \"affiliateDetails\">,\n): AffiliateFormData {\n const details = affiliate.affiliateDetails;\n\n return {\n bankAccountDetails: details?.bankAccountDetails ?? {\n accountHolderName: \"\",\n accountNumber: \"\",\n },\n contactDetails: details?.contactDetails ?? {\n mobilePhone: \"\",\n },\n irdNumber: details?.irdNumber ?? \"\",\n location: details?.location ?? emptyLocation,\n participantType:\n details?.participantType ?? (\"\" as AffiliateParticipantTypeEnum),\n socialMedia: toFormSocialMedia(details?.socialMedia),\n termsAgreement: details?.termsAgreement\n ? {\n appBuildNumber: details.termsAgreement.appBuildNumber,\n appId: details.termsAgreement.appId,\n appVersion: details.termsAgreement.appVersion,\n brand: details.termsAgreement.brand,\n deviceName: details.termsAgreement.deviceName,\n installationId: details.termsAgreement.installationId,\n manufacturer: details.termsAgreement.manufacturer,\n modelName: details.termsAgreement.modelName,\n osName: details.termsAgreement.osName,\n osVersion: details.termsAgreement.osVersion,\n termVersion: details.termsAgreement.termVersion,\n timestamp: details.termsAgreement.timestamp,\n }\n : null,\n };\n}\n","/**\n * Client-side-only enums that have no GraphQL schema equivalent.\n * Schema-backed enums are generated by codegen in src/generated/graphql.ts.\n */\n\nexport enum EnumFoodType {\n ADDITIVE_FREE = \"Additive_Free\",\n AIR_FRIED = \"Air_Fried\",\n ALLERGEN_FRIENDLY = \"Allergen_Friendly\",\n ATHLETE_FRIENDLY = \"Athlete_Friendly\",\n BAKED = \"Baked\",\n DAIRY_FREE = \"Dairy_Free\",\n DIABETIC_FRIENDLY = \"Diabetic_Friendly\",\n EGG_FREE = \"Egg_Free\",\n FRESH = \"Fresh\",\n GLUTEN_FREE = \"Gluten_Free\",\n GRILLED = \"Grilled\",\n HALAL = \"Halal\",\n HEART_HEALTHY = \"Heart_Healthy\",\n HIGH_FIBER = \"High_Fiber\",\n HIGH_PROTEIN = \"High_Protein\",\n KETO = \"Keto\",\n KOSHER = \"Kosher\",\n LACTOSE_FREE = \"Lactose_Free\",\n LOW_CALORIE = \"Low_Calorie\",\n LOW_CARB = \"Low_Carb\",\n LOW_FAT = \"Low_Fat\",\n LOW_SODIUM = \"Low_Sodium\",\n NO_ADDED_SUGAR = \"No_Added_Sugar\",\n NO_PRESERVATIVES = \"No_Preservatives\",\n NON_GMO = \"Non_GMO\",\n NUT_FREE = \"Nut_Free\",\n ORGANIC = \"Organic\",\n PALEO = \"Paleo\",\n PLANT_BASED = \"Plant_Based\",\n RAW = \"Raw\",\n SMOKED = \"Smoked\",\n SOY_FREE = \"Soy_Free\",\n SUGAR_FREE = \"Sugar_Free\",\n VEGAN = \"Vegan\",\n VEGETARIAN = \"Vegetarian\",\n}\n\nexport enum EnumRegions {\n All = \"All Regions\",\n Auckland = \"Auckland\",\n BayOfPlentyGisborne = \"Bay of Plenty & Gisborne\",\n CanterburyWestCoast = \"Canterbury & West Coast\",\n HawkesBay = \"Hawke's Bay\",\n ManawatuWanganui = \"Manawatu-Wanganui\",\n MarlboroughNelsonTasman = \"Marlborough & Nelson & Tasman\",\n Northland = \"Northland\",\n Otago = \"Otago\",\n Southland = \"Southland\",\n Taranaki = \"Taranaki\",\n Waikato = \"Waikato\",\n Wellington = \"Wellington\",\n}\n\nexport enum ImageTypeEnum {\n AVATAR = \"avatar\",\n COVER = \"cover\",\n IMAGE = \"image\",\n LOGO = \"logo\",\n}\n","import type { Dayjs } from \"dayjs\";\n\nimport {\n DailyClueGameData,\n GamePlacement,\n GamePlacementClue,\n gameScreenIdentifierList,\n} from \"../types/game/dailyClue\";\n\nimport { nzStartOfDay } from \"./date\";\n\nfunction createSeededRng(seed: number) {\n let t = seed >>> 0;\n\n return function random() {\n t += 0x6d2b79f5;\n let x = t;\n\n x = Math.imul(x ^ (x >>> 15), x | 1);\n x ^= x + Math.imul(x ^ (x >>> 7), x | 61);\n\n return ((x ^ (x >>> 14)) >>> 0) / 4294967296;\n };\n}\n\nfunction hashStringToNumber(seed: string): number {\n let hash = 2166136261;\n\n for (let i = 0; i < seed.length; i++) {\n hash ^= seed.codePointAt(i) ?? 0;\n hash = Math.imul(hash, 16777619);\n }\n\n return hash >>> 0;\n}\n\n/** Seeded shuffle so all players see the same letter order / placements for a game. */\nexport function seededShuffle<T>(array: readonly T[], seed: string): T[] {\n const rng = createSeededRng(hashStringToNumber(seed));\n const result = [...array];\n\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n\n return result;\n}\n\nfunction getDayIndex(start: Dayjs, today: Dayjs): number {\n return today.diff(start, \"day\");\n}\n\nexport function computeDailyClueState(dailyClue: DailyClueGameData): {\n todaysClue: GamePlacementClue | null;\n todaysLetter: string | null;\n todaysPlacement: GamePlacement | null;\n} | null {\n const { startDate, endDate } = dailyClue.gameFields.gameDate;\n const { solutionShuffled, collected } = dailyClue.letterInfo;\n\n const today = nzStartOfDay();\n const start = nzStartOfDay(startDate);\n const end = nzStartOfDay(endDate);\n\n // Before game starts\n if (today.isBefore(start)) {\n return null;\n }\n\n const shuffledPlacements = seededShuffle(\n gameScreenIdentifierList,\n start.toISOString(),\n );\n\n const index = getDayIndex(start, today);\n\n // After game ends\n if (today.isAfter(end)) {\n return {\n todaysClue: null,\n todaysLetter: null,\n todaysPlacement: null,\n };\n }\n\n // Safety: index must exist in BOTH arrays\n if (\n index < 0 ||\n index >= solutionShuffled.length ||\n index >= shuffledPlacements.length\n ) {\n return null;\n }\n\n const letterToday = solutionShuffled[index];\n const placement = shuffledPlacements[index];\n\n if (!letterToday || !placement) return null;\n\n const alreadyCollectedToday = (collected ?? []).includes(letterToday);\n\n // Already completed today\n if (alreadyCollectedToday) {\n return {\n todaysClue: null,\n todaysLetter: null,\n todaysPlacement: null,\n };\n }\n\n // Active state\n return {\n todaysClue: placement.clue,\n todaysLetter: letterToday,\n todaysPlacement: placement.id,\n };\n}\n","import { stripOverlaySubtitleSpecialChars } from \"src/sharing/normalizeShareDescription\";\nimport { OptionItem, PROMO_CODE_PREFIX, SocialMediaType } from \"src/types\";\n\nimport { EnumRegions } from \"../enums/clientEnums\";\nimport {\n InviteEnumType,\n PaymentMethodEnumType,\n PostTypeEnum,\n RewardEnumType,\n SocialMediaEnumType,\n LicencesEnumType,\n} from \"../generated/graphql\";\n\nimport { isIsoDateString } from \"./date\";\nimport { mapArrayToOptions } from \"./mapArrayToOptions\";\n\nexport const removeTypename = (obj: any): any => {\n // Preserve Date objects\n if (obj instanceof Date) {\n return obj;\n }\n\n // Preserve File objects (for apollo-upload-client)\n if (obj instanceof File) {\n return obj;\n }\n\n // Preserve ISO date strings\n if (isIsoDateString(obj)) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map(removeTypename);\n }\n\n // Handle plain objects only\n if (obj !== null && typeof obj === \"object\") {\n const { __typename, ...cleanedObj } = obj;\n\n return Object.keys(cleanedObj).reduce((acc: any, key) => {\n acc[key] = removeTypename(cleanedObj[key]);\n return acc;\n }, {});\n }\n\n // Primitives\n return obj;\n};\n\n/**\n * Truncate text to a specified length and append ellipsis if necessary.\n * @param text\n * @param maxLength\n * @returns\n */\nexport const truncateText = (text: string, maxLength: number = 30): string => {\n const result = stripOverlaySubtitleSpecialChars(text);\n return result.length > maxLength\n ? result.substring(0, maxLength) + \"...\"\n : result;\n};\n\nexport const capitalizeFirstLetter = (str: string): string => {\n return str\n .split(\" \")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\" \");\n};\n\nexport const statusOptions = [\n ...Object.values(InviteEnumType)\n .map((status) => ({\n label: status,\n value: status,\n }))\n .sort((a, b) => a.label.localeCompare(b.label)), // Sort the options alphabetically\n];\n\nexport const availableRegionTypes = Object.values(EnumRegions);\nexport const availableRegionOptions: OptionItem[] =\n mapArrayToOptions(availableRegionTypes);\n\nexport const paymentMethodOptions: OptionItem[] = mapArrayToOptions(\n Object.values(PaymentMethodEnumType),\n);\n\nexport function normalizeUrl(url: string): string {\n const withProtocol =\n !url.startsWith(\"http://\") && !url.startsWith(\"https://\")\n ? `https://${url}`\n : url;\n return withProtocol.replace(/\\/+$/, \"\");\n}\n\nexport const licenseNiceNames: Record<LicencesEnumType, string> = {\n [LicencesEnumType.ProEvent]: \"Pro Event\",\n [LicencesEnumType.ProVendor]: \"Pro Stallholder\",\n [LicencesEnumType.StandardEvent]: \"Standard Event\",\n [LicencesEnumType.StandardVendor]: \"Standard Stallholder\",\n [LicencesEnumType.ProPlusEvent]: \"Pro+Ads Event\",\n [LicencesEnumType.ProPlusVendor]: \"Pro+Ads Stallholder\",\n [LicencesEnumType.StandardPartner]: \"Partner\",\n [LicencesEnumType.StandardAffiliate]: \"Affiliate\",\n [LicencesEnumType.StandardSchool]: \"School\",\n};\n\nexport const cluemartSocialMedia: SocialMediaType[] = [\n {\n link: \"https://www.facebook.com/ClueMartApp\",\n name: SocialMediaEnumType.Facebook,\n },\n {\n link: \"https://www.instagram.com/cluemart_app\",\n name: SocialMediaEnumType.Instagram,\n },\n {\n link: \"https://www.tiktok.com/@cluemart\",\n name: SocialMediaEnumType.Tiktok,\n },\n {\n link: \"https://www.youtube.com/@ClueMart-App-NZ\",\n name: SocialMediaEnumType.Youtube,\n },\n];\n\nexport const IOS_URL = \"https://apps.apple.com/nz/app/cluemart/id6747251008\";\nexport const ANDROID_URL =\n \"https://play.google.com/store/apps/details?id=com.timardex.cluemart\";\n\nexport const DEFAULT_RESOURCES_RETURN_LIMIT = 1000;\nexport const DEFAULT_RESOURCES_RETURN_OFFSET = 0;\nexport const CLUEMART_MAIN_DOMAIN_URL = \"https://cluemart.co.nz\";\n\nexport const rewardNiceNames: Record<\n RewardEnumType,\n { name: string; points: number }\n> = {\n [RewardEnumType.MysteryJoyBox]: {\n name: \"Mystery Joy Box\",\n points: 250,\n },\n [RewardEnumType.MysterySqueezeBox]: {\n name: \"Mystery Squeeze Box\",\n points: 350,\n },\n [RewardEnumType.MysteryDeluxeBox]: {\n name: \"Mystery Deluxe Box\",\n points: 500,\n },\n};\n\nexport const PostTypeLabels: Record<PostTypeEnum, string> = {\n [PostTypeEnum.MarketFaces]: \"Market Faces\",\n [PostTypeEnum.ClueBites]: \"Clue Bites\",\n [PostTypeEnum.PlayAndWin]: \"Play & Win\",\n};\n\n/** Hardcoded registration gateway code; only valid at sign-up (not a generated school referral code). */\nexport const CM_SCHOOL_PROMO_CODE = `${PROMO_CODE_PREFIX}SCHOOL`;\n\n/** Hardcoded registration gateway code; only valid at sign-up (not a generated affiliate referral code). */\nexport const CM_AFFILIATE_PROMO_CODE = `${PROMO_CODE_PREFIX}AFFILIATE`;\n\nexport function formatNZBankAccount(input: string): string {\n // Remove all non-digit characters\n const digitsOnly = input.replaceAll(/\\D/g, \"\");\n\n // Build the formatted string step-by-step\n const parts = [];\n if (digitsOnly.length > 0) parts.push(digitsOnly.slice(0, 2)); // bank\n if (digitsOnly.length > 2) parts.push(digitsOnly.slice(2, 6)); // branch\n if (digitsOnly.length > 6) parts.push(digitsOnly.slice(6, 13)); // account\n if (digitsOnly.length > 13) parts.push(digitsOnly.slice(13, 15)); // suffix\n\n return parts.join(\"-\");\n}\n","import { ResourceImageType } from \"../types/global\";\n\n/**\n * Loose input shape for normalization helpers. Callers often pass partial or legacy\n * data (e.g. Mongo docs without `active`, GraphQL input, empty placeholders) before\n * it becomes a strict `ResourceImageType`.\n */\ntype ResourceImageLike = {\n active?: boolean | null;\n source?: string | null;\n title?: string | null;\n};\n\n/**\n * Resolves whether a resource image is active.\n * Only an explicit `active: true` shows the image; missing `active` defaults to false\n * so licence downgrades (e.g. standard vendor gallery limit) are not overridden.\n */\nexport function resolveResourceImageActive(\n image: ResourceImageLike | null | undefined,\n): boolean {\n if (image?.active != null) {\n return image.active;\n }\n\n return false;\n}\n\nexport function normalizeResourceImage(\n image: ResourceImageLike | null | undefined,\n): ResourceImageType {\n return {\n active: resolveResourceImageActive(image),\n source: image?.source ?? \"\",\n title: image?.title ?? \"\",\n };\n}\n\nexport function isResourceImageActive(\n image: ResourceImageLike | null | undefined,\n): boolean {\n return resolveResourceImageActive(image);\n}\n","export const SCHOOL_MIN_STUDENT_COUNT = 300;\nexport const SCHOOL_MAX_STUDENT_COUNT = 0;\n","import { EventDateStatusEnumType } from \"../generated/graphql\";\nimport type { DateTimeType } from \"../types/global\";\n\nexport const EVENT_DATE_STATUS_PERIOD_MAP: Partial<\n Record<EventDateStatusEnumType, EventDateStatusEnumType[]>\n> = {\n [EventDateStatusEnumType.Today]: [\n EventDateStatusEnumType.Started,\n EventDateStatusEnumType.StartingSoon,\n EventDateStatusEnumType.Today,\n ],\n [EventDateStatusEnumType.ThisWeek]: [\n EventDateStatusEnumType.Started,\n EventDateStatusEnumType.StartingSoon,\n EventDateStatusEnumType.ThisWeek,\n EventDateStatusEnumType.Today,\n EventDateStatusEnumType.Tomorrow,\n ],\n};\n\nexport function getAllowedEventDateStatuses(\n period: EventDateStatusEnumType,\n): EventDateStatusEnumType[] {\n return EVENT_DATE_STATUS_PERIOD_MAP[period] ?? [period];\n}\n\nexport function eventMatchesDateStatusPeriod(\n dateTime: Pick<DateTimeType, \"dateStatus\">[] | null | undefined,\n period: EventDateStatusEnumType,\n): boolean {\n const allowedStatuses = getAllowedEventDateStatuses(period);\n\n return (\n dateTime?.some((dt) => allowedStatuses.includes(dt.dateStatus)) ?? false\n );\n}\n\nexport function filterEventsByDateStatusPeriod<\n T extends { dateTime?: Pick<DateTimeType, \"dateStatus\">[] | null },\n>(events: T[], period: EventDateStatusEnumType): T[] {\n return events.filter((event) =>\n eventMatchesDateStatusPeriod(event.dateTime, period),\n );\n}\n","import dayjs from \"dayjs\";\n\nimport {\n EventDateStatusEnumType,\n ResourceTypeEnum,\n} from \"../generated/graphql\";\nimport type { DateTimeType, LocationType } from \"../types/global\";\nimport { dateFormat, formatDate } from \"../utils/date\";\n\nexport type CalendarSheetContentData = {\n dateTime: DateTimeType;\n location: LocationType;\n resourceId: string;\n resourceType: ResourceTypeEnum;\n};\n\nexport const KNOWN_EVENT_SCHEDULE_STATUSES = new Set<EventDateStatusEnumType>([\n EventDateStatusEnumType.Started,\n EventDateStatusEnumType.StartingSoon,\n EventDateStatusEnumType.Ended,\n]);\n\nexport type EventScheduleStatusTone =\n | \"started\"\n | \"startingSoon\"\n | \"ended\"\n | \"default\";\n\nexport type EventScheduleStatusPresentation = {\n status: EventDateStatusEnumType;\n statusLabel: string;\n tone: EventScheduleStatusTone;\n};\n\nexport function toCalendarIsoDate(startDate: string): string | null {\n const formatted = dayjs(startDate, dateFormat).format(\"YYYY-MM-DD\");\n\n return dayjs(formatted, \"YYYY-MM-DD\", true).isValid() ? formatted : null;\n}\n\nexport function fromCalendarIsoDate(isoDate: string): string {\n return dayjs(isoDate, \"YYYY-MM-DD\").format(dateFormat);\n}\n\nexport function getEventCalendarIsoDates(\n dateTimes: DateTimeType[] | null | undefined,\n): string[] {\n return (\n dateTimes\n ?.map((dateTime) => toCalendarIsoDate(dateTime.startDate))\n .filter((date): date is string => Boolean(date)) ?? []\n );\n}\n\nexport function findEventDateTimeByIsoDate(\n dateTimes: DateTimeType[] | null | undefined,\n isoDate: string,\n): DateTimeType | null {\n const selected = fromCalendarIsoDate(isoDate);\n\n return dateTimes?.find((dateTime) => dateTime.startDate === selected) ?? null;\n}\n\nexport function buildCalendarSheetContentData(\n dateTimes: DateTimeType[],\n isoDate: string,\n location: LocationType,\n resourceId: string,\n resourceType: ResourceTypeEnum,\n): CalendarSheetContentData | null {\n const dateTime = findEventDateTimeByIsoDate(dateTimes, isoDate);\n\n if (!dateTime) {\n return null;\n }\n\n return {\n dateTime,\n location,\n resourceId,\n resourceType,\n };\n}\n\nexport function getEventScheduleStatusTone(\n status: EventDateStatusEnumType,\n): EventScheduleStatusTone {\n switch (status) {\n case EventDateStatusEnumType.Started:\n return \"started\";\n case EventDateStatusEnumType.StartingSoon:\n return \"startingSoon\";\n case EventDateStatusEnumType.Ended:\n return \"ended\";\n default:\n return \"default\";\n }\n}\n\nexport function getEventScheduleStatusPresentation(\n dateTime: DateTimeType,\n): EventScheduleStatusPresentation {\n return {\n status: dateTime.dateStatus,\n statusLabel: dateTime.dateStatus.replaceAll(\"_\", \" \").toLowerCase(),\n tone: getEventScheduleStatusTone(dateTime.dateStatus),\n };\n}\n\nexport function getEventScheduleStatusLabel(\n dateTime: DateTimeType,\n onlyShowKnownStatuses: boolean,\n): string | null {\n const { dateStatus: status } = dateTime;\n const isKnownStatus = Boolean(\n status && KNOWN_EVENT_SCHEDULE_STATUSES.has(status),\n );\n\n if (onlyShowKnownStatuses && !isKnownStatus) {\n return null;\n }\n\n return isKnownStatus\n ? status.replaceAll(\"_\", \" \").toLowerCase()\n : `Next event date: ${formatDate(dateTime.startDate, \"date\")}`;\n}\n\nexport function shouldShowEventActionsWithWeather(\n resourceType: ResourceTypeEnum,\n dateTime: DateTimeType,\n location: LocationType | null | undefined,\n): boolean {\n return (\n resourceType === ResourceTypeEnum.Event &&\n Boolean(location) &&\n Boolean(dateTime?.dateStatus) &&\n ![\n EventDateStatusEnumType.Ended,\n EventDateStatusEnumType.Canceled,\n EventDateStatusEnumType.Started,\n ].includes(dateTime.dateStatus)\n );\n}\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const clothingAndFashion: Category[] = [\n {\n id: \"clothing-fashion\",\n name: \"Clothing & Fashion\",\n description:\n \"New, handmade, or upcycled clothing and accessories with a creative twist.\",\n subcategories: [\n {\n id: \"apparel-babywear\",\n name: \"Apparel & Babywear\",\n items: [\n {\n id: \"apparel\",\n name: \"Apparel\",\n description: \"Dresses, t-shirts, jumpers, rompers, sets, kidswear.\",\n },\n {\n id: \"baby-toddler-apparel\",\n name: \"Baby & Toddler Apparel\",\n description:\n \"Handmade baby clothes, soft shoes, bibs, hats, knitted sets.\",\n },\n {\n id: \"upcycled-fashion\",\n name: \"Upcycled Fashion\",\n description:\n \"Reworked garments, patchwork pieces, restyled vintage.\",\n },\n {\n id: \"other-wearable-items\",\n name: \"Other wearable items\",\n description: \"Unique clothing not listed above.\",\n },\n ],\n },\n {\n id: \"fashion-accessories\",\n name: \"Fashion Accessories\",\n items: [\n {\n id: \"accessories\",\n name: \"Accessories\",\n description: \"Scarves, belts, gloves, hats, headbands, caps.\",\n },\n {\n id: \"shoes\",\n name: \"Shoes\",\n description: \"Handmade shoes, baby booties, sandals, slippers.\",\n },\n {\n id: \"bags-wallets\",\n name: \"Bags & Wallets\",\n description:\n \"Leather bags, fabric purses, wallets, backpacks, totes.\",\n },\n {\n id: \"other-accessories\",\n name: \"Other accessories\",\n description: \"Brooches, pins, or hybrid functional items.\",\n },\n ],\n },\n {\n id: \"jewelry-creative-wearables\",\n name: \"Jewelry & Creative Wearables\",\n items: [\n {\n id: \"jewelry\",\n name: \"Jewelry\",\n description: \"Necklaces, earrings, bracelets, rings, anklets.\",\n },\n {\n id: \"other-creative-wearables\",\n name: \"Other creative wearables\",\n description:\n \"Wearable art, statement pieces, bold handmade designs.\",\n },\n ],\n },\n {\n id: \"traditional-cultural-clothing-accessories\",\n name: \"Traditional & Cultural Clothing and Accessories\",\n items: [\n {\n id: \"traditional-clothing-accessories\",\n name: \"Traditional Clothing & Accessories \",\n description:\n \"raditional clothing, jewellery, accessories and footwear from around the world, including both handmade and non-handmade items.\",\n },\n {\n id: \"other-traditional-cultural-items\",\n name: \"Other Traditional & Cultural Items\",\n description:\n \"traditional or culturally inspired wearables and decorative cultural pieces such as plates, table linens and similar items.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const electronicsAndTechnology: Category[] = [\n {\n id: \"electronics-technology\",\n name: \"Electronics & Technology\",\n description:\n \"New, second-hand, or handmade tech and digital items commonly found at markets.\",\n subcategories: [\n {\n id: \"mobile-everyday-tech\",\n name: \"Mobile & Everyday Tech\",\n items: [\n {\n id: \"mobile-phone-accessories\",\n name: \"Mobile & Phone Accessories\",\n description:\n \"Phone cases, holders, screen protectors, charging cables, power banks, grips.\",\n },\n {\n id: \"other-mobile-gadgets\",\n name: \"Other mobile gadgets\",\n description:\n \"Stands, styluses, SIM tools, cleaning kits, wallet accessories.\",\n },\n ],\n },\n {\n id: \"audio-music-creative-tech\",\n name: \"Audio, Music & Creative Tech\",\n items: [\n {\n id: \"audio-music-tech\",\n name: \"Audio & Music Tech\",\n description:\n \"Portable speakers, headphones, mini radios, Bluetooth adapters, sound accessories.\",\n },\n {\n id: \"instruments-music-gear\",\n name: \"Instruments & Music Gear\",\n description:\n \"Small electronic instruments, DIY synth kits, drum pads, loop machines.\",\n },\n {\n id: \"recording-creative-devices\",\n name: \"Recording & Creative Devices\",\n description:\n \"USB microphones, podcast tools, voice recorders, mobile lighting, content tools.\",\n },\n {\n id: \"other-audio-music-items\",\n name: \"Other audio or music-related items\",\n description: \"Musical gadgets or creative gear not listed above.\",\n },\n ],\n },\n {\n id: \"diy-gadgets-secondhand-finds\",\n name: \"DIY, Gadgets & Second-Hand Finds\",\n items: [\n {\n id: \"diy-electronics-tools\",\n name: \"DIY Electronics & Tools\",\n description:\n \"LED lights, USB gadgets, circuit boards, repair kits, tech-themed toys, novelty items.\",\n },\n {\n id: \"other-tech-finds\",\n name: \"Other Tech Finds\",\n description:\n \"Used electronics, smart gadgets, wearable tech, calculators, tablet stands, e-waste upcycles.\",\n },\n {\n id: \"other-technology-innovation-items\",\n name: \"Other technology or innovation-related items\",\n description: \"Anything not covered but clearly tech-driven.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const foodAndBeverages: Category[] = [\n {\n id: \"food-beverages\",\n name: \"Food & Beverages\",\n description:\n \"Fresh produce, drinks, sweet and savoury street food, ready-to-eat meals, and packaged goods.\",\n subcategories: [\n {\n id: \"fresh-food-groceries\",\n name: \"Fresh Food & Groceries\",\n items: [\n {\n id: \"fruits-vegetables\",\n name: \"Fruits & Vegetables\",\n description:\n \"Fresh seasonal fruit, organic vegetables, specialty produce, berries, tropical fruit, heritage varieties.\",\n },\n {\n id: \"meat-seafood\",\n name: \"Meat & Seafood\",\n description:\n \"Butcher cuts, fresh fish, smoked meats, sausages, seafood platters, artisanal jerky.\",\n },\n {\n id: \"dairy-eggs\",\n name: \"Dairy & Eggs\",\n description:\n \"Farm eggs, handmade cheeses, yoghurt, fresh milk, goat’s milk products.\",\n },\n {\n id: \"bakery-pastries\",\n name: \"Bakery & Pastries\",\n description:\n \"Freshly baked bread, sourdough, bagels, croissants, focaccia, traditional baked goods.\",\n },\n {\n id: \"spices-condiments\",\n name: \"Spices & Condiments\",\n description:\n \"Dried herbs, spice blends, specialty salts, hot sauces, infused oils, chutneys.\",\n },\n {\n id: \"packaged-specialty-foods\",\n name: \"Packaged & Specialty Foods\",\n description:\n \"Honey, jams, preserves, pickles, nut butters, sauces, pre-packaged snacks.\",\n },\n {\n id: \"other-fresh-food-items\",\n name: \"Other fresh food items\",\n description:\n \"Items that don’t fit above, e.g. fermented foods, plant-based substitutes.\",\n },\n ],\n },\n {\n id: \"beverages-specialty-drinks\",\n name: \"Beverages & Specialty Drinks\",\n items: [\n {\n id: \"fresh-juices-smoothies\",\n name: \"Fresh Juices & Smoothies\",\n description:\n \"Cold-pressed juices, detox blends, fruit smoothies, tropical mixes.\",\n },\n {\n id: \"coffee-teas\",\n name: \"Coffee & Teas\",\n description:\n \"Espresso, pour-over coffee, herbal teas, matcha, bubble tea, locally blended tea.\",\n },\n {\n id: \"dairy-plant-based-drinks\",\n name: \"Dairy & Plant-Based Drinks\",\n description:\n \"Milkshakes, lassis, almond/oat milk drinks, coconut water.\",\n },\n {\n id: \"alcoholic-beverages\",\n name: \"Alcoholic Beverages\",\n description:\n \"Local wines, craft beer, mead, cider, infused spirits, cocktail kits.\",\n },\n {\n id: \"other-beverages\",\n name: \"Other beverages\",\n description:\n \"Kombucha, kefir, energy drinks, non-alcoholic wines or beers.\",\n },\n ],\n },\n {\n id: \"savoury-prepared-foods-street-food\",\n name: \"Savoury Prepared Foods & Street Food\",\n items: [\n {\n id: \"local-specialties\",\n name: \"Local Specialties\",\n description:\n \"Meat pies, hangi, seafood chowder, fry bread, traditional NZ dishes.\",\n },\n {\n id: \"asian-cuisine\",\n name: \"Asian Cuisine\",\n description:\n \"Sushi, bao buns, dumplings, ramen, satay, spring rolls, fried rice.\",\n },\n {\n id: \"mediterranean-cuisine\",\n name: \"Mediterranean Cuisine\",\n description: \"Falafel, hummus wraps, souvlaki, Greek salad, dolma.\",\n },\n {\n id: \"italian-delights\",\n name: \"Italian Delights\",\n description: \"Pizza, calzone, focaccia, fresh pasta, arancini.\",\n },\n {\n id: \"bbq-grilled-foods\",\n name: \"BBQ & Grilled Foods\",\n description: \"Kebabs, grilled chicken, ribs, burgers, sausages.\",\n },\n {\n id: \"savoury-crepes-pancakes\",\n name: \"Savoury Crepes & Pancakes\",\n description: \"Filled savoury crepes, mini savoury pancakes.\",\n },\n {\n id: \"savoury-baked-goods-pastries\",\n name: \"Savoury Baked Goods & Pastries\",\n description:\n \"Quiches, savoury muffins, filled savoury pastries, empanadas.\",\n },\n {\n id: \"vegan-vegetarian-dishes\",\n name: \"Vegan & Vegetarian Dishes\",\n description:\n \"Buddha bowls, plant-based burgers, salads, vegan sushi.\",\n },\n {\n id: \"other-savoury-foods\",\n name: \"Other Savoury Foods\",\n description: \"Fusion dishes, mixed platters, savoury meal kits.\",\n },\n ],\n },\n {\n id: \"sweet-prepared-foods\",\n name: \"Sweet Prepared Foods\",\n items: [\n {\n id: \"sweet-crepes-pancakes-fried-treats\",\n name: \"Sweet Crepes, Pancakes & Fried Treats\",\n description: \"Crepes, waffles, mini pancakes, muffins, doughnuts.\",\n },\n {\n id: \"sweet-baked-goods-desserts\",\n name: \"Sweet Baked Goods & Desserts\",\n description: \"Cakes, pastries, slices, trifles, layered desserts.\",\n },\n {\n id: \"fruit-based-snacks\",\n name: \"Fruit-Based Snacks\",\n description:\n \"Fruit skewers, dried fruit packs, chocolate-dipped fruit.\",\n },\n {\n id: \"candy-confectionery\",\n name: \"Candy & Confectionery\",\n description: \"Fudge, handmade candies, toffee, nougat, brittle.\",\n },\n {\n id: \"other-sweet-foods\",\n name: \"Other Sweet Foods\",\n description: \"Fusion desserts, sweet platters, sweet meal kits.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const handmadeAndLocalProducts: Category[] = [\n {\n id: \"handmade-local-products\",\n name: \"Handmade & Local Products\",\n description: \"Unique, handmade, or locally produced artisan goods.\",\n subcategories: [\n {\n id: \"home-living\",\n name: \"Home & Living\",\n items: [\n {\n id: \"ceramics-pottery\",\n name: \"Ceramics & Pottery\",\n description:\n \"Handmade mugs, vases, bowls, decorative plates, plant pots.\",\n },\n {\n id: \"candles-home-scents\",\n name: \"Candles & Home Scents\",\n description:\n \"Soy candles, beeswax candles, wax melts, incense sticks, room sprays and herbal sachets.\",\n },\n {\n id: \"botanical-crafts\",\n name: \"Botanical Crafts\",\n description: \"Dried-flower décor and pressed-flower crafts\",\n },\n {\n id: \"textiles-embroidery\",\n name: \"Textiles & Embroidery\",\n description:\n \"Handsewn items, embroidered napkins, home linens, aprons, fabric gift wraps, handmade fabric bags, quilted goods, personalized textile gifts.\",\n },\n {\n id: \"woodcraft-metalcraft\",\n name: \"Woodcraft & Metalcraft\",\n description:\n \"Wooden boards, handmade frames, sculptures, metal signs, furniture.\",\n },\n {\n id: \"handmade-soaps-natural-body-goods\",\n name: \"Handmade Soaps & Natural Body Goods\",\n description: \"Handmade soaps, balms, oils, bath items\",\n },\n {\n id: \"seasonal-festive-crafts\",\n name: \"Seasonal & Festive Crafts\",\n description: \"Christmas, Easter and seasonal handmade decor\",\n },\n {\n id: \"other-home-living-products\",\n name: \"Other home & living products\",\n description:\n \"Items that don't fit above but serve home-related purposes.\",\n },\n ],\n },\n {\n id: \"art-personal-expression\",\n name: \"Art & Personal Expression\",\n items: [\n {\n id: \"paintings-illustrations\",\n name: \"Paintings & Illustrations\",\n description:\n \"Paintings, canvas art, hand-drawn illustrations, digital prints, graphic art and calligraphy pieces.\",\n },\n {\n id: \"sculptures-carvings\",\n name: \"Sculptures & Carvings \",\n description:\n \"Sculptures made from wood, stone, metal, clay or resin, carved decorative pieces and artistic 3D works.\",\n },\n {\n id: \"creative-handmade-alternative-art\",\n name: \"Creative Handmade & Alternative Art\",\n description:\n \"Handmade textile décor, fabric ornaments, mixed-media art, creative craft pieces and modern handmade artworks.\",\n },\n {\n id: \"handmade-mini-figures-decor\",\n name: \"Handmade Mini Figures & Décor\",\n description:\n \"Small handmade figures, tiny houses and crafted mini decorative items.\",\n },\n {\n id: \"other-artistic-expressive-products\",\n name: \"Other Artistic or Expressive Products\",\n description:\n \"Custom artworks, specialty handmade décor, unique crafts.\",\n },\n ],\n },\n {\n id: \"handmade-jewellery-accessories-cultural-crafts\",\n name: \"Handmade Jewellery, Accessories & Cultural Crafts\",\n items: [\n {\n id: \"jewellery-handmade-items-nz-traditional-materials\",\n name: \"Jewellery & Handmade Items from NZ Traditional Materials\",\n description:\n \"Handmade jewellery and decorative or functional items crafted from pounamu, bone, wood and other traditional New Zealand materials.\",\n },\n {\n id: \"maori-pasifika-cultural-crafts-clothing\",\n name: \"Māori & Pasifika Cultural Crafts and Clothing\",\n description:\n \"Culturally inspired handmade items, accessories and garments featuring Māori or Pasifika motifs, traditional patterns and regional craftsmanship.\",\n },\n {\n id: \"traditional-handmade-clothing-jewellery-crafts-global-cultures\",\n name: \"Traditional Handmade Clothing, Jewellery & Crafts from Global Cultures\",\n description:\n \"Handcrafted clothing, jewellery and cultural items from diverse traditions — including Asian, African, European, Middle Eastern, Indian, Chinese, Japanese, Pacific, and other culturally significant handmade pieces.\",\n },\n {\n id: \"other-handmade-cultural-items\",\n name: \"Other Handmade Cultural Items\",\n description:\n \"Unique or culturally inspired handmade pieces not specifically covered in the categories above.\",\n },\n ],\n },\n {\n id: \"gift-ideas-accessories\",\n name: \"Gift Ideas & Accessories\",\n items: [\n {\n id: \"handmade-pens-keychains-fridge-magnets\",\n name: \"Handmade Pens, Keychains and Fridge Magnets\",\n description: null,\n },\n {\n id: \"gift-packaging-wrapping-accessories\",\n name: \"Gift Packaging & Wrapping Accessories\",\n description: null,\n },\n {\n id: \"handmade-crochet-knitting-fibre-crafts\",\n name: \"Handmade Crochet, Knitting & Fibre Crafts\",\n description:\n \"Handmade crochet and knitted items, fabric-based crafts, small decorative pieces, creative fibre artworks and unique handcrafted accessories.\",\n },\n {\n id: \"handmade-toys-mini-play-items\",\n name: \"Handmade Toys & Mini Play Items\",\n description:\n \"small handmade toys, soft toys, wooden miniatures, crochet or felt play pieces\",\n },\n {\n id: \"other-small-handmade-gifts-accessories\",\n name: \"Other small handmade gifts or accessories\",\n description: \"Compact creative items made to surprise or delight.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const healthAndWellness: Category[] = [\n {\n id: \"health-wellness\",\n name: \"Health & Wellness\",\n description:\n \"Natural products and services that promote wellbeing, body care, and holistic health.\",\n subcategories: [\n {\n id: \"body-skincare\",\n name: \"Body & Skincare\",\n items: [\n {\n id: \"skincare-body-products\",\n name: \"Skincare & Body Products\",\n description:\n \"Soaps, creams, lip balms, bath salts, bath bombs, body oils, natural deodorants.\",\n },\n {\n id: \"other-body-care-items\",\n name: \"Other body care items\",\n description:\n \"Additional handmade or eco-conscious personal care goods.\",\n },\n ],\n },\n {\n id: \"aromatherapy-herbal-wellness\",\n name: \"Aromatherapy & Herbal Wellness\",\n items: [\n {\n id: \"aromatherapy-herbal-remedies\",\n name: \"Aromatherapy & Herbal Remedies\",\n description:\n \"Essential oils, herbal balms, massage oils, salves, natural teas, rollers.\",\n },\n {\n id: \"other-herbal-aroma-products\",\n name: \"Other herbal or aroma-based products\",\n description: \"Wellness blends, herb sachets, custom infusions.\",\n },\n ],\n },\n {\n id: \"wellness-tools-accessories\",\n name: \"Wellness Tools & Accessories\",\n items: [\n {\n id: \"wellness-accessories\",\n name: \"Wellness Accessories\",\n description:\n \"Yoga mats, meditation cushions, eye pillows, incense, smudging sticks, eco water bottles, wellness journals.\",\n },\n {\n id: \"spiritual-tools-crystals\",\n name: \"Spiritual Tools & Crystals\",\n description:\n \"Healing crystals, gemstone bracelets, pendulums, sprays, spiritual kits, altar decor.\",\n },\n {\n id: \"other-wellness-spiritual-items\",\n name: \"Other wellness or spiritual items\",\n description: \"Items that aid relaxation, focus, or inner work.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const homeGardenHousehold: Category[] = [\n {\n id: \"home-garden-household-goods\",\n name: \"Home, Garden & Household Goods\",\n description:\n \"Functional, decorative, and eco-conscious products designed for everyday use indoors and outdoors.\",\n subcategories: [\n {\n id: \"home-decor-living\",\n name: \"Home Decor & Living\",\n items: [\n {\n id: \"home-decor\",\n name: \"Home Decor\",\n description:\n \"Cushions, wall art, table runners, vases, trays, mirrors, handmade centerpieces.\",\n },\n {\n id: \"kitchenware-dining\",\n name: \"Kitchenware & Dining\",\n description:\n \"Mugs, bowls, cutting boards, utensils, jars, coasters, kitchen textiles.\",\n },\n {\n id: \"mini-figures-decor\",\n name: \"Mini Figures & Décor\",\n description:\n \"handmade or non-handmade small figures, tiny houses and miniature decorative items.\",\n },\n {\n id: \"other-indoor-home-items\",\n name: \"Other indoor home items\",\n description:\n \"Any decorative or practical household items not listed above.\",\n },\n ],\n },\n {\n id: \"cleaning-eco-essentials\",\n name: \"Cleaning & Eco Essentials\",\n items: [\n {\n id: \"cleaning-eco-supplies\",\n name: \"Cleaning & Eco Supplies\",\n description:\n \"Beeswax wraps, reusable cloths, brushes, natural soaps, detergent bars, eco sponges.\",\n },\n {\n id: \"other-eco-cleaning-items\",\n name: \"Other eco or cleaning items\",\n description: \"Environmentally friendly goods not listed above.\",\n },\n ],\n },\n {\n id: \"garden-outdoor-living\",\n name: \"Garden & Outdoor Living\",\n items: [\n {\n id: \"plants-botanical-decor\",\n name: \"Plants & Botanical Decor\",\n description:\n \"Potted herbs, succulents, dried flowers, terrariums, plant-based ornaments.\",\n },\n {\n id: \"fresh-flowers-botanical-bouquets\",\n name: \"Fresh Flowers & Botanical Bouquets\",\n description:\n \"Cut flowers, seasonal bouquets, simple floral arrangements, native flower selections, and other fresh botanical items.\",\n },\n {\n id: \"natural-decor-nature-inspired-elements\",\n name: \"Natural Decor & Nature-Inspired Elements\",\n description:\n \"Seashell décor, driftwood pieces, sand ornaments, natural wood accents, stone or mineral decorations, and other nature-based decorative items.\",\n },\n {\n id: \"garden-tools-outdoor-items\",\n name: \"Garden Tools & Outdoor Items\",\n description:\n \"Plant markers, garden signs, stakes, small tools, wind chimes, gifts.\",\n },\n {\n id: \"other-outdoor-garden-products\",\n name: \"Other outdoor or garden products\",\n description: \"Functional or decorative items for outside use.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const petProductsAndAnimalGoods: Category[] = [\n {\n id: \"pet-products-animal-goods\",\n name: \"Pet Products & Animal Goods\",\n description: \"Items for pets, pet lovers, or animal-themed market stalls.\",\n subcategories: [\n {\n id: \"products-for-pets\",\n name: \"Products for Pets\",\n items: [\n {\n id: \"pet-food-treats\",\n name: \"Pet Food & Treats\",\n description:\n \"Homemade dog biscuits, cat snacks, natural chews, pet-safe cakes, training treats.\",\n },\n {\n id: \"apparel-toys-accessories\",\n name: \"Apparel, Toys & Accessories\",\n description:\n \"Leashes, collars, harnesses, toys, grooming tools, beds, travel gear, jumpers, bandanas.\",\n },\n {\n id: \"other-pet-products\",\n name: \"Other pet products\",\n description: \"Any pet-related items not listed above.\",\n },\n ],\n },\n {\n id: \"small-pets-birds-exotic-animals\",\n name: \"Small Pets, Birds & Exotic Animals\",\n items: [\n {\n id: \"products-small-pets-birds-exotics\",\n name: \"Products for Small Pets, Birds & Exotics\",\n description:\n \"Toys, enclosures, perches, feeding bowls, bedding, habitat decor, transport gear, and care items for birds, rabbits, hamsters, reptiles, turtles, aquarium pets, and other exotic species.\",\n },\n {\n id: \"other-small-exotic-animal-items\",\n name: \"Other small or exotic animal items\",\n description: \"Unusual accessories for non-mainstream pets.\",\n },\n ],\n },\n {\n id: \"farm-working-animals\",\n name: \"Farm & Working Animals\",\n items: [\n {\n id: \"goods-for-farm-working-animals\",\n name: \"Goods for Farm & Working Animals\",\n description:\n \"Treats, care products, equipment, signage and accessories for chickens, goats, alpacas, horses, and other livestock.\",\n },\n {\n id: \"other-farm-animal-items\",\n name: \"Other farm animal-related items\",\n description:\n \"Rural, barnyard, or utility-specific gear not listed above.\",\n },\n ],\n },\n {\n id: \"animal-themed-gifts-custom-items\",\n name: \"Animal-Themed Gifts & Custom Items\",\n items: [\n {\n id: \"pet-art-custom-gifts\",\n name: \"Pet Art & Custom Gifts\",\n description:\n \"Pet portraits, name tags, personalized bowls, breed-specific items, pet-themed home decor and stationery.\",\n },\n {\n id: \"other-animal-themed-gifts\",\n name: \"Other animal-themed gifts\",\n description:\n \"Artistic or sentimental items made for animal lovers.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"src/types\";\n\n/* eslint-disable sort-keys */\nexport const serviceAndExperience: Category[] = [\n {\n id: \"services-experiences\",\n name: \"Services & Experiences\",\n description:\n \"On-site offerings that provide entertainment, personal care, learning, or interactive activities beyond products.\",\n subcategories: [\n {\n id: \"personal-care-body-art\",\n name: \"Personal Care & Body Art\",\n items: [\n {\n id: \"nails-handcare\",\n name: \"Nails & Handcare\",\n description:\n \"Nail painting, decoration, quick manicures, temporary nail extensions.\",\n },\n {\n id: \"hair-styling-braiding\",\n name: \"Hair Styling & Braiding\",\n description:\n \"Hair braiding, plaits, child-friendly festival hairstyles.\",\n },\n {\n id: \"face-body-decoration\",\n name: \"Face & Body Decoration\",\n description:\n \"Henna, glitter tattoos, face painting, light makeup, eyelash styling, professional tattooing (where permitted).\",\n },\n {\n id: \"other-beauty-grooming-services\",\n name: \"Other beauty or grooming services\",\n description: \"Small-scale personal care options offered on-site.\",\n },\n ],\n },\n {\n id: \"practical-wellness-services\",\n name: \"Practical & Wellness Services\",\n items: [\n {\n id: \"mobile-practical-services\",\n name: \"Mobile & Practical Services\",\n description:\n \"Shoe repair, phone repairs, knife sharpening, key cutting, battery replacement, bike repairs, engraving.\",\n },\n {\n id: \"wellness-alternative-therapies\",\n name: \"Wellness & Alternative Therapies\",\n description:\n \"Massage, aromatherapy, reflexology, energy healing (e.g. Reiki), natural consultations.\",\n },\n {\n id: \"other-service-based-offerings\",\n name: \"Other service-based offerings\",\n description: \"Wellness or functional services not listed above.\",\n },\n ],\n },\n {\n id: \"creative-educational-experiences\",\n name: \"Creative & Educational Experiences\",\n items: [\n {\n id: \"creative-workshops-maker-services\",\n name: \"Creative Workshops & Maker Services\",\n description:\n \"Candle making, pottery, jewelry crafting, soap or balm workshops, calligraphy, seasonal crafts.\",\n },\n {\n id: \"education-awareness-stalls\",\n name: \"Education & Awareness Stalls\",\n description:\n \"Eco awareness, cultural storytelling, local history, first aid demos, health booths, sustainability education, kids’ science displays.\",\n },\n {\n id: \"other-creative-educational-services\",\n name: \"Other creative or educational services\",\n description:\n \"Informal learning, demonstrations, or community-focused sessions.\",\n },\n ],\n },\n {\n id: \"kids-activities-family-fun\",\n name: \"Kids’ Activities & Family Fun\",\n items: [\n {\n id: \"kids-activities-fun\",\n name: \"Kids’ Activities & Fun\",\n description:\n \"Face painting, glitter tattoos, pony rides, bouncy castles, small amusement rides, balloon twisting, animal petting zones.\",\n },\n {\n id: \"other-family-oriented-activities\",\n name: \"Other family-oriented activities\",\n description:\n \"On-site entertainment that engages children or family groups.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"src/types\";\n\n/* eslint-disable sort-keys */\nexport const toysChildren: Category[] = [\n {\n id: \"toys-childrens-items\",\n name: \"Toys & Children’s Items\",\n description: \"Products and services made for or inspired by children.\",\n subcategories: [\n {\n id: \"toys-playthings\",\n name: \"Toys & Playthings\",\n items: [\n {\n id: \"toys-classic-electric-character\",\n name: \"Toys – Classic, Electric & Character-Based\",\n description:\n \"Building blocks, dolls, puzzles, plush animals, toy vehicles, remote-control toys, light-up gadgets, character figurines, themed playsets.\",\n },\n {\n id: \"handmade-toys-crafty-playthings\",\n name: \"Handmade Toys & Crafty Playthings\",\n description:\n \"Wooden puzzles, crocheted animals, felt toys, fabric dolls, DIY kits, nature-inspired games, sensory toys.\",\n },\n {\n id: \"other-play-items\",\n name: \"Other play items\",\n description:\n \"Toys not listed above, including limited-edition or hybrid items.\",\n },\n ],\n },\n {\n id: \"educational-developmental\",\n name: \"Educational & Developmental\",\n items: [\n {\n id: \"educational-developmental-tools\",\n name: \"Educational & Developmental Tools\",\n description:\n \"STEM kits, Montessori toys, storybooks, picture books, flashcards, early learning games, language tools.\",\n },\n {\n id: \"other-educational-experience-based-items\",\n name: \"Other educational or experience-based items\",\n description:\n \"Creative experiences or learning aids not listed above.\",\n },\n ],\n },\n {\n id: \"baby-kidswear-accessories\",\n name: \"Baby & Kidswear + Accessories\",\n items: [\n {\n id: \"baby-kidswear-accessories\",\n name: \"Baby & Kidswear + Accessories\",\n description:\n \"Handmade baby clothes, toddler outfits, bibs, hats, headbands, bags, pacifier clips, soft shoes.\",\n },\n {\n id: \"baby-developmental-soft-toys\",\n name: \"Baby Developmental Soft Toys\",\n description:\n \"Sensory toys, rattles, fabric books, teething items, high-contrast cards, and early-skill. Montessori materials designed to support infants’ cognitive and motor development.\",\n },\n {\n id: \"other-childrens-clothing-accessories\",\n name: \"Other children’s clothing or accessories\",\n description: \"Unique fashion or functional pieces for kids.\",\n },\n ],\n },\n ],\n },\n];\n","/* eslint-disable sort-keys */\nimport { Category } from \"src/types\";\n\nexport const vintageAndAntique: Category[] = [\n {\n id: \"vintage-antique\",\n name: \"Vintage & Antique\",\n description:\n \"Unique, historic, or nostalgic items with collectible or decorative value.\",\n subcategories: [\n {\n id: \"vintage-antique-clothing-accessories\",\n name: \"Vintage & Antique Clothing & Accessories\",\n items: [\n {\n id: \"clothing-vintage-fashion\",\n name: \"Clothing and wearable items from past eras\",\n description:\n \"Vintage dresses, jackets, hats, gloves, belts, bags, shoes, jewellery.\",\n },\n {\n id: \"other-clothing-accessory-items\",\n name: \"Other clothing-related items\",\n description:\n \"Hair clips, brooches, pins, scarf rings, small fashion accessories.\",\n },\n ],\n },\n {\n id: \"collectibles-memorabilia\",\n name: \"Collectibles & Memorabilia\",\n items: [\n {\n id: \"small-collectible-items\",\n name: \"Small collectible items with historical or nostalgic significance\",\n description:\n \"Coins, stamps, toys, postcards, comics, sports cards, vintage packaging.\",\n },\n {\n id: \"other-collectible-items\",\n name: \"Other collectible items\",\n description:\n \"Rare small objects, miniature figurines, special-edition items.\",\n },\n ],\n },\n {\n id: \"homewares-decor-curiosities\",\n name: \"Homewares, Decor & Curiosities\",\n items: [\n {\n id: \"decorative-functional-vintage-items\",\n name: \"Decorative or functional items with a vintage or antique aesthetic\",\n description:\n \"Teacups, plates, vases, mirrors, clocks, furniture, old tools, lanterns, typewriters, curiosities.\",\n },\n {\n id: \"handmade-vintage-art\",\n name: \"Handmade vintage art\",\n description: \"Paintings, sculptures, crafted pieces.\",\n },\n {\n id: \"other-home-decor-items\",\n name: \"Other home or decor items\",\n description:\n \"Decorative pieces not listed above, unique household objects.\",\n },\n ],\n },\n {\n id: \"vintage-media-printed-nostalgia\",\n name: \"Vintage Media & Printed Nostalgia\",\n items: [\n {\n id: \"older-media-printed-works\",\n name: \"Older media formats and printed works\",\n description:\n \"Vinyl records, cassette tapes, CDs, DVDs, books, magazines, board games, posters.\",\n },\n {\n id: \"other-media-printed-items\",\n name: \"Other media or printed items\",\n description: \"Maps, manuals, leaflets, out-of-print materials.\",\n },\n ],\n },\n {\n id: \"other-vintage-antique-items\",\n name: \"Other Vintage & Antique Items\",\n items: [\n {\n id: \"any-vintage-antique-items-not-listed\",\n name: \"Any vintage or antique items not listed above\",\n description: \"Unique, rare or uncategorised pieces.\",\n },\n ],\n },\n ],\n },\n];\n","import { Category } from \"../../types/global\";\n\nimport { clothingAndFashion } from \"./clothingAndFashion\";\nimport { electronicsAndTechnology } from \"./electronicsAndTechnology\";\nimport { foodAndBeverages } from \"./foodAndBeverages\";\nimport { handmadeAndLocalProducts } from \"./handmadeAndLocalProducts\";\nimport { healthAndWellness } from \"./healthAndWellness\";\nimport { homeGardenHousehold } from \"./homeGardenHousehold\";\nimport { petProductsAndAnimalGoods } from \"./petProductsAndAnimalGoods\";\nimport { serviceAndExperience } from \"./serviceAndExperience\";\nimport { toysChildren } from \"./toysChildren\";\nimport { vintageAndAntique } from \"./vintageAndAntique\";\n\nexport const categoryColors: Record<string, string> = {\n \"clothing-fashion\": \"#9D4EDD\",\n \"electronics-technology\": \"#3AF3FF\",\n \"food-beverages\": \"#FF0D1F\",\n \"handmade-local-products\": \"#EE7E54\",\n \"health-wellness\": \"#E23794\",\n \"home-garden-household-goods\": \"#067325\",\n \"pet-products-animal-goods\": \"#68E788\",\n \"services-experiences\": \"#2E16A5\",\n \"toys-childrens-items\": \"#FFF966\",\n \"vintage-antique\": \"#8D6748\",\n};\n\nconst assignColorToCategories = (categories: Category[]): Category[] => {\n const result = categories.map((category) => ({\n ...category,\n color: categoryColors[category.id],\n }));\n return result;\n};\n\nexport const availableCategories = assignColorToCategories([\n ...foodAndBeverages,\n ...handmadeAndLocalProducts,\n ...clothingAndFashion,\n ...homeGardenHousehold,\n ...toysChildren,\n ...healthAndWellness,\n ...electronicsAndTechnology,\n ...vintageAndAntique,\n ...petProductsAndAnimalGoods,\n ...serviceAndExperience,\n]);\n","import { availableCategories } from \"../formFields/categories\";\nimport type { UnregisteredVendorType, VendorType } from \"../types\";\nimport type { DateTimeType } from \"../types/global\";\nimport { sortDatesChronologically } from \"../utils/date\";\n\ntype RelationType = NonNullable<VendorType[\"relations\"]>[number];\ntype RelationDateType = NonNullable<RelationType[\"relationDates\"]>[number];\n\nexport type StallholderFilterOption = {\n label: string;\n value: string;\n};\n\nexport function eventStartDatesSet(\n dateTime: DateTimeType[] | undefined,\n): Set<string> {\n return new Set((dateTime ?? []).map((date) => date.startDate));\n}\n\nexport function hasMatchingEventDate(\n vendorStartDates: string[],\n eventStartDates: Set<string>,\n): boolean {\n return vendorStartDates.some((startDate) => eventStartDates.has(startDate));\n}\n\nexport function matchesSelectedDate(\n vendorStartDates: string[],\n selectedDate: string | null,\n): boolean {\n if (!selectedDate) {\n return true;\n }\n\n return vendorStartDates.includes(selectedDate);\n}\n\nexport function matchesCategory(\n categoryNames: string[],\n selectedCategory: string | null,\n): boolean {\n if (!selectedCategory) {\n return true;\n }\n\n return categoryNames.includes(selectedCategory);\n}\n\nexport function getRegisteredVendorStartDates(vendor: VendorType): string[] {\n const relations = vendor.relations ?? [];\n\n return relations.flatMap((relation) =>\n (relation.relationDates ?? []).map(\n (relationDate: RelationDateType) => relationDate.dateTime.startDate,\n ),\n );\n}\n\nexport function getRegisteredVendorCategoryNames(vendor: VendorType): string[] {\n return (vendor.categories ?? []).map((category) => category.name);\n}\n\nexport function getUnregisteredVendorStartDates(\n vendor: UnregisteredVendorType,\n): string[] {\n return (vendor.invitations ?? []).flatMap((invitation) =>\n invitation.dateTime.map((date) => date.startDate),\n );\n}\n\nexport function getUnregisteredVendorCategoryNames(\n vendor: UnregisteredVendorType,\n): string[] {\n return availableCategories\n .filter(\n (category) => category.id && vendor.categoryIds.includes(category.id),\n )\n .map((category) => category.name);\n}\n\nexport function filterRegisteredVendorsForEvent(\n vendors: VendorType[] | null | undefined,\n eventStartDates: Set<string>,\n selectedDate: string | null,\n selectedCategory: string | null,\n): VendorType[] {\n if (!vendors) {\n return [];\n }\n\n return vendors.filter((vendor) => {\n const startDates = getRegisteredVendorStartDates(vendor);\n\n return (\n hasMatchingEventDate(startDates, eventStartDates) &&\n matchesSelectedDate(startDates, selectedDate) &&\n matchesCategory(\n getRegisteredVendorCategoryNames(vendor),\n selectedCategory,\n )\n );\n });\n}\n\nexport function filterUnregisteredVendorsForEvent(\n vendors: UnregisteredVendorType[],\n eventStartDates: Set<string>,\n selectedDate: string | null,\n selectedCategory: string | null,\n): UnregisteredVendorType[] {\n return vendors.filter((vendor) => {\n const startDates = getUnregisteredVendorStartDates(vendor);\n\n return (\n hasMatchingEventDate(startDates, eventStartDates) &&\n matchesSelectedDate(startDates, selectedDate) &&\n matchesCategory(\n getUnregisteredVendorCategoryNames(vendor),\n selectedCategory,\n )\n );\n });\n}\n\nexport function filterRegisteredVendorsByEventDatesOnly(\n vendors: VendorType[] | null | undefined,\n eventStartDates: Set<string>,\n): VendorType[] {\n if (!vendors) {\n return [];\n }\n\n return vendors.filter((vendor) =>\n hasMatchingEventDate(\n getRegisteredVendorStartDates(vendor),\n eventStartDates,\n ),\n );\n}\n\nexport function filterUnregisteredVendorsByEventDatesOnly(\n vendors: UnregisteredVendorType[],\n eventStartDates: Set<string>,\n): UnregisteredVendorType[] {\n return vendors.filter((vendor) =>\n hasMatchingEventDate(\n getUnregisteredVendorStartDates(vendor),\n eventStartDates,\n ),\n );\n}\n\nexport function collectStallholderStartDates(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): string[] {\n return [\n ...registeredVendors.flatMap(getRegisteredVendorStartDates),\n ...unregisteredVendors.flatMap(getUnregisteredVendorStartDates),\n ];\n}\n\nexport function getEventDatesWithStallholders(\n eventDateTime: DateTimeType[] | undefined,\n stallholderStartDates: string[],\n): DateTimeType[] {\n if (!eventDateTime?.length) {\n return [];\n }\n\n const startDates = new Set(stallholderStartDates);\n\n return sortDatesChronologically(\n eventDateTime.filter((date) => startDates.has(date.startDate)),\n );\n}\n\nexport function getStallholderCategoryNames(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): string[] {\n const names = [\n ...registeredVendors.flatMap(getRegisteredVendorCategoryNames),\n ...unregisteredVendors.flatMap(getUnregisteredVendorCategoryNames),\n ];\n\n return Array.from(new Set(names));\n}\n\nexport function getStallholderCategoryOptions(\n registeredVendors: VendorType[],\n unregisteredVendors: UnregisteredVendorType[] = [],\n): StallholderFilterOption[] {\n return getStallholderCategoryNames(\n registeredVendors,\n unregisteredVendors,\n ).map((name) => ({\n label: name,\n value: name,\n }));\n}\n\nexport function getEventStallholderEmptyMessage(\n hasAnyStallholdersForEvent: boolean,\n selectedDate: string | null,\n): string {\n return hasAnyStallholdersForEvent && selectedDate\n ? \"No stallholders found for this event date.\"\n : \"No stallholders found for this event.\";\n}\n","import type { StallholderFilterOption } from \"../eventStallholders/eventStallholderFilters\";\nimport type {\n EventListItemType,\n RelationDateTimeType,\n} from \"../generated/graphql\";\nimport { sortDatesChronologically } from \"../utils/date\";\n\ntype RelationType = NonNullable<\n NonNullable<EventListItemType[\"relations\"]>[number]\n>;\ntype RelationDateType = NonNullable<\n NonNullable<RelationType[\"relationDates\"]>[number]\n>;\n\nexport function relationHasSelectedDate(\n relation: RelationType,\n selectedDate: string,\n): boolean {\n return (\n relation.relationDates?.some(\n (date) => date != null && date.dateTime.startDate === selectedDate,\n ) ?? false\n );\n}\n\nexport function eventHasSelectedDate(\n event: EventListItemType,\n selectedDate: string,\n): boolean {\n return (\n event.relations?.some(\n (relation) =>\n relation != null && relationHasSelectedDate(relation, selectedDate),\n ) ?? false\n );\n}\n\nexport function filterVendorEventsBySelectedDate(\n events: EventListItemType[] | null | undefined,\n selectedDate: string | null,\n): EventListItemType[] {\n if (!events) {\n return [];\n }\n\n if (!selectedDate) {\n return events;\n }\n\n return events.filter((event) => eventHasSelectedDate(event, selectedDate));\n}\n\nexport function collectVendorEventRelationDateTimes(\n events: EventListItemType[] | null | undefined,\n): RelationDateTimeType[] {\n if (!events?.length) {\n return [];\n }\n\n return events.flatMap((event) =>\n (event.relations ?? []).flatMap((relation) => {\n if (relation == null) {\n return [];\n }\n return (relation.relationDates ?? []).flatMap((relationDate) =>\n relationDate?.dateTime != null ? [relationDate.dateTime] : [],\n );\n }),\n );\n}\n\nexport function getVendorEventRelationStartDates(\n events: EventListItemType[] | null | undefined,\n): string[] {\n const sortedDates = sortDatesChronologically(\n collectVendorEventRelationDateTimes(events),\n );\n const uniqueStartDates: string[] = [];\n\n for (const dateTime of sortedDates) {\n if (!uniqueStartDates.includes(dateTime.startDate)) {\n uniqueStartDates.push(dateTime.startDate);\n }\n }\n\n return uniqueStartDates;\n}\n\nexport function getVendorEventRelationDateOptions(\n events: EventListItemType[] | null | undefined,\n): StallholderFilterOption[] {\n return getVendorEventRelationStartDates(events).map((startDate) => ({\n label: startDate,\n value: startDate,\n }));\n}\n\nexport function getVendorEventsEmptyMessage(): string {\n return \"No events found.\";\n}\n","import { AffiliateRewardWriteType } from \"src/types/affiliate\";\n\nimport {\n AffiliateParticipantTypeEnum,\n AffiliateRewardEnumType,\n} from \"../generated/graphql\";\n\nexport const AFFILIATE_PARTICIPANT_TYPE_LABELS = {\n [AffiliateParticipantTypeEnum.Individual]: \"Individual\",\n [AffiliateParticipantTypeEnum.SoleTrader]: \"Sole Trader\",\n [AffiliateParticipantTypeEnum.Company]: \"Company\",\n} as const satisfies Record<AffiliateParticipantTypeEnum, string>;\n\ntype AffiliateRewardConfig = {\n description: string;\n value: number;\n};\n\nexport const AFFILIATE_REWARDS = {\n [AffiliateRewardEnumType.NewEventRegistration]: {\n description: \"10 points for new event registration (one-time reward)\",\n value: 10,\n },\n [AffiliateRewardEnumType.NewVendorRegistration]: {\n description: \"5 points for new vendor registration (one-time reward)\",\n value: 5,\n },\n [AffiliateRewardEnumType.ActiveVendorProSubscription]: {\n description: \"3 points for active vendor pro subscription (monthly reward)\",\n value: 3,\n },\n [AffiliateRewardEnumType.ActiveVendorStandardSubscription]: {\n description:\n \"1 point for active vendor standard subscription (monthly reward)\",\n value: 1,\n },\n [AffiliateRewardEnumType.ActiveVendorBonusReward]: {\n description:\n \"5 bonus points for every 10th active vendor (one-time reward)\",\n value: 5,\n },\n [AffiliateRewardEnumType.ActiveEventWithVendorRegistrations]: {\n description:\n \"40 points for active event with vendor registrations (one-time reward)\",\n value: 40,\n },\n} satisfies Record<AffiliateRewardEnumType, AffiliateRewardConfig>;\n\n/**\n * Create an affiliate reward\n * @param rewardType - The type of reward\n * @param createdAt - The date the reward was received\n * @returns The affiliate reward\n * @example\n * const reward = createAffiliateReward(AffiliateRewardEnumType.NewEventRegistration, new Date());\n * console.log(reward);\n */\nexport function createAffiliateReward(\n rewardType: AffiliateRewardEnumType,\n createdAt: Date,\n): AffiliateRewardWriteType {\n const reward = AFFILIATE_REWARDS[rewardType];\n\n return {\n createdAt,\n redeemedAt: null,\n rewardDescription: reward.description,\n rewardType,\n rewardValue: reward.value,\n };\n}\n","/**\n * NZ IRD number validation (Inland Revenue modulus-11 check digit).\n *\n * Valid range: 10,000,000 – 200,000,000 (upper limit raised Feb 2026; length stays\n * 8–9 digits). See IRD file-upload / payday filing specifications.\n */\n\nconst PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\nconst IRD_MIN = 10_000_000;\n/** Raised from 150_000_000 as of IRD's Feb 2026 annual update. */\nconst IRD_MAX = 200_000_000;\n\nfunction calculateCheckDigit(\n baseDigits: string,\n weights: readonly number[],\n): number {\n const sum = [...baseDigits].reduce(\n (acc, digit, index) => acc + Number(digit) * weights[index],\n 0,\n );\n const remainder = sum % 11;\n return remainder === 0 ? 0 : 11 - remainder;\n}\n\n/** Digits only; strips spaces/hyphens and other non-digits. */\nexport function normalizeIrdNumber(value: string): string {\n return value.replace(/\\D/g, \"\");\n}\n\n/**\n * Formats an IRD number as groups of three digits separated by hyphens\n * (e.g. `490-918-50` / `049-091-850`), capping at 9 digits.\n */\nexport function formatIrdNumber(input: string): string {\n const digitsOnly = normalizeIrdNumber(input).slice(0, 9);\n\n const parts = [];\n if (digitsOnly.length > 0) parts.push(digitsOnly.slice(0, 3));\n if (digitsOnly.length > 3) parts.push(digitsOnly.slice(3, 6));\n if (digitsOnly.length > 6) parts.push(digitsOnly.slice(6, 9));\n\n return parts.join(\"-\");\n}\n\n/**\n * Returns true when `value` is a valid NZ IRD number (8–9 digits, in issued\n * range, and modulus-11 check digit matches).\n */\nexport function isValidIrdNumber(value: string): boolean {\n const digits = normalizeIrdNumber(value);\n\n if (!/^\\d{8,9}$/.test(digits)) {\n return false;\n }\n\n const ird = Number(digits);\n if (ird < IRD_MIN || ird > IRD_MAX) {\n return false;\n }\n\n const padded = digits.padStart(9, \"0\");\n const baseDigits = padded.slice(0, -1);\n const checkDigit = Number(padded.slice(-1));\n\n let calculated = calculateCheckDigit(baseDigits, PRIMARY_WEIGHTS);\n if (calculated === 10) {\n calculated = calculateCheckDigit(baseDigits, SECONDARY_WEIGHTS);\n if (calculated === 10) {\n return false;\n }\n }\n\n return calculated === checkDigit;\n}\n","import { isTransientNetworkError } from \"./isTransientNetworkError\";\n\nexport const DEFAULT_USER_FACING_ERROR =\n \"Something went wrong. Please try again.\";\n\nexport const NETWORK_USER_FACING_ERROR =\n \"Unable to connect. Please check your internet connection and try again.\";\n\n/** Backend/infra messages that must never be shown to end users. */\nconst INTERNAL_ERROR_PATTERNS = [\n /connection\\s*<monitor>/i,\n /\\bmongo/i,\n /\\b\\d{1,3}(?:\\.\\d{1,3}){3}(?::\\d+)?\\b/,\n /\\beconn(?:refused|reset|aborted)\\b/i,\n /\\betimedout\\b/i,\n /\\benotfound\\b/i,\n /\\bsocket hang up\\b/i,\n /\\binternal server error\\b/i,\n /received status code 5\\d\\d/i,\n /\\bat\\s+\\S+\\s+\\([^)]+:\\d+:\\d+\\)/,\n] as const;\n\nexport function isInternalErrorMessage(message: string): boolean {\n return INTERNAL_ERROR_PATTERNS.some((pattern) => pattern.test(message));\n}\n\n/**\n * Maps raw API/client errors to a safe message for UI surfaces.\n * Keeps intentional product copy (e.g. \"Vendor not found\"); hides infra details.\n */\nexport function toUserFacingErrorMessage(\n error: string | null | undefined,\n fallback = DEFAULT_USER_FACING_ERROR,\n): string {\n const message = (error ?? \"\").trim();\n if (!message) return fallback;\n\n if (isTransientNetworkError(message)) {\n return NETWORK_USER_FACING_ERROR;\n }\n\n if (isInternalErrorMessage(message)) {\n return fallback;\n }\n\n return message;\n}\n\n/** Extracts and sanitizes an unknown catch/Apollo error for toasts and alerts. */\nexport function getErrorMessage(\n error: unknown,\n fallback = DEFAULT_USER_FACING_ERROR,\n): string {\n let raw: string | undefined;\n if (error instanceof Error) {\n raw = error.message;\n } else if (typeof error === \"string\") {\n raw = error;\n }\n\n return toUserFacingErrorMessage(raw, fallback);\n}\n","export function normalizePromoCode(\n decoratedPromoCode: string | null | undefined,\n): string | null {\n const normalized = decoratedPromoCode?.trim().toUpperCase();\n return normalized || null;\n}\n","import {\n SchemaCreateBulkNotificationInput,\n NotificationModel,\n} from \"src/mongoose/Notification\";\nimport { ObjectId } from \"src/types\";\n\n/**\n * Create notifications in the database for multiple users\n * This is typically called when sending push notifications\n */\nexport async function saveNotificationsInDb(\n payload: SchemaCreateBulkNotificationInput,\n): Promise<ObjectId[]> {\n const { data, message, title, type, userIds } = payload;\n console.log('NOTIFICATION DATA', JSON.stringify(payload, null, 2));\n try {\n const notifications = userIds.map((userId) => ({\n data,\n isRead: false,\n message,\n title,\n type,\n userId,\n }));\n\n // Save notifications to database\n await NotificationModel.insertMany(notifications);\n console.log(\n `Created ${notifications.length} notifications for ${userIds.length} users`,\n );\n\n return [...new Set(userIds)];\n } catch (error) {\n console.error(\"Failed to create notifications:\", error);\n return [];\n //throw new Error(`Failed to create notifications: ${error}`);\n }\n}\n","import { NotificationDataType } from \"@timardex/cluemart-shared\";\nimport { Expo, ExpoPushMessage, ExpoPushTicket } from \"expo-server-sdk\";\n\nimport { SchemaCreateBulkNotificationInput } from \"src/mongoose/Notification\";\nimport { PushTokenModel } from \"src/mongoose/PushToken\";\n\nconst expo = new Expo();\n\n/**\n * Safely extract tokens from ExpoPushMessage handling both string and array cases\n */\nfunction extractTokensFromMessage(message: ExpoPushMessage): string[] {\n return Array.isArray(message.to) ? message.to : [message.to];\n}\n\ninterface CreatePushMessagesOptions {\n tokens: string[];\n message: string;\n title: string;\n data: NotificationDataType;\n}\n\n/**\n * Create push messages from valid tokens\n */\nfunction createPushMessages({\n tokens,\n message,\n title,\n data,\n}: CreatePushMessagesOptions): {\n messages: ExpoPushMessage[];\n invalidTokens: string[];\n} {\n const messages: ExpoPushMessage[] = [];\n const invalidTokens: string[] = [];\n\n for (const token of tokens) {\n if (!Expo.isExpoPushToken(token)) {\n invalidTokens.push(token);\n continue;\n }\n\n messages.push({\n body: message,\n data: { ...data },\n sound: \"tui.wav\",\n title,\n to: token,\n });\n }\n\n return { invalidTokens, messages };\n}\n\n/**\n * Process chunk results and extract failed tokens\n */\nfunction processChunkResults(\n tickets: ExpoPushTicket[],\n chunk: ExpoPushMessage[],\n): { successCount: number; failedTokens: string[] } {\n let successCount = 0;\n const failedTokens: string[] = [];\n\n for (const [ticketIndex, ticket] of tickets.entries()) {\n if (ticket.status === \"error\") {\n const message = chunk[ticketIndex];\n if (message) {\n const tokens = extractTokensFromMessage(message);\n if (ticket.details?.error === \"DeviceNotRegistered\") {\n failedTokens.push(...tokens);\n }\n console.log(\"Push notification error\", {\n error: ticket.details?.error,\n tokens,\n });\n }\n } else {\n successCount++;\n }\n }\n\n return { failedTokens, successCount };\n}\n\n/**\n * Send a single chunk of push notifications\n */\nasync function sendChunk(\n chunk: ExpoPushMessage[],\n chunkIndex: number,\n): Promise<{ successCount: number; failedTokens: string[] }> {\n try {\n const tickets = await expo.sendPushNotificationsAsync(chunk);\n const { successCount, failedTokens } = processChunkResults(tickets, chunk);\n\n console.log(\n `Chunk ${chunkIndex + 1}: Sent ${successCount}/${chunk.length} notifications successfully`,\n );\n\n return { failedTokens, successCount };\n } catch (error) {\n console.log(\"Error sending Expo push notification chunk\", {\n chunkIndex,\n chunkSize: chunk.length,\n error: error instanceof Error ? error.message : String(error),\n });\n return { failedTokens: [], successCount: 0 };\n }\n}\n\nexport async function sendPushNotifications({\n data,\n message,\n title,\n userIds,\n}: SchemaCreateBulkNotificationInput) {\n const pushTokens = await PushTokenModel.find({ userId: { $in: userIds } });\n const expoTokens = pushTokens.map((token) => token.token);\n\n if (!data) return;\n\n const { messages, invalidTokens } = createPushMessages({\n data,\n message,\n title,\n tokens: expoTokens,\n });\n\n // Log invalid tokens\n if (invalidTokens.length > 0) {\n console.log(`Found ${invalidTokens.length} invalid push tokens`);\n }\n\n if (messages.length === 0) {\n console.log(\"No valid messages to send after filtering tokens\");\n return;\n }\n\n // Send notifications in chunks\n const chunks = expo.chunkPushNotifications(messages);\n let totalSuccessCount = 0;\n const allFailedTokens: string[] = [];\n\n for (const [chunkIndex, chunk] of chunks.entries()) {\n const { successCount, failedTokens } = await sendChunk(\n chunk,\n chunkIndex + 1,\n );\n totalSuccessCount += successCount;\n allFailedTokens.push(...failedTokens);\n }\n\n // Log final results\n console.log(\n `Sent push notification to ${totalSuccessCount}/${messages.length} tokens across ${chunks.length} chunks`,\n );\n\n if (allFailedTokens.length > 0) {\n console.log(`Found ${allFailedTokens.length} failed push tokens`);\n }\n}\n","import {\n AffiliateRewardEnumType,\n SubscriptionStatusEnumType,\n LicencesEnumType,\n UserLicenceType,\n UserType,\n} from \"@timardex/cluemart-shared\";\n\n/**\n * Paying = Stripe subscriptionId present and status active or trialing.\n * Matches `hasActiveStripeSubscription` in cluemart-server User utils.\n */\nfunction isPayingStripeSubscription(\n stripe?: UserType[\"stripe\"] | null,\n): boolean {\n if (!stripe?.subscriptionId) return false;\n\n return (\n (stripe.status === SubscriptionStatusEnumType.Active ||\n stripe.status === SubscriptionStatusEnumType.Trialing) &&\n (stripe.currentPlan === LicencesEnumType.ProVendor ||\n stripe.currentPlan === LicencesEnumType.ProPlusVendor)\n );\n}\n\n/**\n * Maps a vendor owner's highest valid vendor licence to a monthly subscription reward.\n *\n * Prefer Pro / Pro+ over Standard when both are valid.\n * Pro / Pro+ only yields the Pro reward when the owner has an active Stripe subscription;\n * otherwise falls back to the Standard subscription reward.\n * Returns `null` when no non-expired vendor licence applies.\n */\nexport function mapVendorLicenceToSubscriptionRewardType(\n licences: UserLicenceType[] | null | undefined,\n now: Date = new Date(),\n stripe?: UserType[\"stripe\"] | null,\n): AffiliateRewardEnumType | null {\n const validTypes = new Set(\n (licences ?? [])\n .filter(\n (licence) => new Date(licence.expiryDate).getTime() > now.getTime(),\n )\n .map((licence) => licence.licenceType),\n );\n\n if (\n validTypes.has(LicencesEnumType.ProVendor) ||\n validTypes.has(LicencesEnumType.ProPlusVendor)\n ) {\n if (isPayingStripeSubscription(stripe)) {\n return AffiliateRewardEnumType.ActiveVendorProSubscription;\n }\n return AffiliateRewardEnumType.ActiveVendorStandardSubscription;\n }\n\n if (validTypes.has(LicencesEnumType.StandardVendor)) {\n return AffiliateRewardEnumType.ActiveVendorStandardSubscription;\n }\n\n return null;\n}\n\n/** Inclusive start / exclusive end of the UTC calendar month containing `now`. */\nexport function getSubscriptionRewardPeriodBounds(now: Date = new Date()): {\n periodEnd: Date;\n periodStart: Date;\n} {\n const periodStart = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1),\n );\n const periodEnd = new Date(\n Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1),\n );\n return { periodEnd, periodStart };\n}\n","import {\n AffiliateResourceType,\n AffiliateRewardEnumType,\n NotificationResourceEnumType,\n NotificationEnumType,\n ResourceTypeEnum,\n PromoCodeType,\n UserType,\n} from \"@timardex/cluemart-shared\";\nimport { createAffiliateReward } from \"@timardex/cluemart-shared/utils\";\n\nimport {\n AffiliateModel,\n SchemaOwnerType,\n UserModel,\n VendorModel,\n} from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { normalizePromoCode } from \"../promoCode/normalizePromoCode\";\nimport { saveNotificationsInDb } from \"../saveNotificationsInDb\";\nimport { sendPushNotifications } from \"../sendPushNotifications\";\n\nimport {\n getSubscriptionRewardPeriodBounds,\n mapVendorLicenceToSubscriptionRewardType,\n} from \"./vendorSubscriptionRewards\";\n\nexport type AwardAffiliateVendorSubscriptionRewardsResult = {\n awarded: number;\n skipped: number;\n};\n\ntype LeanAffiliateResource = Omit<\n AffiliateResourceType,\n \"resourceId\" | \"resourceOwner\" | \"rewards\"\n> & {\n resourceId: ObjectId;\n resourceOwner: SchemaOwnerType;\n};\n\ntype LeanAffiliate = {\n _id: ObjectId;\n affiliateCode: PromoCodeType;\n affiliateResources: LeanAffiliateResource[];\n owner: SchemaOwnerType;\n};\n\ntype PeriodBounds = {\n periodEnd: Date;\n periodStart: Date;\n};\n\nfunction isEligibleVendorResource(resource: LeanAffiliateResource): boolean {\n return (\n resource.resourceType === ResourceTypeEnum.Vendor &&\n resource.resourceActive === true &&\n resource.resourceDeletedAt == null\n );\n}\n\nasync function findAffiliatesWithActiveVendorReferrals(): Promise<\n LeanAffiliate[]\n> {\n return (await AffiliateModel.find({\n affiliateResources: {\n $elemMatch: {\n resourceActive: true,\n resourceDeletedAt: null,\n resourceType: ResourceTypeEnum.Vendor,\n },\n },\n deletedAt: null,\n })\n .select(\"_id affiliateCode affiliateResources owner\")\n .lean()\n .exec()) as LeanAffiliate[];\n}\n\nasync function isEligibleReferredVendor({\n affiliateCode,\n resourceId,\n resourceOwnerUserId,\n}: {\n affiliateCode: string;\n resourceId: ObjectId;\n resourceOwnerUserId: ObjectId | string;\n}): Promise<boolean> {\n const match = await VendorModel.exists({\n _id: resourceId,\n active: true,\n deletedAt: null,\n \"owner.userId\": resourceOwnerUserId,\n promoCodes: affiliateCode,\n }).exec();\n\n return Boolean(match);\n}\n\nasync function resolveSubscriptionRewardTypeForVendorOwner(\n vendorOwnerUserId: ObjectId | string,\n now: Date,\n): Promise<AffiliateRewardEnumType | null> {\n const vendorOwner = await UserModel.findById(vendorOwnerUserId)\n .select(\"licences stripe\")\n .lean()\n .exec();\n\n return mapVendorLicenceToSubscriptionRewardType(\n vendorOwner?.licences,\n now,\n vendorOwner?.stripe as UserType[\"stripe\"],\n );\n}\n\nasync function notifyAffiliateOwnerOfSubscriptionReward({\n affiliateId,\n affiliateOwnerUserId,\n resourceName,\n rewardValue,\n}: {\n affiliateId: ObjectId | string;\n affiliateOwnerUserId: ObjectId | string;\n resourceName: string;\n rewardValue: number;\n}): Promise<void> {\n const payload = {\n data: {\n resourceId: String(affiliateId),\n resourceName,\n resourceType: NotificationResourceEnumType.AffiliateRewardReceived,\n },\n message: `You earned ${rewardValue} points! Your referred vendor \"${resourceName}\" has an active subscription.`,\n title: \"Affiliate points earned\",\n type: NotificationEnumType.System,\n userIds: [affiliateOwnerUserId as ObjectId],\n };\n\n try {\n await saveNotificationsInDb(payload);\n await sendPushNotifications(payload);\n } catch (error) {\n console.error(\n `[affiliate] subscription reward notification failed for affiliate ${String(affiliateId)}:`,\n error instanceof Error ? error.message : error,\n );\n }\n}\n\nasync function tryAwardSubscriptionReward({\n affiliate,\n createdAt,\n period,\n resource,\n rewardType,\n}: {\n affiliate: LeanAffiliate;\n createdAt: Date;\n period: PeriodBounds;\n resource: LeanAffiliateResource;\n rewardType: AffiliateRewardEnumType;\n}): Promise<\"awarded\" | \"skipped\"> {\n const reward = createAffiliateReward(rewardType, createdAt);\n\n const updateResult = await AffiliateModel.updateOne(\n {\n _id: affiliate._id,\n affiliateResources: {\n $elemMatch: {\n resourceActive: true,\n resourceDeletedAt: null,\n resourceId: resource.resourceId,\n resourceType: ResourceTypeEnum.Vendor,\n rewards: {\n $not: {\n $elemMatch: {\n createdAt: {\n $gte: period.periodStart,\n $lt: period.periodEnd,\n },\n rewardType,\n },\n },\n },\n },\n },\n deletedAt: null,\n },\n {\n $inc: { overallPoints: reward.rewardValue },\n $push: { \"affiliateResources.$.rewards\": reward },\n },\n ).exec();\n\n if (updateResult.modifiedCount === 0) {\n return \"skipped\";\n }\n\n await notifyAffiliateOwnerOfSubscriptionReward({\n affiliateId: affiliate._id,\n affiliateOwnerUserId: affiliate.owner.userId,\n resourceName: resource.resourceName,\n rewardValue: reward.rewardValue,\n });\n\n return \"awarded\";\n}\n\nasync function processAffiliateVendorReferral({\n affiliate,\n affiliateCode,\n createdAt,\n now,\n period,\n resource,\n}: {\n affiliate: LeanAffiliate;\n affiliateCode: string;\n createdAt: Date;\n now: Date;\n period: PeriodBounds;\n resource: LeanAffiliateResource;\n}): Promise<\"awarded\" | \"skipped\"> {\n const vendorEligible = await isEligibleReferredVendor({\n affiliateCode,\n resourceId: resource.resourceId,\n resourceOwnerUserId: resource.resourceOwner.userId,\n });\n if (!vendorEligible) {\n return \"skipped\";\n }\n\n const rewardType = await resolveSubscriptionRewardTypeForVendorOwner(\n resource.resourceOwner.userId,\n now,\n );\n if (!rewardType) {\n return \"skipped\";\n }\n\n return tryAwardSubscriptionReward({\n affiliate,\n createdAt,\n period,\n resource,\n rewardType,\n });\n}\n\n/**\n * Awards monthly vendor subscription rewards to affiliates with active referred vendors.\n *\n * Eligibility per affiliate resource:\n * - Affiliate is not soft-deleted\n * - Linked `affiliateResources` entry is vendor, `resourceActive`, and not deleted\n * - Vendor is active, not deleted, has the affiliate promo code, and\n * `owner.userId` matches `affiliateResources.resourceOwner.userId`\n * - `affiliateResources.resourceOwner` has a non-expired Standard / Pro / Pro+ vendor licence\n *\n * Reward type follows the owner's highest valid licence + Stripe paying status:\n * - Pro / Pro+ with paying Stripe (ACTIVE/TRIALING) → `ACTIVE_VENDOR_PRO_SUBSCRIPTION` (3 pts)\n * - Pro / Pro+ without paying Stripe → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)\n * - Standard → `ACTIVE_VENDOR_STANDARD_SUBSCRIPTION` (1 pt)\n *\n * Idempotent per affiliate + vendor + reward type + calendar month.\n * On successful award, notifies the affiliate owner (best-effort).\n *\n * @param now - Reference time for month bounds and licence expiry (defaults to now).\n * @returns Counts of awarded and skipped candidates.\n */\nexport async function awardAffiliateVendorSubscriptionRewards(\n now: Date = new Date(),\n): Promise<AwardAffiliateVendorSubscriptionRewardsResult> {\n const period = getSubscriptionRewardPeriodBounds(now);\n const createdAt = now;\n const affiliates = await findAffiliatesWithActiveVendorReferrals();\n\n let awarded = 0;\n let skipped = 0;\n const seenPairs = new Set<string>();\n\n for (const affiliate of affiliates) {\n const affiliateCode = normalizePromoCode(affiliate.affiliateCode);\n if (!affiliateCode) {\n continue;\n }\n\n for (const resource of affiliate.affiliateResources) {\n if (!isEligibleVendorResource(resource)) {\n continue;\n }\n\n const pairKey = `${String(affiliate._id)}:${String(resource.resourceId)}`;\n if (seenPairs.has(pairKey)) {\n continue;\n }\n seenPairs.add(pairKey);\n\n const outcome = await processAffiliateVendorReferral({\n affiliate,\n affiliateCode,\n createdAt,\n now,\n period,\n resource,\n });\n\n if (outcome === \"awarded\") {\n awarded += 1;\n } else {\n skipped += 1;\n }\n }\n }\n\n return { awarded, skipped };\n}\n","import { PromoCodeType } from \"@timardex/cluemart-shared\";\n\nimport { AffiliateModel } from \"src/mongoose\";\n\n/**\n * Loads the affiliate whose `affiliateCode` matches the given promo code.\n *\n * Affiliate codes are assigned on admin activation, so referrals only resolve after\n * a code exists. Soft-deleted affiliates (`deletedAt` set) are excluded.\n *\n * @param promoCode - Normalized affiliate promo code to resolve.\n * @returns The matching affiliate document, or `null` when not found.\n */\nexport async function findAffiliateByPromoCode(promoCode: PromoCodeType) {\n return AffiliateModel.findOne({\n affiliateCode: promoCode,\n deletedAt: null,\n }).exec();\n}\n","import { PromoCodeType } from \"@timardex/cluemart-shared\";\n\nimport { normalizePromoCode } from \"../promoCode/normalizePromoCode\";\n\n/**\n * Normalizes, trims, uppercases, and deduplicates resource promo codes.\n *\n * @param promoCodes - Raw promo codes from a create-resource mutation input.\n * @returns Unique normalized codes; empty strings and whitespace-only values are removed.\n */\nexport function normalizeAffiliatePromoCodes(\n promoCodes: PromoCodeType[] | null | undefined,\n): PromoCodeType[] {\n const normalized = (promoCodes ?? [])\n .map((code) => normalizePromoCode(code))\n .filter((code): code is PromoCodeType => code !== null);\n\n return [...new Set(normalized)];\n}\n","import mongoose from \"mongoose\";\n\n/**\n * Connect to MongoDB using Mongoose.\n * Supports both local MongoDB (via MONGODB_URI) and MongoDB Atlas (via individual env vars).\n */\nexport const connectToDatabase = async ({\n appName,\n dbName,\n dbPassword,\n dbUser,\n mongodbUri,\n}: {\n appName: string;\n dbName: string;\n dbPassword: string;\n dbUser: string;\n mongodbUri: string;\n}) => {\n try {\n // Check if MONGODB_URI is provided (for local Docker MongoDB)\n const mongoUri = mongodbUri\n ? mongodbUri\n : // Fallback to MongoDB Atlas connection string\n `mongodb+srv://${dbUser}:${dbPassword}@${dbName}.mongodb.net/?retryWrites=true&w=majority&appName=${appName}`;\n\n await mongoose.connect(mongoUri);\n\n const connectionType = mongodbUri ? \"Local MongoDB\" : \"MongoDB Atlas\";\n console.log(\n `${connectionType} connected from server/src/service/database.ts`,\n );\n } catch (err) {\n console.error(\"Error connecting to MongoDB:\", err);\n throw err; // You can throw the error if you want to stop the server in case of connection failure\n }\n};\n","import {\n NotificationModel,\n SchemaCreateBulkNotificationInput,\n} from \"src/mongoose/Notification\";\nimport { EnumPubSubEvents, GraphQLContext, ObjectId } from \"src/types\";\n\nimport { saveNotificationsInDb } from \"./saveNotificationsInDb\";\nimport { sendPushNotifications } from \"./sendPushNotifications\";\n\n/**\n * Publish-only pubsub used to emit GraphQL subscription events. Optional in\n * {@link notifyUsers} because non-GraphQL processes (e.g. the cron worker) have\n * no subscribers to publish to.\n */\nexport type NotificationPubSub = Pick<GraphQLContext[\"pubsub\"], \"publish\">;\n\n/** Publishes notification list/count subscription events for a user. */\nexport async function publishNotificationEvents(\n userId: ObjectId,\n pubsub: NotificationPubSub,\n) {\n try {\n // Get user's notifications for the subscription\n const userNotifications = await NotificationModel.find({\n userId,\n }).sort({ createdAt: -1 });\n\n // Get notification count\n const [total, unread] = await Promise.all([\n NotificationModel.countDocuments({ userId }),\n NotificationModel.countDocuments({ isRead: false, userId }),\n ]);\n\n // Publish both events\n pubsub.publish(EnumPubSubEvents.GET_NOTIFICATIONS, {\n getNotifications: userNotifications,\n getNotificationsUserId: userId,\n });\n\n pubsub.publish(EnumPubSubEvents.GET_NOTIFICATIONS_COUNT, {\n getNotificationsCount: { total, unread, userId },\n });\n\n console.log(`Published notification events for user: ${String(userId)}`);\n } catch (error) {\n console.error(\n `Failed to publish notification events for user ${String(userId)}:`,\n error,\n );\n }\n}\n\n/**\n * Sends push notifications, saves them in the database, and (when a `pubsub`\n * is provided) publishes GraphQL subscription events for each affected user.\n *\n * Errors are logged and swallowed; this function never throws. Instead it\n * returns `false` when notifications were not confirmed (an error was thrown,\n * or `saveNotificationsInDb` failed to persist any rows for a non-empty\n * `userIds`), so callers can gate success logs/metrics on the result.\n */\nexport async function notifyUsers({\n payload,\n pubsub,\n}: {\n payload: SchemaCreateBulkNotificationInput;\n pubsub?: NotificationPubSub;\n}): Promise<boolean> {\n try {\n await sendPushNotifications(payload);\n\n const uniqueUserIds = await saveNotificationsInDb(payload);\n // saveNotificationsInDb swallows its own errors and returns []; treat an\n // empty result for a non-empty input as a persistence failure.\n const persisted = payload.userIds.length === 0 || uniqueUserIds.length > 0;\n\n if (pubsub) {\n for (const userId of uniqueUserIds) {\n await publishNotificationEvents(userId, pubsub);\n }\n }\n\n return persisted;\n } catch (error) {\n console.error(\"Error in notifyUsers:\", error);\n return false;\n }\n}\n","import { AdStatusTypeEnum } from \"@timardex/cluemart-shared\";\n\nimport { AdModel } from \"src/mongoose\";\n\n/**\n * Updates ad statuses based on start/end dates and validity\n */\nexport async function updateAdStatuses(): Promise<void> {\n const now = new Date();\n\n // invalid\n const invalidResult = await AdModel.updateMany(\n {\n $or: [\n { start: { $exists: false } },\n { end: { $exists: false } },\n { $expr: { $gt: [\"$start\", \"$end\"] } },\n ],\n status: { $ne: AdStatusTypeEnum.Paused },\n },\n { $set: { status: AdStatusTypeEnum.Paused } },\n );\n\n // expired\n const expiredResult = await AdModel.updateMany(\n { end: { $lte: now }, status: { $ne: AdStatusTypeEnum.Expired } },\n { $set: { status: AdStatusTypeEnum.Expired } },\n );\n\n // active\n const activeResult = await AdModel.updateMany(\n {\n end: { $gt: now },\n start: { $lte: now },\n status: { $ne: AdStatusTypeEnum.Active },\n },\n { $set: { status: AdStatusTypeEnum.Active } },\n );\n\n // paused\n const pausedResult = await AdModel.updateMany(\n { start: { $gt: now }, status: { $ne: AdStatusTypeEnum.Paused } },\n { $set: { status: AdStatusTypeEnum.Paused } },\n );\n\n console.log(\n `✅ Ad statuses updated: invalid=${invalidResult.modifiedCount}, expired=${expiredResult.modifiedCount}, active=${activeResult.modifiedCount}, paused=${pausedResult.modifiedCount}`,\n );\n}\n","import { LicencesEnumType } from \"@timardex/cluemart-shared\";\n\nimport { UserModel, VendorModel, SchemaVendorType } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nexport async function updateVendorBasedOnUserLicense(\n userId: ObjectId,\n licenceType: LicencesEnumType,\n): Promise<void> {\n try {\n /**\n * Fetch user vendor reference\n */\n const user = await UserModel.findById(userId)\n .select(\"vendor\")\n .lean()\n .exec();\n\n if (!user?.vendor) {\n console.warn(`[updateVendor] No vendor found for userId=${userId}`);\n return;\n }\n\n /**\n * Fetch vendor\n */\n const vendor = await VendorModel.findById(user.vendor)\n .lean<SchemaVendorType>()\n .exec();\n\n if (!vendor) {\n console.warn(`[updateVendor] Vendor not found for id=${user.vendor}`);\n return;\n }\n\n /**\n * Build vendor update payload\n */\n const updateData: Partial<SchemaVendorType> = {};\n\n const isStandardVendor = licenceType === LicencesEnumType.StandardVendor;\n\n if (isStandardVendor) {\n updateData.availability = {\n corporate: false,\n private: false,\n school: false,\n };\n\n updateData.products = {\n active: false,\n productsList: vendor.products?.productsList ?? [],\n };\n\n updateData.calendar = {\n active: false,\n calendarData: vendor.calendar?.calendarData ?? [],\n };\n }\n\n /**\n * Image rules\n * STANDARD_VENDOR => only first 6 active, 6 is the default image limit for standard vendors\n * PRO_VENDOR => all active\n */\n updateData.images = (vendor.images ?? []).map((image, index) => ({\n ...image,\n active: isStandardVendor ? index < 6 : true,\n }));\n\n /**\n * Persist vendor updates\n */\n await VendorModel.updateOne({ _id: vendor._id }, { $set: updateData });\n } catch (error) {\n console.error(\"[updateVendorBasedOnUserLicense] Failed:\", error);\n }\n}\n","import mongoose from \"mongoose\";\n\n/**\n * True when `value` is a valid Mongo ObjectId string.\n */\nexport function isValidObjectId(value: string): boolean {\n return mongoose.Types.ObjectId.isValid(value);\n}\n\n/**\n * Recursively converts all ObjectId fields to strings in an object\n * This is needed because GraphQL expects string IDs, not ObjectIds\n */\nexport function convertObjectIdsToStrings(obj: any): any {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (obj instanceof mongoose.Types.ObjectId) {\n return obj.toString();\n }\n\n if (obj instanceof Date) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(convertObjectIdsToStrings);\n }\n\n if (typeof obj === \"object\") {\n const converted: any = {};\n for (const [key, value] of Object.entries(obj)) {\n converted[key] = convertObjectIdsToStrings(value);\n }\n return converted;\n }\n\n return obj;\n}\n","import {\n dateFormat,\n DateTimeType,\n EventDateStatusEnumType,\n timeFormat,\n} from \"@timardex/cluemart-shared\";\nimport dayjs from \"dayjs\";\nimport customParseFormat from \"dayjs/plugin/customParseFormat\";\nimport isoWeek from \"dayjs/plugin/isoWeek\";\n\nimport { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\n\n// Enable dayjs plugins used by event date parsing/status logic\ndayjs.extend(customParseFormat);\ndayjs.extend(isoWeek);\n\n/**\n * Determines the dateStatus for a future event date\n * @param startDateTime The start date-time of the event\n * @param now The current date-time\n * @returns The appropriate EventDateStatusEnumType\n */\nexport function getFutureEventStatus(\n startDateTime: dayjs.Dayjs,\n now: dayjs.Dayjs,\n): EventDateStatusEnumType {\n const hoursUntilStart = startDateTime.diff(now, \"hour\", true);\n if (hoursUntilStart > 0 && hoursUntilStart <= 4) {\n return EventDateStatusEnumType.StartingSoon;\n }\n\n if (startDateTime.isSame(now, \"day\")) {\n return EventDateStatusEnumType.Today;\n }\n\n if (startDateTime.isSame(now.add(1, \"day\"), \"day\")) {\n return EventDateStatusEnumType.Tomorrow;\n }\n\n if (startDateTime.isSame(now, \"isoWeek\")) {\n return EventDateStatusEnumType.ThisWeek;\n }\n\n if (startDateTime.isSame(now.add(1, \"week\"), \"isoWeek\")) {\n return EventDateStatusEnumType.NextWeek;\n }\n\n return EventDateStatusEnumType.Upcoming;\n}\n\n/**\n * Updates the dateStatus field for a single DateTimeType object based on the current date/time\n * @param dateTime The date-time object to update\n * @returns A new object with updated dateStatus value\n */\nexport function updateSingleDateTimeStatus<T extends DateTimeType>(\n dateTime: T,\n now: dayjs.Dayjs = dayjs(),\n): T {\n // Parse start and end date-time\n const dateTimeFormat = `${dateFormat} ${timeFormat}`;\n const startDateTime = dayjs(\n `${dateTime.startDate} ${dateTime.startTime}`,\n dateTimeFormat,\n true,\n );\n const endDateTime = dayjs(\n `${dateTime.endDate} ${dateTime.endTime}`,\n dateTimeFormat,\n true,\n );\n\n // Skip if dates are invalid\n if (!startDateTime.isValid() || !endDateTime.isValid()) {\n return {\n ...dateTime,\n dateStatus: EventDateStatusEnumType.Invalid,\n };\n }\n\n if (endDateTime.isBefore(startDateTime)) {\n return {\n ...dateTime,\n dateStatus: EventDateStatusEnumType.Invalid,\n };\n }\n\n let dateStatus: EventDateStatusEnumType;\n\n if (endDateTime.isAfter(now)) {\n if (startDateTime.isAfter(now)) {\n // Event has not started yet\n dateStatus = getFutureEventStatus(startDateTime, now);\n } else {\n // Event is in progress\n dateStatus = EventDateStatusEnumType.Started;\n }\n } else {\n // Event has ended\n dateStatus = EventDateStatusEnumType.Ended;\n }\n\n return {\n ...dateTime,\n dateStatus,\n };\n}\n\nconst dateTimeStatusFilter = {\n dateTime: { $ne: [], $type: \"array\" },\n deletedAt: null,\n} as const;\n\nconst CURSOR_BATCH_SIZE = 50;\n\ntype DocWithDateTime = {\n _id: unknown;\n dateTime: DateTimeType[];\n};\n\ntype DateTimeBulkWriteOp = {\n updateOne: {\n filter: { _id: unknown };\n update: { $set: { dateTime: DateTimeType[] } };\n };\n};\n\ntype DateTimeCursor = AsyncIterable<DocWithDateTime> & {\n close: () => Promise<unknown>;\n};\n\ntype DateTimeUpdatableModel = {\n bulkWrite: (ops: DateTimeBulkWriteOp[]) => Promise<unknown>;\n find: (filter: typeof dateTimeStatusFilter) => {\n select: (fields: { _id: 1; dateTime: 1 }) => {\n lean: () => {\n cursor: (opts: { batchSize: number }) => DateTimeCursor;\n };\n };\n };\n};\n\nfunction hasDateTimeStatusChanges(\n stored: DateTimeType[],\n updated: DateTimeType[],\n): boolean {\n if (stored.length !== updated.length) {\n return true;\n }\n\n return stored.some(\n (slot, index) => slot.dateStatus !== updated[index].dateStatus,\n );\n}\n\nasync function updateModelDateTimeStatuses(\n model: DateTimeUpdatableModel,\n now: dayjs.Dayjs,\n): Promise<number> {\n let updated = 0;\n const cursor = model\n .find(dateTimeStatusFilter)\n .select({ _id: 1, dateTime: 1 })\n .lean()\n .cursor({ batchSize: CURSOR_BATCH_SIZE });\n\n let bulkOps: DateTimeBulkWriteOp[] = [];\n\n const flushBulkWrites = async (): Promise<void> => {\n if (!bulkOps.length) {\n return;\n }\n\n await model.bulkWrite(bulkOps);\n updated += bulkOps.length;\n bulkOps = [];\n };\n\n try {\n for await (const doc of cursor) {\n const dateTime = doc.dateTime.map((slot) =>\n updateSingleDateTimeStatus(slot, now),\n );\n\n if (!hasDateTimeStatusChanges(doc.dateTime, dateTime)) {\n continue;\n }\n\n bulkOps.push({\n updateOne: {\n filter: { _id: doc._id },\n update: { $set: { dateTime } },\n },\n });\n\n if (bulkOps.length >= CURSOR_BATCH_SIZE) {\n await flushBulkWrites();\n }\n }\n\n await flushBulkWrites();\n } finally {\n await cursor.close().catch(() => undefined);\n }\n\n return updated;\n}\n\n/**\n * Recomputes dateStatus for every dateTime slot on events and google imported markets.\n */\nexport async function updateAllEventDateTimeStatuses(): Promise<void> {\n const now = dayjs();\n const [eventCount, marketCount] = await Promise.all([\n updateModelDateTimeStatuses(EventModel as DateTimeUpdatableModel, now),\n updateModelDateTimeStatuses(\n GoogleImportedMarketModel as DateTimeUpdatableModel,\n now,\n ),\n ]);\n\n console.log(\n `✅ Event dateTime statuses updated: events=${eventCount} changed, google imported markets=${marketCount} changed`,\n );\n}\n","import { EventListItemType } from \"@timardex/cluemart-shared\";\n\nimport { EventModel, GoogleImportedMarketModel } from \"src/mongoose\";\nimport { ObjectId } from \"src/types\";\n\nimport { convertObjectIdsToStrings } from \"../objectIdToString\";\n\ntype EventOrMarket = Pick<EventListItemType, \"_id\" | \"name\"> | null;\n\n/**\n * This function attempts to find an Event or a Google Imported Market by the given resource ID.\n * It first normalizes the resource ID to a string format, then performs parallel queries to both collections.\n * If an Event is found, it returns that; otherwise, it checks for a Google Imported Market and returns it if found.\n * If neither is found, it returns null.\n * @param resourceId - The ID of the resource to find, which can be an ObjectId, string, null, or undefined.\n * @returns A promise that resolves to either an Event or a Google Imported Market object containing _id and name, or null if not found.\n */\n\nexport async function findEventOrImportedMarketById(\n resourceId: ObjectId | string | null | undefined,\n): Promise<EventOrMarket> {\n if (!resourceId) {\n return null;\n }\n\n const normalizedId = convertObjectIdsToStrings(resourceId) as string;\n\n const [eventDoc, googleImportedDoc] = await Promise.all([\n EventModel.findById(normalizedId)\n .select(\"_id name\")\n .lean<Pick<EventListItemType, \"_id\" | \"name\">>()\n .exec(),\n GoogleImportedMarketModel.findById(normalizedId)\n .select(\"_id name\")\n .lean<Pick<EventListItemType, \"_id\" | \"name\">>()\n .exec(),\n ]);\n\n return eventDoc ?? googleImportedDoc;\n}\n","import {\n DateTimeWithPriceType,\n InviteEnumType,\n} from \"@timardex/cluemart-shared\";\nimport { DateTimeType } from \"@timardex/cluemart-shared/types\";\n\nimport { SchemaRelationType } from \"src/mongoose/Relation\";\n\ntype EventDateSlot = Pick<DateTimeType, \"startDate\" | \"startTime\">;\n\n/**\n * Returns true when at least one startDate/startTime slot from the previous\n * schedule is absent from the next schedule (i.e. a date was removed).\n */\nexport function didRemoveAnyEventDates(\n previousDateTime: EventDateSlot[] | undefined,\n nextDateTime: EventDateSlot[] | undefined,\n): boolean {\n if (!previousDateTime?.length) {\n return false;\n }\n\n return previousDateTime.some(\n (prev) =>\n !nextDateTime?.some(\n (next) =>\n next.startDate === prev.startDate &&\n next.startTime === prev.startTime,\n ),\n );\n}\n\n/**\n * Helper: Update relationDates based on event's dateTime\n * Marks dates as UNAVAILABLE if they no longer exist in the event's dateTime.\n */\nexport function updateRelationDatesToUnavailable(\n relationDates: SchemaRelationType[\"relationDates\"],\n eventDateTime: DateTimeWithPriceType[] | undefined,\n): SchemaRelationType[\"relationDates\"] {\n return relationDates.map((relationDate) => {\n // Check if this relationDate exists in the event's dateTime\n const existsInEvent =\n eventDateTime?.some(\n (dt) =>\n dt.startDate === relationDate.dateTime.startDate &&\n dt.startTime === relationDate.dateTime.startTime,\n ) ?? false;\n\n return {\n ...relationDate,\n status: existsInEvent\n ? relationDate.status\n : InviteEnumType.Unavailable,\n };\n });\n}\n"],"mappings":"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J;QACF;MACF;IACF;EACF;AACF;ACnGO,IAAM,2BAAuC;EAClD;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC/EO,IAAM,mBAA+B;EAC1C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACpLO,IAAM,2BAAuC;EAClD;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC9JO,IAAM,oBAAgC;EAC3C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;ACnEO,IAAM,sBAAkC;EAC7C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;IACF;EACF;AACF;AC3FO,IAAM,4BAAwC;EACnD;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;IACF;EACF;AACF;ACpFO,IAAM,uBAAmC;EAC9C;IACE,IAAI;IACJ,MAAM;IACN,aACE;IACF,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aAAa;UACf;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;IACF;EACF;AACF;ACvGO,IAAM,eAA2B;EACtC;IACE,IAAI;IACJ,MAAM;IACN,aAAa;IACb,eAAe;MACb;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;QACF;MACF;MACA;QACE,IAAI;QACJ,MAAM;QACN,OAAO;UACL;YACE,IAAI;YACJ,MAAM;YACN,aACE;UACJ;UACA;YACE,IAAI;YACJ,MAAM;YA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","names":["EventDateStatusEnumType","InviteEnumType","PaymentMethodEnumType","EventDateStatusEnumType","EnumRegions","dayjs","InviteEnumType","mongoose","dayjs","customParseFormat","dayjs","customParseFormat"]}