import type { LotteryCity, lotteryHeader, LotteryTable, Lottery, LotteryPrize, LotteryResultItem, } from '#lib/types' import { DATE_TIME_FORMAT, PRIZES } from '#lib/constants' import { LOTTERY_API } from '#lib/configs' import type { Dayjs } from 'dayjs' import { formatDate, toDate } from '#lib/utils' import { splitStringAndWrap } from '#lib/utils/string' import { useRuntimeConfig } from 'nuxt/app' import { ref, reactive, onMounted, nextTick } from 'vue' /** * Composable for managing and displaying lottery results. * Provides functionality to fetch lottery data, handle date and region changes, * format and highlight results, and manage UI state. * * @returns {Object} An object containing functions and reactive variables for lottery management. * @namespace */ export function useLottery() { const runtimeConfig = useRuntimeConfig() const baseURLLottery = runtimeConfig.public.DOMAIN_LOTTERY_RESULT const isLoading = ref(false) const currentDate = new Date() const THRESHOLD_HOUR = 19 // 7:00 pm const lotteryOption = ref(0) const probeNumber = ref('') const regions = ref([]) const lotteryHeader = reactive({ region: regions.value[0] ?? null, date: currentDate, }) const lotteryTable = reactive({ originalResult: {}, lottery: { title: '', result: {}, }, }) // Adjust current date if it's before the threshold hour. if (currentDate?.getHours() < THRESHOLD_HOUR) { currentDate.setDate(currentDate.getDate() - 1) } /** * Updates the lottery header date and fetches cities based on the selected date. * * @param {Dayjs} value The selected date. */ const onChangeDate = (value: Dayjs): void => { if (value) { lotteryHeader.date = value void fetchCities( formatDate(value.toString(), DATE_TIME_FORMAT.SHORT_DATE_FORMAT), ) } } /** * Fetches the list of cities based on the provided date. * * @param {string} [date] The date to fetch cities for. * @returns {Promise} A promise that resolves when the cities have been fetched. */ const fetchCities = async (date?: string): Promise => { const newDate = date ?? formatDate(toDate(), DATE_TIME_FORMAT.SHORT_DATE_FORMAT) isLoading.value = true const data = await $fetch(baseURLLottery + LOTTERY_API.LOTTERY_CITY, { query: { date: newDate, }, }) const responseData = typeof data === 'string' ? JSON.parse(data) : data if (responseData != null) { const newData = responseData?.rows?.map((item: LotteryCity) => ({ ...item, code: item.id, label: item.name, value: item.id, })) ?? [] regions.value = newData lotteryHeader.region = regions.value[0] await getLotteries(lotteryHeader.date, regions.value[0]) } isLoading.value = false } /** * Fetches the lottery result for a specific city and date. * * @param {number} city The city ID for which to fetch the lottery results. * @param {string} [date] The date to fetch the lottery results for. * @returns {Promise} A promise that resolves to the lottery results. */ const fetchLotteryResult = async ( city: number, date?: string, ): Promise => { const newDate = date ?? formatDate(toDate(), DATE_TIME_FORMAT.SHORT_DATE_FORMAT) const data = await $fetch(baseURLLottery + LOTTERY_API.LOTTERY_RESULTS, { immediate: true, server: true, query: { date: newDate, city, }, }) const responseData = typeof data === 'string' ? JSON.parse(data) : data return responseData ? responseData?.rows : { title: '', result: {} } } /** * Retrieves lottery data for a specific date and region. * * @param {Dayjs | Date} [date] The date to retrieve the data for. * @param {LotteryCity | null} [region] The region to retrieve the data for. * @returns {Promise} A promise that resolves when the data has been retrieved. */ const getLotteries = async ( date: Dayjs | Date = lotteryHeader.date, region: LotteryCity | null = regions.value[0], ): Promise => { const newDate = formatDate( date.toString(), DATE_TIME_FORMAT.SHORT_DATE_FORMAT, ) const data = region?.id ? await fetchLotteryResult(region.id, newDate) : { title: '', result: {} } lotteryTable.lottery = data lotteryTable.originalResult = data.result onSearchNumber(lotteryOption.value) } /** * Searches and formats lottery numbers based on the selected option. * * @param {number} option The number of digits to display. */ const onSearchNumber = (option: number): void => { let result: LotteryPrize = {} Object.entries(lotteryTable.originalResult).forEach((item) => { if (item.length >= 2) { let currentOption = item[1] if (item[0].includes('prize') || item[0] === 'special') { const numberData = item[1]?.toString()?.split('-') currentOption = numberData ?.map((it) => { if (!option) { return it.trim() } return option > it.trim().length ? '' : it.trim().slice(-option) }) .filter((it) => it) .join(' - ') ?? '' } result = { ...result, [item[0]]: item[1] && !currentOption ? null : currentOption, } } }) lotteryOption.value = option lotteryTable.lottery.result = result } /** * Renders the HTML tables for displaying lottery results. * * @returns {LotteryResultItem[]} An array of lottery result items with HTML formatting. */ const renderHtmlTables = (): LotteryResultItem[] => { return PRIZES.map((prize) => ({ ...prize, value: guessNumbers(lotteryTable.lottery.result[prize.key] ?? ''), })) } /** * Formats the lottery result data by highlighting the probe number. * * @param {string} value The lottery result data to format. * @param {string} probeNumber The number to highlight in the result. * @returns {string} The formatted lottery result data with HTML highlighting. */ const formatData = (value: string, probeNumber: string): string => { const numbers = value.split('-') const data = numbers.map((item: string) => { item = item.trim() if (probeNumber) { const regex = `(${probeNumber})(?!\\d)` item = item.replace( new RegExp(regex, 'g'), (match) => `${match}`, ) } return item }) return data.join(' - ') } /** * Converts a string containing numbers into HTML paragraphs. * * @param {string} input The string to convert. * @returns {string} The converted HTML string. */ const convertStringSpecial = (input: string): string => { let result = '' for (let i = 0; i < input.length; i++) { const char = input[i] result += /\d/.test(char) ? `${char}` : char } return result } /** * Formats special lottery result data with HTML highlighting and paragraphs. * * @param {string} value The lottery result data to format. * @param {string} probeNumber The number to highlight in the result. * @returns {string} The formatted lottery result data with HTML highlighting and paragraphs. */ const formatLotterySpecial = (value: string, probeNumber: string): string => { const numbers = value.replace(/<\/?p>/g, '').split('-') const data = numbers.map((item: string) => { item = item.trim() if (probeNumber) { const regex = `(${probeNumber})(?!\\d)` item = item.replace( new RegExp(regex, 'g'), (match) => `${match}`, ) return new RegExp(regex).test(item) ? convertStringSpecial(item) : splitStringAndWrap(item, 'p') } return '

' + item + '

' }) return data.join('') } /** * Formats and highlights lottery result numbers based on the probe number. * * @param {string | null | number} value The value to format. * @param {boolean} [isSpecial=false] Whether the result is special. * @returns {string} The formatted result. */ const guessNumbers = ( value: string | null | number, isSpecial: boolean = false, ): string => { if (!value) { return '-' } if (!probeNumber.value) { return value.toString().split('-').join(' - ') } return probeNumber.value && isSpecial ? formatLotterySpecial(value.toString(), probeNumber.value) : formatData(value.toString(), probeNumber.value) } /** * Replaces specific text patterns in a string with formatted text. * * @param {string} value The string to process. * @returns {string} The processed string. */ const replaceText = (value: string): string => { return value .replace(/ ngày/g, ', ngày') .replace(/-/g, '/') .replace(/ chưa có kết quả/g, '') } /** * Updates the lottery header region and fetches lottery results for the selected region. * * @param {LotteryCity} item The selected city. */ const onChangeRegion = (item: LotteryCity): void => { lotteryHeader.region = item void getLotteries(lotteryHeader.date, item) } // Fetch initial city data after component mount onMounted(() => { nextTick(() => { void fetchCities( formatDate( lotteryHeader.date.toString(), DATE_TIME_FORMAT.SHORT_DATE_FORMAT, ), ) }) }) return { onChangeDate, onSearchNumber, onChangeRegion, replaceText, renderHtmlTables, convertStringSpecial, currentDate, lotteryHeader, isLoading, regions, lotteryTable, lotteryOption, probeNumber, } }