Port modular features into boursecu and fix desktop login UX.
Add support chat, piggy funds, PWA banner, notification/SSE sync, and messenger legacy guards while keeping boursecu theme; also balance login panels and unblock login navigation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+120
-2
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
</AConfigProvider>
|
||||
<div
|
||||
v-if="showSupportButton && !showSplash"
|
||||
v-if="showSupportButton && !showSplash && route.name !== 'support'"
|
||||
id="support-button"
|
||||
ref="supportButtonEl"
|
||||
class="fixed z-[5100] cursor-grab select-none rounded-full"
|
||||
@@ -48,7 +48,7 @@
|
||||
@pointercancel="onSupportPointerCancel"
|
||||
>
|
||||
<div
|
||||
class="inline-flex items-center gap-2 rounded-full bg-white border border-solid border-gray-200 shadow-[0_6px_20px_rgba(15,23,42,0.14)] px-3.5 py-[9px] text-slate-800 text-[13px] font-600 leading-none transition-all duration-200"
|
||||
class="relative inline-flex items-center gap-2 rounded-full bg-white border border-solid border-gray-200 shadow-[0_6px_20px_rgba(15,23,42,0.14)] px-3.5 py-[9px] text-slate-800 text-[13px] font-600 leading-none transition-all duration-200"
|
||||
:class="{
|
||||
'shadow-[0_10px_28px_rgba(15,23,42,0.22)]': supportDragging,
|
||||
'scale-[0.94] shadow-[0_4px_14px_rgba(15,23,42,0.12)]': supportPickerOpen && !supportDragging,
|
||||
@@ -61,6 +61,12 @@
|
||||
class="shrink-0"
|
||||
/>
|
||||
<span class="whitespace-nowrap">پشتیبانی</span>
|
||||
<span
|
||||
v-if="supportChatUnreadCount > 0"
|
||||
class="support-fab-badge"
|
||||
>
|
||||
{{ supportChatUnreadCount > 99 ? '99+' : supportChatUnreadCount }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,6 +92,13 @@
|
||||
|
||||
<VersionUpdateModal />
|
||||
|
||||
<PwaInstallBanner
|
||||
v-if="!showSplash"
|
||||
:visible="bannerVisible && !guideOpen"
|
||||
@install="openGuide()"
|
||||
@dismiss="dismissBanner()"
|
||||
/>
|
||||
|
||||
<PwaInstallGuideModal
|
||||
v-model:open="guideOpen"
|
||||
:platform="platform"
|
||||
@@ -109,7 +122,20 @@ import SupportMessengerPicker from '@/components/SupportMessengerPicker.vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { recoverMobileViewportAfterAuth } from '@/layouts/composables/useMobileScrollReset'
|
||||
import { usePwaInstall } from '@/composables/usePwaInstall'
|
||||
import PwaInstallBanner from '@/components/pwa/PwaInstallBanner.vue'
|
||||
import PwaInstallGuideModal from '@/components/pwa/PwaInstallGuideModal.vue'
|
||||
import { useUserStore } from '@/store/userStore'
|
||||
import { useSettingsStore } from '@/store/settingsStore'
|
||||
import {
|
||||
connectChatIfExists,
|
||||
disconnectChat,
|
||||
isSettingFlagOn,
|
||||
unreadSupportCount,
|
||||
useChatService,
|
||||
} from '@/utils/chat'
|
||||
import { getToken } from '@/utils'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { showBrowserNotification } from '@/utils/browserNotification'
|
||||
|
||||
const VersionUpdateModal = defineAsyncComponent(
|
||||
() => import('@/modules/version/VersionUpdateModal.vue')
|
||||
@@ -130,8 +156,12 @@ const popVisible = ref(false)
|
||||
|
||||
const {
|
||||
guideOpen,
|
||||
bannerVisible,
|
||||
platform,
|
||||
openGuide,
|
||||
dismissBanner,
|
||||
completeGuide,
|
||||
showBanner,
|
||||
} = usePwaInstall()
|
||||
|
||||
const {
|
||||
@@ -139,6 +169,7 @@ const {
|
||||
supportPickerAnchor,
|
||||
supportMessengerEntries,
|
||||
messengerMeta,
|
||||
isChatEnabled,
|
||||
hasMessengerLink: showSupportButton,
|
||||
openMessengerEntry,
|
||||
openSupportMessenger,
|
||||
@@ -147,6 +178,10 @@ const {
|
||||
const supportButtonEl = ref<HTMLElement | null>(null)
|
||||
const pickerAnchorEl = computed(() => supportPickerAnchor.value ?? supportButtonEl.value)
|
||||
|
||||
const supportChatUnreadCount = computed(() =>
|
||||
isChatEnabled.value ? Number(unreadSupportCount.value) || 0 : 0,
|
||||
)
|
||||
|
||||
const supportIcon = computed(() => messengerMeta.value.icon)
|
||||
|
||||
const supportAccentColor = computed(() => {
|
||||
@@ -226,6 +261,7 @@ watch(
|
||||
void import('@/utils/safeAreaInsets').then(({ syncSafeAreaInsets }) => {
|
||||
syncSafeAreaInsets()
|
||||
})
|
||||
showBanner()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
@@ -241,6 +277,64 @@ watch(
|
||||
{ flush: 'post' },
|
||||
)
|
||||
|
||||
const userStore = useUserStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const { latestIncomingNotification } = useChatService()
|
||||
let lastConnectedChatSessionKey = ''
|
||||
let lastSupportToastAt = 0
|
||||
|
||||
watch(
|
||||
() => [
|
||||
userStore.userInfo?.chat_room_id,
|
||||
userStore.userInfo?.chat_user_id,
|
||||
settingsStore.getSettingByKey('chat_api_key'),
|
||||
settingsStore.getSettingByKey('show_to_users_chat'),
|
||||
settingsStore.getSettingByKey('has_chat'),
|
||||
getToken(),
|
||||
],
|
||||
async ([roomId, chatUserId, chatApiKey, chatAdminFlag, chatCustomerFlag, token]) => {
|
||||
const chatOn =
|
||||
isSettingFlagOn(chatAdminFlag) && isSettingFlagOn(chatCustomerFlag)
|
||||
if (!token || !chatOn || !chatApiKey || !roomId || !chatUserId) {
|
||||
lastConnectedChatSessionKey = ''
|
||||
disconnectChat()
|
||||
return
|
||||
}
|
||||
|
||||
const sessionKey = `${roomId}:${chatUserId}:${chatApiKey}`
|
||||
if (lastConnectedChatSessionKey === sessionKey) return
|
||||
|
||||
try {
|
||||
await connectChatIfExists()
|
||||
lastConnectedChatSessionKey = sessionKey
|
||||
} catch (error) {
|
||||
lastConnectedChatSessionKey = ''
|
||||
console.error('connectChatIfExists failed:', error)
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(latestIncomingNotification, (payload) => {
|
||||
if (!payload) return
|
||||
if (payload.at === lastSupportToastAt) return
|
||||
lastSupportToastAt = payload.at
|
||||
|
||||
if (typeof document === 'undefined' || document.visibilityState === 'visible') {
|
||||
message.info({
|
||||
content: `پیام جدید پشتیبانی: ${payload.body}`,
|
||||
duration: 3,
|
||||
})
|
||||
}
|
||||
|
||||
void showBrowserNotification({
|
||||
title: payload.title || 'پشتیبانی',
|
||||
body: payload.body || 'پیام جدید',
|
||||
tag: 'support-chat-message',
|
||||
url: '/support',
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
@@ -263,6 +357,7 @@ watch(
|
||||
/* بخشی از کارت کیفپول زیر نوار «افزودن کارت جدید» دیده میشود */
|
||||
--wallet-card-dock-overlap: 52px;
|
||||
--app-z-nav: 5000;
|
||||
--app-z-toast: 5050;
|
||||
--app-z-wallet-dock: 4490;
|
||||
--app-z-wallet-add-bar: 4510;
|
||||
--app-z-action-sheet: 5200;
|
||||
@@ -273,6 +368,11 @@ watch(
|
||||
--app-z-support-fab-open: 5615;
|
||||
}
|
||||
|
||||
/* نوار اعلان Ant — پیشفرض z-index:1010 زیر هدر fixed میرود */
|
||||
.ant-notification {
|
||||
z-index: var(--app-z-toast, 5050) !important;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
@@ -425,6 +525,24 @@ html.project-boursecu {
|
||||
/* جای draggable از fabStyle میآید */
|
||||
}
|
||||
|
||||
.support-fab-badge {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: -4px;
|
||||
z-index: 2;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
padding: 0 5px;
|
||||
border-radius: 999px;
|
||||
background: #dc2626;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 426px) and (max-width: 767.98px) {
|
||||
.desktopConfig {
|
||||
width: 426px;
|
||||
|
||||
Reference in New Issue
Block a user