nuxt ui notifications (#390)
* nuxt ui notifications * use crispInit function --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
parent
9f7cdd09fd
commit
49e6382bbb
|
|
@ -47,7 +47,7 @@
|
||||||
</NuxtLayout>
|
</NuxtLayout>
|
||||||
<ToolsStopImpersonation />
|
<ToolsStopImpersonation />
|
||||||
|
|
||||||
<Notifications />
|
<NotificationsWrapper />
|
||||||
<feature-base />
|
<feature-base />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -125,6 +125,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
useCrisp().onCrispInit()
|
||||||
useCrisp().showChat()
|
useCrisp().showChat()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<UNotifications
|
||||||
|
:ui="{
|
||||||
|
strategy: 'override',
|
||||||
|
position,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #title="{ title }">
|
||||||
|
<span v-html="title" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #description="{ description }">
|
||||||
|
<span v-html="description" />
|
||||||
|
</template>
|
||||||
|
</UNotifications>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// fixed flex flex-col justify-end z-[55] bottom-0 end-0 w-full sm:w-96
|
||||||
|
// fixed flex flex-col justify-end z-[55] top-auto bottom-0 w-full sm:w-96
|
||||||
|
const appStore = useAppStore()
|
||||||
|
const crispChatOpened = computed(() => appStore.crisp.chatOpened)
|
||||||
|
const crispHidden = computed(() => appStore.crisp.hidden)
|
||||||
|
|
||||||
|
const position = computed(() => {
|
||||||
|
if (crispHidden.value) {
|
||||||
|
return 'end-0 bottom-0'
|
||||||
|
} else {
|
||||||
|
if (crispChatOpened.value) {
|
||||||
|
return 'bottom-0'
|
||||||
|
}
|
||||||
|
return 'end-0 bottom-20'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -1,50 +1,68 @@
|
||||||
const { notify } = useNotification()
|
export function useAlert () {
|
||||||
|
|
||||||
export const useAlert = () => {
|
function success (message, autoClose = 10000, options = {}) {
|
||||||
function success(message, autoClose = 10000) {
|
return useToast().add({
|
||||||
notify({
|
icon: 'i-heroicons-check-circle',
|
||||||
title: "Success",
|
title: options.title ?? 'Success',
|
||||||
text: message,
|
description: message,
|
||||||
type: "success",
|
color: 'green',
|
||||||
duration: autoClose,
|
timeout: autoClose,
|
||||||
|
...options
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(message, autoClose = 10000) {
|
function error (message, autoClose = 10000, options = {}) {
|
||||||
notify({
|
return useToast().add({
|
||||||
title: "Error",
|
icon: 'i-heroicons-exclamation-circle',
|
||||||
text: message,
|
title: options.title ?? 'Error',
|
||||||
type: "error",
|
description: message,
|
||||||
duration: autoClose,
|
color: 'red',
|
||||||
|
timeout: autoClose,
|
||||||
|
...options
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function warning(message, autoClose = 10000) {
|
function warning (message, autoClose = 10000, options = {}) {
|
||||||
notify({
|
return useToast().add({
|
||||||
title: "Warning",
|
icon: 'i-heroicons-exclamation-triangle',
|
||||||
text: message,
|
title: options.title ?? 'Warning',
|
||||||
type: "warning",
|
description: message,
|
||||||
duration: autoClose,
|
color: 'yellow',
|
||||||
|
timeout: autoClose,
|
||||||
|
...options
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirm(message, success, failure = () => {}, autoClose = 10000) {
|
function confirm (
|
||||||
notify({
|
message,
|
||||||
title: "Confirm",
|
onSuccess,
|
||||||
text: message,
|
onFailure = null,
|
||||||
type: "confirm",
|
autoClose = 10000,
|
||||||
duration: autoClose,
|
options = {}
|
||||||
data: {
|
) {
|
||||||
success,
|
return useToast().add({
|
||||||
failure,
|
icon: 'i-heroicons-question-mark-circle',
|
||||||
},
|
title: options.title ?? 'Are you sure?',
|
||||||
|
description: message,
|
||||||
|
color: 'blue',
|
||||||
|
timeout: autoClose,
|
||||||
|
actions: [
|
||||||
|
{ label: options.successLabel ?? 'Yes', click: onSuccess },
|
||||||
|
...(onFailure ? [{ label: options.failureLabel ?? 'No', click: onFailure }] : [])
|
||||||
|
],
|
||||||
|
...options
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function remove (id) {
|
||||||
|
useToast().remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success,
|
success,
|
||||||
error,
|
error,
|
||||||
warning,
|
warning,
|
||||||
confirm,
|
confirm,
|
||||||
|
remove
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,106 @@
|
||||||
export const useCrisp = () => {
|
export function useCrisp () {
|
||||||
let crisp = import.meta.client ? window.Crisp : null
|
const crisp = import.meta.client ? window.Crisp : null
|
||||||
|
|
||||||
function openChat() {
|
function onCrispInit () {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
|
crisp.chat.onChatOpened(() => {
|
||||||
|
useAppStore().crisp.chatOpened = true
|
||||||
|
})
|
||||||
|
crisp.chat.onChatClosed(() => {
|
||||||
|
useAppStore().crisp.chatOpened = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function openChat () {
|
||||||
|
if (!crisp)
|
||||||
|
return
|
||||||
showChat()
|
showChat()
|
||||||
crisp.chat.open()
|
crisp.chat.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
function showChat() {
|
function showChat () {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.chat.show()
|
crisp.chat.show()
|
||||||
|
useAppStore().crisp.hidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideChat() {
|
function hideChat () {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.chat.hide()
|
crisp.chat.hide()
|
||||||
|
useAppStore().crisp.hidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeChat() {
|
function closeChat () {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.chat.close()
|
crisp.chat.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openAndShowChat(message = null) {
|
function openAndShowChat (message = null) {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
openChat()
|
openChat()
|
||||||
if (message) sendTextMessage(message)
|
if (message)
|
||||||
|
sendTextMessage(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openHelpdesk() {
|
function openHelpdesk () {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
openChat()
|
openChat()
|
||||||
crisp.chat.setHelpdeskView()
|
crisp.chat.setHelpdeskView()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openHelpdeskArticle(articleSlug, locale = "en") {
|
function openHelpdeskArticle (articleSlug, locale = 'en') {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.chat.openHelpdeskArticle(locale, articleSlug)
|
crisp.chat.openHelpdeskArticle(locale, articleSlug)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendTextMessage(message) {
|
function sendTextMessage (message) {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
crisp.message.send("text", message)
|
return
|
||||||
|
crisp.message.send('text', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUser(user) {
|
function setUser (user) {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.user.setEmail(user.email)
|
crisp.user.setEmail(user.email)
|
||||||
crisp.user.setNickname(user.name)
|
crisp.user.setNickname(user.name)
|
||||||
crisp.session.setData({
|
crisp.session.setData({
|
||||||
user_id: user.id,
|
'user_id': user.id,
|
||||||
"pro-subscription": user?.is_subscribed ?? false,
|
'pro-subscription': user?.is_subscribed ?? false,
|
||||||
"stripe-id": user?.stripe_id ?? "",
|
'stripe-id': user?.stripe_id ?? '',
|
||||||
subscription: user?.has_enterprise_subscription ? "enterprise" : "pro",
|
'subscription': user?.has_enterprise_subscription ? 'enterprise' : 'pro'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (user?.is_subscribed ?? false) {
|
if (user?.is_subscribed ?? false) {
|
||||||
setSegments([
|
setSegments([
|
||||||
"subscribed",
|
'subscribed',
|
||||||
user?.has_enterprise_subscription ? "enterprise" : "pro",
|
user?.has_enterprise_subscription ? 'enterprise' : 'pro'
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pushEvent(event, data = {}) {
|
function pushEvent (event, data = {}) {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.session.pushEvent(event, data)
|
crisp.session.pushEvent(event, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSegments(segments, overwrite = false) {
|
function setSegments (segments, overwrite = false) {
|
||||||
if (!crisp) return
|
if (!crisp)
|
||||||
|
return
|
||||||
crisp.session.setSegments(segments, overwrite)
|
crisp.session.setSegments(segments, overwrite)
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crisp,
|
crisp,
|
||||||
|
onCrispInit,
|
||||||
openChat,
|
openChat,
|
||||||
showChat,
|
showChat,
|
||||||
hideChat,
|
hideChat,
|
||||||
|
|
@ -84,6 +110,7 @@ export const useCrisp = () => {
|
||||||
openHelpdeskArticle,
|
openHelpdeskArticle,
|
||||||
sendTextMessage,
|
sendTextMessage,
|
||||||
pushEvent,
|
pushEvent,
|
||||||
setUser,
|
setSegments,
|
||||||
|
setUser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ export default defineNuxtConfig({
|
||||||
'@pinia/nuxt',
|
'@pinia/nuxt',
|
||||||
'@vueuse/nuxt',
|
'@vueuse/nuxt',
|
||||||
'@vueuse/motion/nuxt',
|
'@vueuse/motion/nuxt',
|
||||||
'nuxt3-notifications',
|
|
||||||
'nuxt-simple-sitemap',
|
'nuxt-simple-sitemap',
|
||||||
'@nuxt/ui',
|
'@nuxt/ui',
|
||||||
...process.env.NUXT_PUBLIC_GOOGLE_ANALYTICS_CODE ? ['nuxt-gtag'] : [],
|
...process.env.NUXT_PUBLIC_GOOGLE_ANALYTICS_CODE ? ['nuxt-gtag'] : [],
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"js-sha256": "^0.10.0",
|
"js-sha256": "^0.10.0",
|
||||||
"libphonenumber-js": "^1.10.44",
|
"libphonenumber-js": "^1.10.44",
|
||||||
"nuxt3-notifications": "^1.1.9",
|
|
||||||
"object-to-formdata": "^4.5.1",
|
"object-to-formdata": "^4.5.1",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"prismjs": "^1.24.1",
|
"prismjs": "^1.24.1",
|
||||||
|
|
|
||||||
|
|
@ -74,14 +74,12 @@ watch(form, (form) => {
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (isDirty()) {
|
if (isDirty()) {
|
||||||
return useAlert().confirm(
|
if (window.confirm('Changes you made may not be saved. Are you sure want to leave?')) {
|
||||||
"Changes you made may not be saved. Are you sure want to leave?",
|
|
||||||
() => {
|
|
||||||
window.onbeforeunload = null
|
window.onbeforeunload = null
|
||||||
next()
|
next()
|
||||||
},
|
} else {
|
||||||
() => {},
|
next(false)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,13 @@ useOpnSeoMeta({
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeRouteLeave((to, from, next) => {
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
if (isDirty()) {
|
if (this.isDirty()) {
|
||||||
return useAlert().confirm(
|
if (window.confirm('Changes you made may not be saved. Are you sure want to leave?')) {
|
||||||
"Changes you made may not be saved. Are you sure want to leave?",
|
|
||||||
() => {
|
|
||||||
window.onbeforeunload = null
|
window.onbeforeunload = null
|
||||||
next()
|
next()
|
||||||
},
|
} else {
|
||||||
() => {},
|
next(false)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ export const useAppStore = defineStore("app", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
layout: "default",
|
layout: "default",
|
||||||
navbarHidden: false,
|
navbarHidden: false,
|
||||||
|
crisp: {
|
||||||
|
chatOpened: false,
|
||||||
|
hidden: false
|
||||||
|
},
|
||||||
|
|
||||||
// App Loader
|
// App Loader
|
||||||
loader: {
|
loader: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "opnform",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue