Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/basic-integration/vue/movies/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_POSTHOG_PROJECT_TOKEN=your_posthog_project_token
VITE_POSTHOG_HOST=https://us.i.posthog.com
1 change: 1 addition & 0 deletions apps/basic-integration/vue/movies/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ __screenshots__/

# Vite
*.timestamp-*-*.mjs
.env
130 changes: 130 additions & 0 deletions apps/basic-integration/vue/movies/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/basic-integration/vue/movies/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lru-cache": "^10.2.0",
"ohash": "^1.1.3",
"pinia": "^3.0.4",
"posthog-js": "^1.441.2",
"vue": "^3.5.27",
"vue-router": "^5.0.1"
},
Expand Down
7 changes: 7 additions & 0 deletions apps/basic-integration/vue/movies/src/components/NavBar.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import posthog from 'posthog-js'
import { useAuth } from '../composables/useAuth'

const route = useRoute()
const router = useRouter()
const { user, logout } = useAuth()
const isPostHogConfigured = Boolean(
import.meta.env.VITE_POSTHOG_PROJECT_TOKEN && import.meta.env.VITE_POSTHOG_HOST,
)

const handleLogout = async () => {
if (isPostHogConfigured) {
posthog.capture('user_logged_out')
}
await logout()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
<script setup lang="ts">
import posthog from 'posthog-js'
import type { Media, MediaType, QueryItem } from '../../types'
import { formatVote } from '../../composables/utils'

defineProps<{
const props = defineProps<{
type: MediaType
item: Media
query?: QueryItem
}>()
const isPostHogConfigured = Boolean(
import.meta.env.VITE_POSTHOG_PROJECT_TOKEN && import.meta.env.VITE_POSTHOG_HOST,
)

function trackMediaSelection() {
if (isPostHogConfigured) {
posthog.capture('media_selected', {
media_id: props.item.id,
media_type: props.item.media_type || props.type,
selection_surface: props.query ? 'carousel' : 'search_or_recommendations',
})
}
}
</script>

<template>
<router-link
:to="`/${item.media_type || type}/${item.id}`"
class="pb-2 block"
@click="trackMediaSelection"
>
<div
class="block bg-gray-400/10 p-1 aspect-[10/16] transition duration-400 hover:scale-105 z-10"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import posthog from 'posthog-js'
import type { Media } from '../../types'
import { formatTime, formatVote, getTrailer } from '../../composables/utils'

const props = defineProps<{
item: Media
}>()
const isPostHogConfigured = Boolean(
import.meta.env.VITE_POSTHOG_PROJECT_TOKEN && import.meta.env.VITE_POSTHOG_HOST,
)

const mounted = ref(false)
const showModal = ref(false)
Expand All @@ -18,6 +22,11 @@ onMounted(() => {
function playTrailer() {
if (trailerUrl.value) {
showModal.value = true
if (isPostHogConfigured) {
posthog.capture('trailer_started', {
media_id: props.item.id,
})
}
}
}

Expand Down
32 changes: 32 additions & 0 deletions apps/basic-integration/vue/movies/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,44 @@ import 'virtual:uno.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import posthog from 'posthog-js'

import App from './App.vue'
import router from './router'

const app = createApp(App)

const posthogProjectToken = import.meta.env.VITE_POSTHOG_PROJECT_TOKEN
const posthogHost = import.meta.env.VITE_POSTHOG_HOST
let isPostHogConfigured = false

if (!posthogProjectToken || !posthogHost) {
if (import.meta.env.DEV) {
const missingVariable = posthogProjectToken
? 'VITE_POSTHOG_HOST'
: 'VITE_POSTHOG_PROJECT_TOKEN'
throw new Error(
`${missingVariable} variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once ${missingVariable} is configured`,
)
}
} else {
posthog.init(posthogProjectToken, {
api_host: posthogHost,
defaults: '2026-08-30',
logs: {
serviceName: 'vue-movies-web',
environment: import.meta.env.MODE,
},
})
isPostHogConfigured = true
}

app.config.errorHandler = (error) => {
if (isPostHogConfigured) {
posthog.captureException(error)
}
}

app.use(createPinia())
app.use(router)

Expand Down
7 changes: 7 additions & 0 deletions apps/basic-integration/vue/movies/src/views/LoginView.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
<script setup lang="ts">
import { ref } from 'vue'
import posthog from 'posthog-js'
import { useAuth } from '../composables/useAuth'

const username = ref('')
const password = ref('')
const error = ref('')
const loading = ref(false)
const { login } = useAuth()
const isPostHogConfigured = Boolean(
import.meta.env.VITE_POSTHOG_PROJECT_TOKEN && import.meta.env.VITE_POSTHOG_HOST,
)

const handleLogin = async () => {
error.value = ''
loading.value = true

try {
await login(username.value, password.value)
if (isPostHogConfigured) {
posthog.capture('user_logged_in')
}
} catch (e: any) {
error.value = e.message || 'Login failed'
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import posthog from 'posthog-js'
import type { Media } from '../types'
import { getMedia, getRecommendations } from '../composables/useTMDB'
import { formatTime, formatVote, getTrailer } from '../composables/utils'
Expand All @@ -12,6 +13,9 @@ console.log('MediaDetailView component loaded')
const props = defineProps<{
type?: 'movie' | 'tv'
}>()
const isPostHogConfigured = Boolean(
import.meta.env.VITE_POSTHOG_PROJECT_TOKEN && import.meta.env.VITE_POSTHOG_HOST,
)

const route = useRoute()
const type = computed(() => {
Expand Down Expand Up @@ -100,8 +104,14 @@ watch(() => route.fullPath, () => {
}, { immediate: false })

function playTrailer() {
if (trailerUrl.value) {
if (trailerUrl.value && item.value) {
showModal.value = true
if (isPostHogConfigured) {
posthog.capture('trailer_started', {
media_id: item.value.id,
media_type: type.value,
})
}
}
}

Expand Down
Loading
Loading