Formbricks (#157)

add quest feedback and bug report form using Formbricks
This commit is contained in:
Justin Lin authored and GitHub committed 2024-08-02 22:07:36 +08:00
commit 9b4f50ebbd
11 files changed
+2642 -1293

No files matched your search

+1
View File
@@ -10,6 +10,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@formbricks/js": "^2.1.1",
"@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0",
+18
View File
@@ -9,9 +9,11 @@ import CompleteAnimation from '../../img/complete-animation.json'
const showModal = ref(true) // Controls whether the modal is visible or not const showModal = ref(true) // Controls whether the modal is visible or not
const router = useRouter() const router = useRouter()
const toast = useToast() const toast = useToast()
const closeModal = () => { const closeModal = () => {
showModal.value = false showModal.value = false
} }
const defineComponent = () => ({ const defineComponent = () => ({
components: { components: {
Vue3Lottie, Vue3Lottie,
@@ -22,6 +24,7 @@ const defineComponent = () => ({
} }
}, },
}) })
const backtoMap = async () => { const backtoMap = async () => {
try { try {
router.push({ name: 'map' }) router.push({ name: 'map' })
@@ -67,6 +70,21 @@ setTimeout(() => {
>回到地圖</a >回到地圖</a
> >
</div> </div>
<div style="position: relative; height: 100vh; overflow: auto">
<iframe
src="https://app.formbricks.com/s/clyffdm0c0be2wh6335clle0l"
frameborder="0"
style="
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border: 0;
"
>
</iframe>
</div>
</div> </div>
</template> </template>
+24 -16
View File
@@ -2,7 +2,7 @@
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import useSession from '../../store/session' import useSession from '../../store/session'
import { surveyUrl } from '../../config' import { openBugReport, openQuestSurvey } from '../../utils/formbricks'
const router = useRouter() const router = useRouter()
const sessionStore = useSession() const sessionStore = useSession()
@@ -21,31 +21,39 @@ const continuePlay = async () => {
</script> </script>
<template> <template>
<section <section class="mt-2 flex flex-row gap-3 justify-center">
class="h-fit w-fit px-1 py-1 grid grid-cols-4 gap-2 place-content-between" <button
> id="survey"
<button id="survey" class="bg-bg-secondary rounded-lg p-2"> class="bg-bg-secondary rounded-lg p-2"
<a title="Survey" :href="surveyUrl" target="_blank" class="h-5 w-5"> @click="openQuestSurvey"
<font-awesome-icon >
:icon="['fas', 'file-invoice']" <font-awesome-icon
class="text-text-primary px-1" :icon="['fas', 'file-invoice']"
/> class="text-text-primary px-1"
<p class="text-text-primary inline ml-1 md:flex">問卷</p> />
</a> <p class="text-text-primary inline md:flex">問卷</p>
</button> </button>
<button disabled id="solution" class="bg-bg-disabled rounded-lg p-2"> <button
title="Bug Report"
@click="openBugReport"
class="bg-bg-secondary rounded-lg p-2"
>
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
<p class="text-text-primary inline ml-1 md:flex">回報</p>
</button>
<!-- <button disabled id="solution" class="bg-bg-disabled rounded-lg p-2">
<font-awesome-icon <font-awesome-icon
:icon="['far', 'circle-question']" :icon="['far', 'circle-question']"
class="text-text-disabled px-1" class="text-text-disabled px-1"
/> />
<p class="text-text-disabled inline ml-1 md:flex">解答</p> <p class="text-text-disabled inline ml-1 md:flex">解答</p>
</button> </button> -->
<button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset"> <button id="reset" class="bg-bg-secondary rounded-lg p-2" @click="reset">
<font-awesome-icon <font-awesome-icon
:icon="['fas', 'arrow-rotate-left']" :icon="['fas', 'arrow-rotate-left']"
class="text-text-primary px-1" class="text-text-primary px-1"
/> />
<p class="text-text-primary inline ml-1 hidden md:flex">重來</p> <p class="text-text-primary inline ml-1 md:flex">重來</p>
</button> </button>
<button <button
id="continue" id="continue"
@@ -56,7 +64,7 @@ const continuePlay = async () => {
:icon="['far', 'circle-right']" :icon="['far', 'circle-right']"
class="text-text-primary px-1" class="text-text-primary px-1"
/> />
<p class="text-text-primary inline ml-1 hidden md:flex">繼續</p> <p class="text-text-primary inline ml-1 md:flex">繼續</p>
</button> </button>
</section> </section>
</template> </template>
@@ -3,7 +3,8 @@ import { onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useToast } from 'vue-toastification' import { useToast } from 'vue-toastification'
import { logout } from '../../utils/auth' import { logout } from '../../utils/auth'
import { bugReportUrl, surveyUrl } from '../../config' import { surveyUrl } from '../../config'
import { openBugReport } from '../../utils/formbricks'
import useSession from '../../store/session' import useSession from '../../store/session'
import useUserProfile from '../../store/userProfile' import useUserProfile from '../../store/userProfile'
@@ -84,15 +85,14 @@ onMounted(async () => {
<font-awesome-icon :icon="['fas', 'map']" class="text-text-primary" /> <font-awesome-icon :icon="['fas', 'map']" class="text-text-primary" />
<span class="text-text sm:hidden px-2">Map</span> <span class="text-text sm:hidden px-2">Map</span>
</RouterLink> </RouterLink>
<a <button
title="Bug Report" title="Bug Report"
:href="bugReportUrl" @click="openBugReport"
target="_blank"
class="md:h-5 md:w-5 px-1.5 w-auto" class="md:h-5 md:w-5 px-1.5 w-auto"
> >
<font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" /> <font-awesome-icon :icon="['fas', 'bug']" class="text-text-primary" />
<span class="text-text sm:hidden px-2">Bug Report</span> <span class="text-text sm:hidden px-2">Bug Report</span>
</a> </button>
<button <button
title="Sign Out" title="Sign Out"
@click="handleLogout" @click="handleLogout"
+7 -1
View File
@@ -10,9 +10,9 @@ import Toast, { POSITION } from 'vue-toastification'
/* import Vue Lottie */ /* import Vue Lottie */
import Vue3Lottie from 'vue3-lottie' import Vue3Lottie from 'vue3-lottie'
import 'vue-toastification/dist/index.css' import 'vue-toastification/dist/index.css'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import formbricks from './utils/formbricks'
const app = createApp(App) const app = createApp(App)
const pinia = createPinia() const pinia = createPinia()
@@ -22,6 +22,12 @@ app.component('font-awesome-icon', FontAwesomeIcon)
app.use(pinia) app.use(pinia)
app.use(router) app.use(router)
router.afterEach(() => {
if (typeof formbricks !== 'undefined') {
formbricks.registerRouteChange()
}
})
const options = { const options = {
position: POSITION.TOP_RIGHT, position: POSITION.TOP_RIGHT,
} }
+7
View File
@@ -61,8 +61,15 @@ const useSession = defineStore('session', {
if (response.tasks) { if (response.tasks) {
this.session.tasks = response.tasks this.session.tasks = response.tasks
} }
if (
response.status === 'finished' &&
this.session.status !== 'finished'
) {
this.finish()
}
this.session.status = response.status this.session.status = response.status
}, },
finish() {},
reset() { reset() {
socket.reset() socket.reset()
term.reset() term.reset()
+40
View File
@@ -0,0 +1,40 @@
import formbricks from '@formbricks/js/website'
import useSession from '../store/session'
import useUserProfile from '../store/userProfile'
if (typeof window !== 'undefined') {
formbricks.init({
environmentId: 'clzcdqqn0000kadvtqykdo0pc',
apiHost: 'https://app.formbricks.com',
})
}
export function openQuestSurvey() {
const sessionStore = useSession()
const userStore = useUserProfile()
formbricks.track('quest_completed', {
hiddenFields: {
quest: sessionStore.questId,
session: sessionStore.session?._id,
username: userStore.username,
email: userStore.email,
},
})
}
export function openBugReport() {
const sessionStore = useSession()
const userStore = useUserProfile()
formbricks.track('bug_report', {
hiddenFields: {
quest: sessionStore.questId,
session: sessionStore.session?._id,
username: userStore.username,
email: userStore.email,
},
})
}
export default formbricks
+11 -1
View File
@@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, onMounted } from 'vue' import { computed, onMounted, watch } from 'vue'
import useSession from '../store/session' import useSession from '../store/session'
import useUserProfile from '../store/userProfile'
import HeaderPart from '../components/header/HeaderPart.vue' import HeaderPart from '../components/header/HeaderPart.vue'
import GameHeaderComponents from '../components/header/GameHeaderComponents.vue' import GameHeaderComponents from '../components/header/GameHeaderComponents.vue'
import DescriptionPart from '../components/game/DescriptionPart.vue' import DescriptionPart from '../components/game/DescriptionPart.vue'
@@ -10,13 +11,22 @@ import VisualizationPart from '../components/game/VisualizationPart.vue'
import ControlPalette from '../components/game/ControlPalette.vue' import ControlPalette from '../components/game/ControlPalette.vue'
import CompleteModal from '../components/game/CompleteModal.vue' import CompleteModal from '../components/game/CompleteModal.vue'
import StartButton from '../components/game/StartButton.vue' import StartButton from '../components/game/StartButton.vue'
import { openQuestSurvey } from '../utils/formbricks'
const sessionStore = useSession() const sessionStore = useSession()
const userStore = useUserProfile()
const completed = computed(() => { const completed = computed(() => {
return sessionStore.session?.status === 'finished' return sessionStore.session?.status === 'finished'
}) })
watch(completed, (newValue, oldValue) => {
if (newValue && !oldValue) {
console.log('finish', userStore)
openQuestSurvey()
}
})
const props = defineProps({ const props = defineProps({
questId: { questId: {
type: String, type: String,
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
}, },
}, },
backgroundImage: { backgroundImage: {
catelogbg: "url('./catelogbg.svg')", catelogbg: "url('/catelogbg.svg')",
}, },
}, },
}, },
+1
View File
@@ -54,6 +54,7 @@
"typescript": "^5.3.3" "typescript": "^5.3.3"
}, },
"dependencies": { "dependencies": {
"latest": "^0.2.0",
"typescript-eslint": "^7.4.0" "typescript-eslint": "^7.4.0"
} }
} }
+2527 -1269
View File
File diff suppressed because it is too large. Load diff