Files
ghatreh_user/src/utils/storeHours.ts
T
2026-06-29 22:43:28 +03:30

210 lines
6.1 KiB
TypeScript

import { ref } from 'vue'
import { useSettingsStore } from '@/store/settingsStore'
import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price'
export interface StoreTradingHours {
start: string
end: string
closePriceEnabled: boolean
}
export interface EffectiveTradingHours {
start: string
end: string
enforced: boolean
}
const productTradingHours = ref<{ start: string; end: string } | null>(null)
const productTradingHoursVersion = ref(0)
function normalizeTradingTimeSetting(value: unknown, fallback: string): string {
const raw = String(value ?? '').trim()
return raw || fallback
}
function parseTimeToSeconds(time: string): number {
const parts = time.trim().split(':').map(Number)
const [hours = 0, minutes = 0, seconds = 0] = parts
return hours * 3600 + minutes * 60 + seconds
}
export function formatTradingTimeDisplay(time: string): string {
const normalized = normalizeTradingTimeSetting(time, '')
if (!normalized) {
return ''
}
const parts = normalized.split(':')
if (parts.length >= 3) {
return `${parts[0]}:${parts[1]}:${parts[2]}`
}
if (parts.length >= 2) {
return `${parts[0]}:${parts[1]}:00`
}
return normalized
}
function hasProductTradingWindow(start?: string | null, end?: string | null): boolean {
const open = formatTradingTimeDisplay(normalizeTradingTimeSetting(start, '00:00:00'))
const close = formatTradingTimeDisplay(normalizeTradingTimeSetting(end, '00:00:00'))
if (!open || !close) {
return false
}
return open !== close
}
export function setProductTradingHours(start?: string | null, end?: string | null) {
if (!hasProductTradingWindow(start, end)) {
productTradingHours.value = null
productTradingHoursVersion.value += 1
return
}
productTradingHours.value = {
start: normalizeTradingTimeSetting(start, '00:00'),
end: normalizeTradingTimeSetting(end, '23:59'),
}
productTradingHoursVersion.value += 1
}
export function syncProductTradingHoursFromProducts(
products: Array<{ auto_open_at?: string | null; auto_close_at?: string | null }>,
) {
for (const product of products) {
if (hasProductTradingWindow(product.auto_open_at, product.auto_close_at)) {
setProductTradingHours(product.auto_open_at, product.auto_close_at)
return
}
}
setProductTradingHours(null, null)
}
export function getStoreTradingHours(): StoreTradingHours {
const settings = useSettingsStore()
const closeRaw = settings.getSettingByKey('close_price')
const closePriceEnabled =
closeRaw === true ||
closeRaw === 1 ||
String(closeRaw ?? 'false').trim().toLowerCase() === 'true'
return {
start: normalizeTradingTimeSetting(settings.getSettingByKey('startDay'), '00:00'),
end: normalizeTradingTimeSetting(settings.getSettingByKey('endDay'), '23:59'),
closePriceEnabled,
}
}
export function getEffectiveTradingHours(): EffectiveTradingHours {
void productTradingHoursVersion.value
const settings = getStoreTradingHours()
if (settings.closePriceEnabled) {
return {
start: settings.start,
end: settings.end,
enforced: true,
}
}
if (productTradingHours.value?.start && productTradingHours.value?.end) {
return {
start: productTradingHours.value.start,
end: productTradingHours.value.end,
enforced: true,
}
}
return {
start: settings.start,
end: settings.end,
enforced: false,
}
}
export function isStoreClosedBySchedule(now = new Date()): boolean {
const { start, end, enforced } = getEffectiveTradingHours()
if (!enforced) {
return false
}
const currentSeconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds()
const openSeconds = parseTimeToSeconds(start)
const closeSeconds = parseTimeToSeconds(end)
return currentSeconds < openSeconds || currentSeconds > closeSeconds
}
export function isStoreClosed(): boolean {
void productTradingHoursVersion.value
const { closePriceEnabled } = getStoreTradingHours()
if (closePriceEnabled) {
if (lastPrice.value?.closed === true) {
return true
}
return isStoreClosedBySchedule()
}
// وقتی close_price خاموش است، صفر بودن قیمت مرجع به معنی بسته بودن فروشگاه نیست.
return isStoreClosedBySchedule()
}
export function getStoreClosedMessage(): string {
const { start, end } = getEffectiveTradingHours()
const open = formatTradingTimeDisplay(start)
const close = formatTradingTimeDisplay(end)
if (!open || !close) {
return 'فروشگاه در حال حاضر بسته است.'
}
return `فروشگاه در حال حاضر بسته است. ساعت کاری از ${open} تا ${close} می‌باشد.`
}
export type PriceDisplayState = 'open' | 'closed' | 'no_quote'
export const NO_QUOTE_MESSAGE = 'مظنه در دسترس نیست'
/**
* وضعیت نمایش قیمت در صفحه اصلی:
* - closed → فروشگاه بسته است (خارج از ساعت کاری close_price یا بازه محصول)
* - no_quote → مظنه در دسترس نیست (قیمت زنده نداریم و show_last_price خاموش است)
* - open → نمایش عادی قیمت
*
* اولویت با status ارسالی از سرور است؛ اگر نبود به منطق سمت کلاینت برمی‌گردیم.
*/
export function getPriceDisplayState(): PriceDisplayState {
void productTradingHoursVersion.value
const p = lastPrice.value
const status = p?.status
if (status === 'closed') {
return 'closed'
}
if (status === 'no_quote') {
return getTradeBuyPrice(p) > 0 ? 'open' : 'no_quote'
}
if (status === 'open') {
return getTradeBuyPrice(p) > 0 || getTradeSellPrice(p) > 0 ? 'open' : 'no_quote'
}
// سرور status نفرستاده (سازگاری با نسخه قدیمی) → استنتاج سمت کلاینت
if (isStoreClosed()) {
return 'closed'
}
return getTradeBuyPrice(p) > 0 || getTradeSellPrice(p) > 0 ? 'open' : 'no_quote'
}
export function getPriceDisplayMessage(state: PriceDisplayState = getPriceDisplayState()): string {
if (state === 'closed') {
return getStoreClosedMessage()
}
if (state === 'no_quote') {
return NO_QUOTE_MESSAGE
}
return ''
}