Files
enlight-lms/frontend/src/components/StreamEmbed.vue
joylessorchid bea5f78b6e feat: add StreamEmbed widget, move course progress to footer corner
- Add StreamEmbed component supporting Twitch and YouTube embeds
  with live badge, platform icon, aspect-ratio 16:9, external link
- Replace AudioPlayer on AdminHome with StreamEmbed (configure
  channel name in streamConfig ref)
- Move course progress % to bottom-right footer corner alongside
  price/cert badge; remove separate progress row above footer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 06:32:25 +03:00

114 lines
3.9 KiB
Vue

<template>
<div class="rounded-xl overflow-hidden border border-outline-gray-2 bg-surface-gray-9">
<!-- Stream header -->
<div class="flex items-center justify-between px-4 py-3 bg-surface-gray-9 border-b border-outline-gray-7">
<div class="flex items-center gap-2.5">
<!-- Live badge -->
<span
v-if="isLive"
class="flex items-center gap-1.5 px-2 py-0.5 rounded text-[11px] font-bold uppercase tracking-wide bg-red-600 text-white"
>
<span class="w-1.5 h-1.5 rounded-full bg-white animate-pulse" />
LIVE
</span>
<span
v-else
class="flex items-center gap-1.5 px-2 py-0.5 rounded text-[11px] font-semibold uppercase tracking-wide bg-surface-gray-7 text-ink-gray-4"
>
OFFLINE
</span>
<!-- Platform icon + channel -->
<div class="flex items-center gap-1.5">
<!-- Twitch icon -->
<svg v-if="platform === 'twitch'" class="w-4 h-4 text-purple-400" viewBox="0 0 24 24" fill="currentColor">
<path d="M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z"/>
</svg>
<!-- YouTube icon -->
<svg v-else-if="platform === 'youtube'" class="w-4 h-4 text-red-500" viewBox="0 0 24 24" fill="currentColor">
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
</svg>
<span class="text-sm font-semibold text-white">{{ channelDisplay }}</span>
</div>
</div>
<!-- Open in new tab -->
<a
:href="externalUrl"
target="_blank"
rel="noopener noreferrer"
class="flex items-center gap-1 text-xs text-ink-gray-4 hover:text-white transition-colors"
>
<ExternalLink class="w-3.5 h-3.5" />
<span class="hidden sm:inline">{{ __('Open') }}</span>
</a>
</div>
<!-- Embed iframe -->
<div class="relative w-full" style="aspect-ratio: 16/9">
<iframe
v-if="embedUrl"
:src="embedUrl"
class="absolute inset-0 w-full h-full"
frameborder="0"
allowfullscreen
allow="autoplay; fullscreen"
:title="channelDisplay"
/>
<!-- Placeholder when no channel configured -->
<div
v-else
class="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-surface-gray-9"
>
<Radio class="w-12 h-12 text-ink-gray-6 stroke-1" />
<p class="text-sm text-ink-gray-5">{{ __('Stream channel not configured') }}</p>
<p class="text-xs text-ink-gray-4">{{ __('Set channel prop to a Twitch or YouTube channel') }}</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { ExternalLink, Radio } from 'lucide-vue-next'
const props = withDefaults(defineProps<{
/** 'twitch' or 'youtube' */
platform?: 'twitch' | 'youtube'
/** Twitch: channel name. YouTube: video/stream ID or channel handle */
channel?: string
/** Show as live */
isLive?: boolean
}>(), {
platform: 'twitch',
channel: '',
isLive: true,
})
const channelDisplay = computed(() => props.channel || '—')
const embedUrl = computed(() => {
if (!props.channel) return ''
if (props.platform === 'twitch') {
const parent = window.location.hostname
return `https://player.twitch.tv/?channel=${props.channel}&parent=${parent}&autoplay=false`
}
if (props.platform === 'youtube') {
// Supports both video ID and live stream
return `https://www.youtube.com/embed/${props.channel}?autoplay=0&rel=0`
}
return ''
})
const externalUrl = computed(() => {
if (!props.channel) return '#'
if (props.platform === 'twitch') return `https://twitch.tv/${props.channel}`
if (props.platform === 'youtube') return `https://youtube.com/watch?v=${props.channel}`
return '#'
})
</script>