Persian keyboards send ٫ ، and / as decimal or thousand separators, so amount and weight fields now normalize input before parsing. Support chat reopens an existing thread instead of starting a new one, and axios surfaces the server message on failure. Co-authored-by: Cursor <cursoragent@cursor.com>
389 lines
12 KiB
TypeScript
389 lines
12 KiB
TypeScript
import { ref } from 'vue'
|
|
import { getSettings } from '@/api/configuration'
|
|
import type { DepositGuideVars } from '@/utils/depositGuideText'
|
|
import {
|
|
DEFAULT_DEPOSIT_GUIDE_TEXT,
|
|
getCustomerNameFromUser,
|
|
getDepositGuideVarsFromSettings,
|
|
getDepositSupportMessageTemplate,
|
|
getRenderedDepositGuideText,
|
|
getRenderedDepositSupportMessage,
|
|
renderDepositGuideText,
|
|
renderDepositSupportMessageText,
|
|
} from '@/utils/depositGuideText'
|
|
import { useSettingsStore } from '@/store/settingsStore'
|
|
|
|
export type SupportMessengerType =
|
|
| 'bale'
|
|
| 'telegram'
|
|
| 'whatsapp'
|
|
| 'rubika'
|
|
| 'eitaa'
|
|
| 'soroush'
|
|
| 'call'
|
|
| 'sms'
|
|
|
|
export interface SupportMessengerSettings {
|
|
messengers?: SupportMessengerEntry[]
|
|
messenger?: SupportMessengerType | string
|
|
messengerId?: string
|
|
messengerText?: string
|
|
depositSupportMessageText?: string
|
|
depositAccountHolder?: string
|
|
depositCardNumber?: string
|
|
depositSheba?: string
|
|
depositSmsSupportNumber?: string
|
|
}
|
|
|
|
export interface SupportMessengerEntry {
|
|
messenger: string
|
|
id: string
|
|
}
|
|
|
|
export interface ResolvedSupportMessengerEntry extends SupportMessengerEntry {
|
|
label: string
|
|
icon: string
|
|
accent: string
|
|
url: string
|
|
}
|
|
|
|
const VALID_SUPPORT_MESSENGERS = new Set([
|
|
'bale',
|
|
'telegram',
|
|
'whatsapp',
|
|
'rubika',
|
|
'eitaa',
|
|
'soroush',
|
|
'call',
|
|
'sms',
|
|
])
|
|
|
|
const PHONE_NATIVE_MESSENGERS = new Set(['call', 'sms'])
|
|
|
|
export const supportMessengerSettings = ref<SupportMessengerSettings>({})
|
|
export const supportMessengerPickerOpen = ref(false)
|
|
/** لنگر منوی انتخاب پیامرسان — FAB یا دکمه داخل مودال شارژ */
|
|
export const supportMessengerPickerAnchor = ref<HTMLElement | null>(null)
|
|
|
|
export function parseSupportMessengersJson(raw?: string | null): SupportMessengerEntry[] {
|
|
const value = (raw ?? '').trim()
|
|
if (!value) return []
|
|
|
|
try {
|
|
const decoded = JSON.parse(value)
|
|
if (!Array.isArray(decoded)) return []
|
|
|
|
return decoded
|
|
.map((row) => ({
|
|
messenger: String(row?.messenger ?? '').trim().toLowerCase(),
|
|
id: String(row?.id ?? '').trim(),
|
|
}))
|
|
.filter((row) => VALID_SUPPORT_MESSENGERS.has(row.messenger) && row.id !== '')
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export function resolveSupportMessengersFromSettings(
|
|
settings: Array<{ key?: string; value?: unknown }>,
|
|
): SupportMessengerEntry[] {
|
|
// اگر کلید جدید در تنظیمات هست (حتی خالی/null)، فقط همان لیست معتبر است — legacy بله را برنگردان
|
|
const hasMessengersKey = settings.some((row) => row?.key === 'support_messengers')
|
|
if (hasMessengersKey) {
|
|
return parseSupportMessengersJson(getSupportSettingValue(settings, 'support_messengers'))
|
|
}
|
|
|
|
const legacyMessenger = getSupportSettingValue(settings, 'support_messenger').toLowerCase()
|
|
const legacyId = getSupportSettingValue(settings, 'support_messenger_id')
|
|
if (!legacyId) return []
|
|
|
|
const messenger = legacyMessenger || 'bale'
|
|
if (!VALID_SUPPORT_MESSENGERS.has(messenger)) return []
|
|
|
|
return [{ messenger, id: legacyId }]
|
|
}
|
|
|
|
/** فقط موارد موجود در `support_messengers` — تماس/پیامک خودکار اضافه نمیشوند */
|
|
export function resolveSupportMessengerEntries(
|
|
settings?: SupportMessengerSettings,
|
|
messageText?: string,
|
|
): ResolvedSupportMessengerEntry[] {
|
|
const resolved = settings ?? supportMessengerSettings.value
|
|
const text = (messageText ?? '').trim()
|
|
// فقط لیست resolveشده؛ قبل از load خالی است تا legacy دوباره دکمه نسازد
|
|
const source = Array.isArray(resolved.messengers) ? resolved.messengers : []
|
|
|
|
return source
|
|
.map((entry) => {
|
|
const meta = resolveSupportMessengerMeta(entry.messenger)
|
|
const url = buildSupportMessengerUrl(entry.messenger, entry.id, text)
|
|
if (!url) return null
|
|
return {
|
|
...entry,
|
|
...meta,
|
|
url,
|
|
}
|
|
})
|
|
.filter((entry): entry is ResolvedSupportMessengerEntry => entry !== null)
|
|
}
|
|
|
|
/** @deprecated از depositGuideText استفاده کنید */
|
|
export const DEFAULT_SUPPORT_MESSENGER_TEXT = DEFAULT_DEPOSIT_GUIDE_TEXT
|
|
|
|
export interface SupportMessengerMeta {
|
|
label: string
|
|
icon: string
|
|
accent: string
|
|
}
|
|
|
|
export const SUPPORT_MESSENGER_META: Record<string, SupportMessengerMeta> = {
|
|
bale: { label: 'بله', icon: 'vuesax-linear:messages-2', accent: '#16a34a' },
|
|
telegram: { label: 'تلگرام', icon: 'vuesax-linear:send-2', accent: '#0ea5e9' },
|
|
whatsapp: { label: 'واتساپ', icon: 'vuesax-linear:whatsapp', accent: '#22c55e' },
|
|
rubika: { label: 'روبیکا', icon: 'vuesax-linear:messages-2', accent: '#7c3aed' },
|
|
eitaa: { label: 'ایتا', icon: 'vuesax-linear:messages-2', accent: '#f59e0b' },
|
|
soroush: { label: 'سروش', icon: 'vuesax-linear:messages-2', accent: '#2563eb' },
|
|
call: { label: 'تماس', icon: 'vuesax-linear:call-calling', accent: '#10b981' },
|
|
sms: { label: 'پیامک', icon: 'vuesax-linear:sms', accent: '#3b82f6' },
|
|
}
|
|
|
|
export const DEFAULT_SUPPORT_MESSENGER_META: SupportMessengerMeta = {
|
|
label: 'پشتیبانی',
|
|
icon: 'vuesax-linear:headphone',
|
|
accent: '#334155',
|
|
}
|
|
|
|
export function resolveSupportMessengerMeta(messenger?: string): SupportMessengerMeta {
|
|
const type = (messenger ?? '').trim().toLowerCase()
|
|
return SUPPORT_MESSENGER_META[type] ?? DEFAULT_SUPPORT_MESSENGER_META
|
|
}
|
|
|
|
export function resolveSupportMessengerLink(
|
|
messenger?: string,
|
|
messengerId?: string,
|
|
fallbackLink = '',
|
|
messageText?: string,
|
|
) {
|
|
const fromSettings = buildSupportMessengerUrl(messenger, messengerId, messageText)
|
|
return fromSettings || fallbackLink
|
|
}
|
|
|
|
export function getDepositGuideVarsFromSupportSettings(
|
|
settings: SupportMessengerSettings,
|
|
): DepositGuideVars {
|
|
return {
|
|
accountHolder: settings.depositAccountHolder,
|
|
cardNumber: settings.depositCardNumber,
|
|
sheba: settings.depositSheba,
|
|
customerName: getCustomerNameFromUser(),
|
|
}
|
|
}
|
|
|
|
export function resolveDepositGuideText(settings?: SupportMessengerSettings): string {
|
|
try {
|
|
const fromStore = getRenderedDepositGuideText()
|
|
if (fromStore.trim()) return fromStore
|
|
} catch {
|
|
// settings store may not be ready
|
|
}
|
|
|
|
const resolved = settings ?? supportMessengerSettings.value
|
|
const template = resolved.messengerText?.trim() || DEFAULT_DEPOSIT_GUIDE_TEXT
|
|
const vars = resolved.depositAccountHolder
|
|
? getDepositGuideVarsFromSupportSettings(resolved)
|
|
: getDepositGuideVarsFromSettings()
|
|
|
|
return renderDepositGuideText(template, vars)
|
|
}
|
|
|
|
export function resolveDepositSupportMessage(settings?: SupportMessengerSettings): string {
|
|
try {
|
|
const fromStore = getRenderedDepositSupportMessage()
|
|
if (fromStore.trim()) return fromStore
|
|
} catch {
|
|
// settings store may not be ready
|
|
}
|
|
|
|
const resolved = settings ?? supportMessengerSettings.value
|
|
const rawTemplate = resolved.depositSupportMessageText?.trim() || ''
|
|
const template = rawTemplate || getDepositSupportMessageTemplate()
|
|
const vars = resolved.depositAccountHolder
|
|
? getDepositGuideVarsFromSupportSettings(resolved)
|
|
: getDepositGuideVarsFromSettings()
|
|
|
|
return renderDepositSupportMessageText(template, vars)
|
|
}
|
|
|
|
/** پیامرسانهایی که پارامتر text در URL دارند */
|
|
export function messengerSupportsUrlText(messenger?: string): boolean {
|
|
const type = (messenger ?? '').trim().toLowerCase()
|
|
return type === 'telegram' || type === 'whatsapp' || type === 'sms'
|
|
}
|
|
|
|
/** تماس/پیام بومی — نیازی به کپی متن در کلیپبورد نیست */
|
|
export function isPhoneNativeSupportMessenger(messenger?: string): boolean {
|
|
const type = (messenger ?? '').trim().toLowerCase()
|
|
return PHONE_NATIVE_MESSENGERS.has(type)
|
|
}
|
|
|
|
export function messengerNeedsClipboardCopy(messenger?: string): boolean {
|
|
if (isPhoneNativeSupportMessenger(messenger) || messengerSupportsUrlText(messenger)) {
|
|
return false
|
|
}
|
|
return Boolean((messenger ?? '').trim())
|
|
}
|
|
|
|
export async function openSupportMessengerWithMessage(
|
|
messenger?: string,
|
|
messengerId?: string,
|
|
messageText?: string,
|
|
fallbackLink = '',
|
|
): Promise<boolean> {
|
|
const text = (messageText ?? '').trim()
|
|
const url =
|
|
buildSupportMessengerUrl(messenger, messengerId, text) ||
|
|
(fallbackLink.trim() || '')
|
|
|
|
return openSupportMessengerUrl(url, messenger, text)
|
|
}
|
|
|
|
export async function openSupportMessengerEntry(
|
|
entry: SupportMessengerEntry | ResolvedSupportMessengerEntry,
|
|
messageText?: string,
|
|
fallbackLink = '',
|
|
): Promise<boolean> {
|
|
const text = (messageText ?? '').trim()
|
|
// همیشه URL را با متن فعلی بساز — url کششدهٔ entry ممکن است text قدیمی داشته باشد
|
|
const url =
|
|
buildSupportMessengerUrl(entry.messenger, entry.id, text) ||
|
|
('url' in entry && !text ? entry.url : '') ||
|
|
fallbackLink.trim()
|
|
|
|
return openSupportMessengerUrl(url, entry.messenger, text)
|
|
}
|
|
|
|
async function openSupportMessengerUrl(
|
|
url: string,
|
|
messenger?: string,
|
|
messageText?: string,
|
|
): Promise<boolean> {
|
|
if (!url) return false
|
|
|
|
if (messageText && messengerNeedsClipboardCopy(messenger) && navigator.clipboard?.writeText) {
|
|
try {
|
|
await navigator.clipboard.writeText(messageText)
|
|
} catch {
|
|
// clipboard optional
|
|
}
|
|
}
|
|
|
|
if (url.startsWith('tel:') || url.startsWith('sms:')) {
|
|
window.location.href = url
|
|
return true
|
|
}
|
|
|
|
window.open(url, '_blank', 'noopener,noreferrer')
|
|
return true
|
|
}
|
|
|
|
function getSupportSettingValue(
|
|
settings: Array<{ key?: string; is_active?: boolean | number; value?: unknown }>,
|
|
key: string
|
|
) {
|
|
const setting = settings.find((item) => item?.key === key)
|
|
if (!setting) return ''
|
|
|
|
const value = setting.value
|
|
if (value === undefined || value === null) return ''
|
|
|
|
return String(value).trim()
|
|
}
|
|
|
|
export function buildSupportMessengerUrl(
|
|
messenger?: string,
|
|
messengerId?: string,
|
|
messageText?: string,
|
|
) {
|
|
const id = (messengerId ?? '').trim()
|
|
if (!id) return ''
|
|
|
|
if (/^https?:\/\//i.test(id)) return id
|
|
|
|
const type = (messenger ?? '').trim().toLowerCase()
|
|
const username = id.replace(/^@/, '')
|
|
const text = (messageText ?? '').trim()
|
|
const textQuery = text ? `?text=${encodeURIComponent(text)}` : ''
|
|
|
|
if (!type) return ''
|
|
|
|
switch (type) {
|
|
case 'bale':
|
|
if (id.includes('ble.ir')) {
|
|
return id.startsWith('http') ? id : `https://${id.replace(/^\/\//, '')}`
|
|
}
|
|
return `https://ble.ir/${username}`
|
|
case 'telegram':
|
|
return `https://t.me/${username}${textQuery}`
|
|
case 'whatsapp':
|
|
return `https://wa.me/${username.replace(/\D/g, '')}${textQuery}`
|
|
case 'rubika':
|
|
return `https://rubika.ir/${username}`
|
|
case 'eitaa':
|
|
return `https://eitaa.com/${username}`
|
|
case 'soroush':
|
|
return `https://splus.ir/splus/${username}`
|
|
case 'call':
|
|
return buildSupportPhoneUrl(id)
|
|
case 'sms':
|
|
return buildDepositSmsUrl(id, text)
|
|
default:
|
|
return ''
|
|
}
|
|
}
|
|
|
|
export function buildDepositSmsUrl(phoneNumber?: string, messageText?: string) {
|
|
const digits = (phoneNumber ?? '').replace(/\D/g, '')
|
|
if (!digits) return ''
|
|
|
|
const body = (messageText ?? '').trim()
|
|
// بدون پیشنویس مگر صریحاً متن داده شود (مودال واریز)
|
|
if (!body) return `sms:${digits}`
|
|
return `sms:${digits}?body=${encodeURIComponent(body)}`
|
|
}
|
|
|
|
export function buildSupportPhoneUrl(phoneNumber?: string) {
|
|
const digits = (phoneNumber ?? '').replace(/\D/g, '')
|
|
if (!digits) return ''
|
|
return `tel:${digits}`
|
|
}
|
|
|
|
export async function loadSupportMessengerFromSettings(forceSettingsRefresh = false) {
|
|
try {
|
|
const settingsStore = useSettingsStore()
|
|
if (forceSettingsRefresh) {
|
|
await settingsStore.fetchSettings(true)
|
|
} else if (!settingsStore.loaded) {
|
|
await settingsStore.fetchSettings()
|
|
}
|
|
|
|
const settings = await getSettings()
|
|
if (!settings || !Array.isArray(settings)) return
|
|
|
|
settingsStore.mergeSettings(settings)
|
|
|
|
supportMessengerSettings.value = {
|
|
messengers: resolveSupportMessengersFromSettings(settings),
|
|
messenger: getSupportSettingValue(settings, 'support_messenger'),
|
|
messengerId: getSupportSettingValue(settings, 'support_messenger_id'),
|
|
messengerText: getSupportSettingValue(settings, 'support_messenger_text'),
|
|
depositSupportMessageText: getSupportSettingValue(settings, 'deposit_support_message_text'),
|
|
depositAccountHolder: getSupportSettingValue(settings, 'deposit_account_holder'),
|
|
depositCardNumber: getSupportSettingValue(settings, 'deposit_card_number'),
|
|
depositSheba: getSupportSettingValue(settings, 'deposit_sheba'),
|
|
depositSmsSupportNumber: getSupportSettingValue(settings, 'deposit_sms_support_number'),
|
|
}
|
|
} catch (e) {
|
|
console.log(e)
|
|
}
|
|
}
|