Files
Lars Behrends 1ec6016b10 dont know ?
2025-11-03 23:34:36 +01:00

1195 lines
73 KiB
Twig

{% extends "layouts/app.twig" %}
{% block content %}
<!-- Modern Hero Section -->
<div class="relative min-h-[60vh] bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 overflow-hidden">
<!-- Background Image with Overlay -->
<div class="absolute inset-0 bg-cover bg-center bg-no-repeat opacity-20"
style="background-image: url('{{ platform_versions[0].banner_url ? "/images/playnite/" ~ platform_versions[0].banner_url : "/images/hero-bg.jpg" }}')"></div>
<div class="absolute inset-0 bg-gradient-to-r from-slate-900/90 via-slate-900/70 to-slate-900/90"></div>
<!-- Content -->
<div class="relative z-10 container mx-auto px-6 py-16">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8 items-center min-h-[50vh]">
<!-- Game Cover -->
<div class="lg:col-span-3 flex justify-center lg:justify-start">
<div id="gameCover" class="relative group">
{% if platform_versions[0].cover_image_url %}
<img src="/images/playnite/{{ platform_versions[0].cover_image_url }}"
class="w-48 h-72 object-cover rounded-xl shadow-2xl transform group-hover:scale-105 transition-transform duration-300"
alt="{{ main_game.title }}">
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
{% elseif main_game.image_url %}
<img src="/images/playnite/{{ main_game.image_url }}"
class="w-48 h-72 object-cover rounded-xl shadow-2xl transform group-hover:scale-105 transition-transform duration-300"
alt="{{ main_game.title }}">
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent rounded-xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
{% else %}
<div class="w-48 h-72 bg-slate-800 rounded-xl flex items-center justify-center shadow-2xl">
<svg class="w-16 h-16 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z"/>
</svg>
</div>
{% endif %}
</div>
</div>
<!-- Game Info -->
<div class="lg:col-span-6 text-white">
<!-- Breadcrumb -->
<nav class="mb-4">
<ol class="flex items-center space-x-2 text-sm">
<li><a href="{{ path_for('games.index') }}" class="text-slate-300 hover:text-white transition-colors">Games</a></li>
<li class="text-slate-500">/</li>
<li class="text-white font-medium">{{ main_game.title }}</li>
</ol>
</nav>
<!-- Title -->
<h1 class="text-4xl lg:text-5xl font-bold mb-4 bg-gradient-to-r from-white to-slate-200 bg-clip-text text-transparent">
{{ main_game.title }}
</h1>
<!-- Badges -->
<div class="flex flex-wrap gap-2 mb-6">
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-600 text-white">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2"/>
</svg>
{{ platform_versions|length }} Platform{{ platform_versions|length > 1 ? 's' : '' }}
</span>
{% if main_game.genre %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-slate-700 text-slate-200">
{{ main_game.genre }}
</span>
{% endif %}
{% set total_playtime = 0 %}
{% for platform in platform_versions %}
{% if platform.playtime_minutes %}
{% set total_playtime = total_playtime + platform.playtime_minutes %}
{% endif %}
{% endfor %}
{% set total_hours = total_playtime // 60 %}
{% if total_hours > 0 %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-600 text-white">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
{{ total_hours }}h Played
</span>
{% endif %}
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap gap-3 mb-8">
<button class="inline-flex items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors duration-200 shadow-lg">
<svg class="w-5 h-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 8a9 9 0 110-18 9 9 0 010 18z"/>
</svg>
Play
</button>
<button class="inline-flex items-center px-6 py-3 bg-slate-700 hover:bg-slate-600 text-white font-medium rounded-lg transition-colors duration-200 border border-slate-600">
<svg class="w-5 h-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
</svg>
Favorite
</button>
<button class="inline-flex items-center px-6 py-3 bg-slate-700 hover:bg-slate-600 text-white font-medium rounded-lg transition-colors duration-200 border border-slate-600">
<svg class="w-5 h-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"/>
</svg>
Share
</button>
</div>
<!-- Platform Tabs -->
<ul class="nav nav-tabs border-0 flex flex-wrap gap-2" id="platformTabs" role="tablist">
{% for version in platform_versions %}
{% set safePlatformId = version.platform|lower|replace({' ': '-', '(': '', ')': ''}) ~ '-' ~ version.source_id %}
{% set platform_icons = {
'windows': 'mdi:microsoft-windows',
'pc': 'mdi:desktop-classic',
'playstation': 'mdi:sony-playstation',
'xbox': 'mdi:microsoft-xbox',
'nintendo': 'mdi:nintendo-switch',
'switch': 'mdi:nintendo-switch',
'wii': 'mdi:nintendo-wii',
'gamecube': 'mdi:gamepad-square',
'n64': 'mdi:gamepad-square',
'snes': 'mdi:gamepad-square',
'nes': 'mdi:gamepad-square',
'android': 'mdi:android',
'ios': 'mdi:apple-ios',
'linux': 'mdi:linux',
'macos': 'mdi:apple',
'steam': 'mdi:steam',
'gog': 'mdi:gog',
'epic': 'mdi:epic-games',
'origin': 'mdi:origin',
'ubisoft': 'mdi:ubisoft',
'battlenet': 'mdi:battle-net',
'ea': 'mdi:ea',
'humble': 'mdi:humble-bundle',
'itch': 'mdi:itch-io',
'browser': 'mdi:web',
'default': 'mdi:gamepad-variant'
} %}
{% set platform_lower = version.platform|lower %}
{% set platform_icon = 'default' %}
{% for key, icon in platform_icons %}
{% if key in platform_lower %}
{% set platform_icon = icon %}
{% endif %}
{% endfor %}
<li class="nav-item" role="presentation">
<button
class="nav-link inline-flex items-center px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 {{ loop.first ? 'active bg-blue-600 text-white shadow-lg' : 'text-slate-300 hover:bg-slate-600/50 hover:text-white' }} platform-tab border-0 bg-transparent"
id="tab-{{ safePlatformId }}"
data-bs-toggle="tab"
data-bs-target="#content-{{ safePlatformId }}"
type="button"
role="tab"
data-platform="{{ version.platform }}"
data-source="{{ version.source_id }}"
data-last-played="{{ version.last_played_at ? version.last_played_at|date('M d, Y') : 'Never' }}"
data-playtime="{{ version.playtime_minutes|format_duration }}"
data-completion="{{ version.completion_percentage ?? 0 }}"
data-cover-image="{{ version.cover_image_url ? '/images/playnite/' ~ version.cover_image_url : (main_game.image_url ? '/images/playnite/' ~ main_game.image_url : '') }}"
data-banner-url="{{ version.hero_image_url ? '/images/playnite/' ~ version.hero_image_url : '' }}"
>
<iconify-icon icon="{{ platform_icon }}" width="18" height="18" class="mr-2"></iconify-icon>
<span>{{ version.platform }}</span>
{% if version.source_name %}
<span class="text-slate-400 ml-1">({{ version.source_name }})</span>
{% endif %}
</button>
</li>
{% endfor %}
</ul>
</div>
<!-- Stats Sidebar -->
<div class="lg:col-span-3">
<div class="bg-slate-800/50 backdrop-blur-sm rounded-xl p-6 border border-slate-700/50">
<h3 class="text-xl font-semibold text-white mb-4">Game Stats</h3>
<div class="space-y-4">
<div class="flex justify-between items-center">
<span class="text-slate-400 text-sm">Last Played</span>
<span class="text-white font-medium stats-last-played">{{ platform_versions[0].last_played_at ? platform_versions[0].last_played_at|date('M d, Y') : 'Never' }}</span>
</div>
<div class="flex justify-between items-center">
<span class="text-slate-400 text-sm">Playtime</span>
{% set total_playtime_minutes = 0 %}
{% for platform in platform_versions %}
{% if platform.playtime_minutes %}
{% set total_playtime_minutes = total_playtime_minutes + platform.playtime_minutes %}
{% endif %}
{% endfor %}
<span class="text-white font-medium stats-playtime">{{ total_playtime_minutes|format_duration }}</span>
</div>
<div class="stats-completion {{ platform_versions[0].completion_percentage > 0 ? '' : 'hidden' }}">
<div class="flex justify-between items-center mb-2">
<span class="text-slate-400 text-sm">Completion</span>
<span class="text-white font-medium stats-completion-percent">{{ platform_versions[0].completion_percentage }}%</span>
</div>
<div class="w-full bg-slate-700 rounded-full h-2">
<div class="bg-green-500 h-2 rounded-full stats-progress-bar transition-all duration-300"
style="width: {{ platform_versions[0].completion_percentage }}%"></div>
</div>
</div>
</div>
{% set metadata = platform_versions[0].metadata|json_decode %}
{% if metadata %}
<div class="border-t border-slate-600/50 mt-4 pt-4">
<h4 class="text-slate-300 font-medium mb-3">Platform Details</h4>
{% if metadata.appid %}
<div class="flex justify-between items-center">
<span class="text-slate-400 text-sm">App ID</span>
<code class="text-blue-400 bg-slate-700/50 px-2 py-1 rounded text-xs">{{ metadata.appid }}</code>
</div>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="container mx-auto px-6 py-8">
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
<!-- Left Column -->
<div class="lg:col-span-8">
<!-- Tab Content -->
<div class="tab-content" id="platformTabsContent">
{% for version in platform_versions %}
{% set safePlatformId = version.platform|lower|replace({' ': '-', '(': '', ')': ''}) ~ '-' ~ version.source_id %}
<div
class="tab-pane fade{% if loop.first %} show active{% endif %}"
id="content-{{ safePlatformId }}"
role="tabpanel"
data-platform="{{ version.platform }}"
data-source="{{ version.source_id }}"
>
<!-- Game Description -->
{% if version.description %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden mb-6">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-900 mb-4">About</h3>
<div class="game-description text-gray-700 leading-relaxed">
{{ version.description|nl2br }}
</div>
<button class="inline-flex items-center mt-3 px-3 py-2 text-sm font-medium text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded-lg transition-colors duration-200 read-more-btn">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
</svg>
Read more
</button>
</div>
</div>
{% endif %}
<!-- Screenshots Section -->
{% if main_game.screenshots is not empty %}
<div class="card mb-4">
<div class="card-header">
<h4 class="mb-0">Screenshots</h4>
</div>
<div class="card-body">
<div id="screenshotsCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-inner rounded" style="max-height: 400px; overflow: hidden;">
{% for screenshot in main_game.screenshots %}
<div class="carousel-item {{ loop.first ? 'active' : '' }}">
<img src="/images/playnite/{{ screenshot }}" class="d-block w-100" alt="Screenshot {{ loop.index }}" style="object-fit: contain; width: 100%; height: 100%;">
</div>
{% endfor %}
</div>
{% if main_game.screenshots|length > 1 %}
<button class="carousel-control-prev" type="button" data-bs-target="#screenshotsCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon bg-dark bg-opacity-50 rounded" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#screenshotsCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon bg-dark bg-opacity-50 rounded" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
{% endif %}
</div>
{% if main_game.screenshots|length > 1 %}
<div class="d-flex justify-content-center mt-3">
<div class="d-flex flex-wrap gap-2" style="max-width: 100%; overflow-x: auto; padding-bottom: 10px;">
{% for screenshot in main_game.screenshots %}
<img
src="/images/playnite/{{ screenshot }}"
class="screenshot-thumbnail {{ loop.first ? 'active' : '' }}"
style="width: 80px; height: 45px; object-fit: cover; cursor: pointer; opacity: 0.7; transition: opacity 0.2s;"
data-bs-target="#screenshotsCarousel"
data-bs-slide-to="{{ loop.index0 }}"
onmouseover="this.style.opacity='1'"
onmouseout="this.style.opacity=this.classList.contains('active') ? '1' : '0.7'"
>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Game Details - Split into Multiple Cards -->
<div class="space-y-6">
{# Basic Information Card #}
{% if version.developer or version.publisher or version.release_date or version.age_rating or version.region or version.version %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<div class="flex items-center mb-4">
<svg class="w-5 h-5 text-blue-600 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<h3 class="text-lg font-semibold text-gray-900">Basic Information</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
{% if version.developer or (version.metadata and version.metadata.developers is defined and version.metadata.developers|length > 0) %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Developer</h4>
<p class="text-gray-900 font-medium">
{% if version.developer %}
{{ version.developer }}
{% elseif version.metadata and version.metadata.developers is defined and version.metadata.developers|length > 0 %}
{{ version.metadata.developers|join(', ') }}
{% endif %}
</p>
</div>
{% endif %}
{% if version.publisher or (version.metadata and version.metadata.publishers is defined and version.metadata.publishers|length > 0) %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Publisher</h4>
<p class="text-gray-900 font-medium">
{% if version.publisher %}
{{ version.publisher }}
{% elseif version.metadata and version.metadata.publishers is defined and version.metadata.publishers|length > 0 %}
{{ version.metadata.publishers|join(', ') }}
{% endif %}
</p>
</div>
{% endif %}
{% if version.release_date %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Release Date</h4>
<p class="text-gray-900 font-medium">{{ version.release_date|date('M d, Y') }}</p>
</div>
{% endif %}
{% if version.age_rating %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Age Rating</h4>
<p class="text-gray-900 font-medium">{{ version.age_rating }}</p>
</div>
{% endif %}
{% if version.region %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Region</h4>
<p class="text-gray-900 font-medium">{{ version.region }}</p>
</div>
{% endif %}
{% if version.version %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Version</h4>
<p class="text-gray-900 font-medium">{{ version.version }}</p>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{# Play Statistics Card #}
{% set has_play_stats = false %}
{% for platform_version in platform_versions %}
{% if platform_version.playtime_minutes is defined and platform_version.playtime_minutes > 0 %}
{% set has_play_stats = true %}
{% endif %}
{% endfor %}
{% if has_play_stats or version.play_count is not null or version.save_count is not null %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<div class="flex items-center mb-4">
<svg class="w-5 h-5 text-green-600 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg>
<h3 class="text-lg font-semibold text-gray-900">Play Statistics</h3>
</div>
<div class="space-y-4">
{# Play Time for All Platforms #}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Play Time by Platform</h4>
<div class="space-y-1">
{% for platform_version in platform_versions %}
{% if platform_version.playtime_minutes is defined and platform_version.playtime_minutes > 0 %}
{% set hours = platform_version.playtime_minutes // 60 %}
{% set minutes = platform_version.playtime_minutes % 60 %}
<div class="flex justify-between items-center py-2 px-3 bg-gray-50 rounded-lg border border-gray-100">
<span class="text-sm font-medium text-gray-700">{{ platform_version.platform }}</span>
<span class="text-sm font-semibold text-gray-900 bg-green-100 text-green-800 px-2 py-1 rounded">
{% if hours > 0 %}{{ hours }}h {% endif %}{% if minutes > 0 or hours == 0 %}{{ minutes }}m{% endif %}
</span>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% if version.play_count is not null %}
<div class="flex justify-between items-center py-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Play Count</h4>
<span class="text-lg font-bold text-blue-600">{{ version.play_count }}</span>
</div>
{% endif %}
{% if version.save_count is not null %}
<div class="flex justify-between items-center py-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Save Files</h4>
<span class="text-lg font-bold text-purple-600">{{ version.save_count }}</span>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{# Technical Information Card #}
{% if version.install_size or version.completion_status %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<div class="flex items-center mb-4">
<svg class="w-5 h-5 text-purple-600 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
<h3 class="text-lg font-semibold text-gray-900">Technical Information</h3>
</div>
<div class="space-y-4">
{% if version.install_size %}
<div class="flex justify-between items-center py-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Install Size</h4>
<span class="text-lg font-bold text-orange-600">
{% if version.install_size >= 1073741824 %}
{{ (version.install_size / 1073741824)|number_format(1) }} GB
{% else %}
{{ (version.install_size / 1048576)|number_format(0) }} MB
{% endif %}
</span>
</div>
{% endif %}
{% if version.completion_status %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Completion Status</h4>
<div class="flex items-center space-x-3">
{% set completionStatus = version.completion_status|lower %}
{% if completionStatus == 'completed' %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
Completed
</span>
{% elseif completionStatus == 'playing' %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h1m4 0h1m-6 8a9 9 0 110-18 9 9 0 010 18z"/>
</svg>
In Progress
</span>
{% elseif completionStatus == 'notplayed' %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<svg class="w-4 h-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Not Played
</span>
{% else %}
<span class="text-gray-900 font-medium">{{ completionStatus|capitalize }}</span>
{% endif %}
{% if version.completion_percentage is not null %}
<span class="text-sm font-semibold text-gray-700 bg-gray-100 px-2 py-1 rounded">
{{ version.completion_percentage }}%
</span>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{# Library Information Card #}
{% if version.added_at or version.modified_at or version.last_played_at %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<div class="flex items-center mb-4">
<svg class="w-5 h-5 text-indigo-600 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.746 0 3.332.477 4.5 1.253v13C19.832 18.477 18.246 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
<h3 class="text-lg font-semibold text-gray-900">Library Information</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
{% if version.added_at %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Added to Library</h4>
<p class="text-gray-900 font-medium">{{ version.added_at|date('M d, Y') }}</p>
</div>
{% endif %}
{% if version.modified_at %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Last Modified</h4>
<p class="text-gray-900 font-medium">{{ version.modified_at|date('M d, Y') }}</p>
</div>
{% endif %}
{% if version.last_played_at %}
<div class="space-y-1">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Last Played</h4>
<p class="text-gray-900 font-medium">{{ version.last_played_at|date('M d, Y') }}</p>
{% if version.last_activity %}
<p class="text-sm text-gray-600">{{ version.last_activity }}</p>
{% endif %}
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{# Metadata & Tags Card #}
{% if version.metadata is defined and version.metadata is not empty %}
{% if version.metadata is iterable %}
{% set metadata = version.metadata %}
{% else %}
{% set metadata = version.metadata|json_decode(true) %}
{% if metadata is null or metadata is not iterable %}
{% set metadata = [] %}
{% endif %}
{% endif %}
{% set has_metadata = false %}
{% if metadata.genres is defined and metadata.genres is iterable and metadata.genres|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if metadata.tags is defined and metadata.tags is iterable and metadata.tags|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if metadata.features is defined and metadata.features is iterable and metadata.features|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if metadata.platforms is defined and metadata.platforms is iterable and metadata.platforms|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if metadata.series is defined and metadata.series is iterable and metadata.series|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if metadata.source is defined and metadata.source is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.user_score is defined and metadata.user_score is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.community_score is defined and metadata.community_score is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.critic_score is defined and metadata.critic_score is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.rating is defined and metadata.rating is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.release_date is defined and metadata.release_date is not empty %}{% set has_metadata = true %}{% endif %}
{% if metadata.links is defined and metadata.links is iterable and metadata.links|length > 0 %}{% set has_metadata = true %}{% endif %}
{% if has_metadata %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<div class="flex items-center mb-4">
<svg class="w-5 h-5 text-pink-600 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
</svg>
<h3 class="text-lg font-semibold text-gray-900">Tags & Metadata</h3>
</div>
<div class="space-y-4">
{# Genres #}
{% if metadata.genres is defined and metadata.genres is iterable and metadata.genres|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Genres</h4>
<div class="flex flex-wrap gap-2">
{% for genre in metadata.genres %}
{% if genre is not empty %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800 border border-blue-200">
<svg class="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4V2a1 1 0 011-1h8a1 1 0 011 1v2m-9 0h10m-9 0V1m10 3V1m0 3l1 1v16a2 2 0 01-2 2H6a2 2 0 01-2-2V5l1-1z"/>
</svg>
{{ genre }}
</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{# Tags (filtered) #}
{% if metadata.tags is defined and metadata.tags is iterable and metadata.tags|length > 0 %}
{% set filteredTags = metadata.tags|filter(tag => tag is not empty and not (tag starts with '[' and ']' in tag)) %}
{% if filteredTags|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Tags</h4>
<div class="flex flex-wrap gap-2">
{% for tag in filteredTags %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800 border border-gray-200">
<svg class="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
</svg>
{{ tag }}
</span>
{% endfor %}
</div>
</div>
{% endif %}
{% endif %}
{# Features #}
{% if metadata.features is defined and metadata.features is iterable and metadata.features|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Features</h4>
<div class="flex flex-wrap gap-2">
{% for feature in metadata.features %}
{% if feature is not empty %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800 border border-green-200">
<svg class="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
{{ feature }}
</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{# Series #}
{% if metadata.series is defined and metadata.series is iterable and metadata.series|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Series</h4>
<div class="flex flex-wrap gap-2">
{% for series in metadata.series %}
{% if series is not empty %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-purple-100 text-purple-800 border border-purple-200">
<svg class="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
</svg>
{{ series }}
</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{# Available Platforms #}
{% if metadata.platforms is defined and metadata.platforms is iterable and metadata.platforms|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Available Platforms</h4>
<div class="flex flex-wrap gap-2">
{% for platform in metadata.platforms %}
{% if platform is not empty %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-indigo-100 text-indigo-800 border border-indigo-200">
<svg class="w-3 h-3 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
{{ platform }}
</span>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
{# Scores Row #}
{% if metadata.user_score is defined and metadata.user_score is not empty or metadata.community_score is defined and metadata.community_score is not empty or metadata.critic_score is defined and metadata.critic_score is not empty %}
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 pt-2 border-t border-gray-100">
{% if metadata.user_score is defined and metadata.user_score is not empty %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">User Score</h4>
<div class="flex items-center space-x-2">
<div class="flex-1 bg-gray-200 rounded-full h-2">
<div class="bg-yellow-500 h-2 rounded-full" style="width: {{ metadata.user_score * 10 }}%"></div>
</div>
<span class="text-sm font-medium text-gray-700">{{ metadata.user_score }}/10</span>
</div>
</div>
{% endif %}
{% if metadata.community_score is defined and metadata.community_score is not empty %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Community Score</h4>
<div class="flex items-center space-x-2">
<div class="flex-1 bg-gray-200 rounded-full h-2">
<div class="bg-blue-500 h-2 rounded-full" style="width: {{ metadata.community_score }}%"></div>
</div>
<span class="text-sm font-medium text-gray-700">{{ metadata.community_score }}%</span>
</div>
</div>
{% endif %}
{% if metadata.critic_score is defined and metadata.critic_score is not empty %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Critic Score</h4>
<div class="flex items-center space-x-2">
<div class="flex-1 bg-gray-200 rounded-full h-2">
<div class="bg-green-500 h-2 rounded-full" style="width: {{ metadata.critic_score }}%"></div>
</div>
<span class="text-sm font-medium text-gray-700">{{ metadata.critic_score }}%</span>
</div>
</div>
{% endif %}
</div>
{% endif %}
{# Rating #}
{% if metadata.rating is defined and metadata.rating is not empty %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Rating</h4>
<p class="text-gray-900 font-medium">
{% if metadata.rating == 'RP' %}
Rating Pending
{% elseif metadata.rating == 'EC' %}
Early Childhood
{% elseif metadata.rating == 'E' %}
Everyone
{% elseif metadata.rating == 'E10+' %}
Everyone 10+
{% elseif metadata.rating == 'T' %}
Teen
{% elseif metadata.rating == 'M' %}
Mature 17+
{% elseif metadata.rating == 'AO' %}
Adults Only 18+
{% else %}
{{ metadata.rating }}
{% endif %}
{% if metadata.rating_description is defined and metadata.rating_description is not empty %}
<small class="text-gray-600 block mt-1">{{ metadata.rating_description }}</small>
{% endif %}
</p>
</div>
{% endif %}
{# Release Date #}
{% if metadata.release_date is defined and metadata.release_date is not empty %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Original Release</h4>
<p class="text-gray-900 font-medium">
{{ metadata.release_date }}
{% if metadata.release_year is defined and metadata.release_year is not empty %}
<small class="text-gray-600">({{ metadata.release_year }})</small>
{% endif %}
</p>
</div>
{% endif %}
{# Links #}
{% if metadata.links is defined and metadata.links is iterable and metadata.links|length > 0 %}
<div class="space-y-2">
<h4 class="text-sm font-medium text-gray-600 uppercase tracking-wide">Links</h4>
<div class="flex flex-wrap gap-2">
{% for link in metadata.links %}
{% if link.name is defined and link.url is defined %}
<a href="{{ link.url }}" target="_blank" class="inline-flex items-center px-3 py-2 border border-gray-300 rounded-lg text-sm text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors duration-200">
<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
{{ link.name }}
</a>
{% endif %}
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<!-- Right Column -->
<div class="lg:col-span-4 space-y-6">
<!-- Platform Versions -->
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-900 mb-4">Available Platforms</h3>
<div class="space-y-3">
{% for version in platform_versions %}
<div class="flex justify-between items-center py-2 border-b border-gray-100 last:border-b-0">
<div>
<h4 class="font-medium text-gray-900">{{ version.platform }}</h4>
{% if version.source_name %}
<p class="text-sm text-gray-600">{{ version.source_name }}</p>
{% endif %}
</div>
{% if version.is_installed %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Installed</span>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Game Links -->
{% set playniteLinks = platform_versions[0].links_json|json_decode %}
{% if playniteLinks %}
<div class="bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden">
<div class="p-6">
<h3 class="text-xl font-semibold text-gray-900 mb-4">Links</h3>
<div class="space-y-2">
{% for link in playniteLinks %}
<a href="{{ link.Url }}" target="_blank" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50 hover:border-gray-400 transition-colors duration-200">
<svg class="w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
</svg>
{{ link.Name }}
</a>
{% endfor %}
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<style>
/* Tab functionality - ensure inactive tabs are hidden */
.tab-pane {
display: none;
}
.tab-pane.show.active {
display: block;
}
.game-hero {
padding: 2rem 0;
position: relative;
margin-bottom: 2rem;
background-size: cover !important;
background-position: center !important;
}
.game-hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.6) 100%);
z-index: 0;
}
.game-hero .container {
position: relative;
z-index: 1;
}
.game-description {
max-height: 150px;
overflow: hidden;
position: relative;
transition: max-height 0.3s ease;
}
.game-description.expanded {
max-height: 1000px;
}
.game-description::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 50px;
background: linear-gradient(to bottom, transparent, var(--bs-body-bg, #fff));
pointer-events: none;
transition: opacity 0.3s ease;
}
.game-description.expanded::after {
opacity: 0;
}
.nav-tabs {
--bs-nav-tabs-border-width: 0;
gap: 0.5rem;
margin-bottom: 1rem;
flex-wrap: wrap;
padding-bottom: 4px;
align-items: center;
}
/* Only show scrollbar on mobile */
@media (max-width: 991.98px) {
.nav-tabs {
flex-wrap: nowrap;
overflow-x: auto;
scrollbar-width: thin;
}
.nav-tabs::-webkit-scrollbar {
height: 4px;
}
.nav-tabs::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 2px;
}
}
.nav-tabs .nav-link {
color: rgba(255, 255, 255, 0.7);
padding: 0.5rem 1rem;
border: none;
border-bottom: 2px solid transparent;
border-radius: 4px 4px 0 0;
transition: all 0.2s ease;
white-space: nowrap;
display: flex;
align-items: center;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.05);
margin-bottom: 0.25rem;
}
.nav-tabs .nav-link:hover:not(.active) {
color: white;
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.1);
}
.nav-tabs .nav-link.active {
color: white;
background: rgba(13, 110, 253, 0.15);
border-color: var(--bs-primary, #0d6efd);
font-weight: 500;
}
/* Platform icon colors */
.nav-tabs .nav-link iconify-icon[icon^="mdi:microsoft-windows"] {
color: #00a4ef;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:sony-playstation"] {
color: #003087;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:microsoft-xbox"] {
color: #107c10;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:nintendo-switch"],
.nav-tabs .nav-link iconify-icon[icon^="mdi:nintendo-wii"] {
color: #e60012;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:steam"] {
color: #00adee;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:apple"],
.nav-tabs .nav-link iconify-icon[icon^="mdi:apple-ios"] {
color: #a2aaad;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:android"] {
color: #3ddc84;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:linux"] {
color: #fcc624;
}
.nav-tabs .nav-link iconify-icon[icon^="mdi:gamepad"] {
color: #8e8e93;
}
/* Screenshot carousel styles */
.carousel-item {
height: 400px;
background-color: #1a1a1a;
display: flex;
align-items: center;
justify-content: center;
}
.screenshot-thumbnail {
border: 2px solid transparent;
transition: all 0.2s ease;
}
.screenshot-thumbnail:hover,
.screenshot-thumbnail.active {
border-color: var(--bs-primary);
opacity: 1 !important;
}
/* Custom scrollbar for thumbnails */
.d-flex.flex-wrap.gap-2::-webkit-scrollbar {
height: 6px;
}
.d-flex.flex-wrap.gap-2::-webkit-scrollbar-thumb {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.card {
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.05);
}
.card-title {
font-weight: 600;
color: var(--bs-heading-color, #212529);
}
.breadcrumb {
background: transparent;
padding: 0;
margin-bottom: 1rem;
}
.breadcrumb-item a {
text-decoration: none;
}
.breadcrumb-item.active {
color: white;
}
.list-group-item {
border-left: none;
border-right: none;
padding: 0.75rem 0;
}
.list-group-item:first-child {
border-top: none;
padding-top: 0;
}
.list-group-item:last-child {
border-bottom: none;
padding-bottom: 0;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Initialize screenshot thumbnails
initScreenshotThumbnails();
// Initialize all tooltips
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Function to update game cover image
function updateGameCover(imageUrl) {
const coverContainer = document.getElementById('gameCover');
if (!imageUrl) {
coverContainer.innerHTML = `
<div class="bg-dark rounded-3 d-flex align-items-center justify-content-center" style="width: 200px; height: 300px;">
<i class="bi bi-controller text-muted" style="font-size: 3rem;"></i>
</div>`;
return;
}
coverContainer.innerHTML = `
<img src="${imageUrl}"
class="img-fluid rounded-3 shadow-lg game-cover"
alt="${document.title}"
style="max-height: 300px; width: auto;">`;
}
// Function to update hero background
function updateHeroBackground(imageUrl) {
const hero = document.getElementById('gameHero');
if (imageUrl) {
hero.style.backgroundImage = `linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('${imageUrl}')`;
} else {
hero.style.backgroundImage = "linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url('/images/hero-bg.jpg')";
}
}
// Function to update stats in the sidebar
function updateStats(platformData) {
// Update last played
const lastPlayedEl = document.querySelector('.stats-last-played');
if (lastPlayedEl) {
lastPlayedEl.textContent = platformData.lastPlayed || 'Never';
}
// Update playtime
const playtimeEl = document.querySelector('.stats-playtime');
if (playtimeEl) {
playtimeEl.textContent = platformData.playtime || '0h 0m';
}
// Update completion
const completionEl = document.querySelector('.stats-completion');
const completionPctEl = document.querySelector('.stats-completion-percent');
const progressBar = document.querySelector('.stats-progress-bar');
if (platformData.completionPercentage > 0) {
if (completionEl) completionEl.style.display = 'flex';
if (completionPctEl) completionPctEl.textContent = `${platformData.completionPercentage}%`;
if (progressBar) progressBar.style.width = `${platformData.completionPercentage}%`;
} else {
if (completionEl) completionEl.style.display = 'none';
}
// Update cover image if available
if (platformData.coverImage) {
updateGameCover(platformData.coverImage);
} else {
// Fallback to main game image if no platform-specific image
updateGameCover('{{ main_game.image_url ? "/images/playnite/" ~ main_game.image_url : "" }}');
}
// Update hero background if available
if (platformData.heroImage) {
updateHeroBackground(platformData.heroImage);
} else {
updateHeroBackground('');
}
}
// Manual tab switching since Bootstrap JS may not be loaded
const platformTabs = document.querySelectorAll('.platform-tab');
const tabPanes = document.querySelectorAll('.tab-pane');
platformTabs.forEach(tab => {
tab.addEventListener('click', function(e) {
e.preventDefault();
// Remove active class from all tabs
platformTabs.forEach(t => {
t.classList.remove('active', 'bg-blue-600', 'text-white', 'shadow-lg');
t.classList.add('text-slate-300');
});
// Add active class to clicked tab
this.classList.add('active', 'bg-blue-600', 'text-white', 'shadow-lg');
this.classList.remove('text-slate-300');
// Hide all tab panes
tabPanes.forEach(pane => {
pane.classList.remove('show', 'active');
pane.style.display = 'none';
});
// Show the target tab pane
const targetId = this.getAttribute('data-bs-target');
const targetPane = document.querySelector(targetId);
if (targetPane) {
targetPane.classList.add('show', 'active');
targetPane.style.display = 'block';
}
// Get the platform data from the tab and update stats
const platformData = {
lastPlayed: this.dataset.lastPlayed || 'Never',
playtime: this.dataset.playtime || '0h 0m',
completionPercentage: parseInt(this.dataset.completion || '0'),
coverImage: this.dataset.coverImage || '',
heroImage: this.dataset.banner_url || ''
};
// Update the UI with platform-specific data
updateStats(platformData);
});
});
// Read more/less functionality
const readMoreBtns = document.querySelectorAll('.read-more-btn');
readMoreBtns.forEach(btn => {
const description = btn.previousElementSibling;
btn.addEventListener('click', function() {
description.classList.toggle('expanded');
this.textContent = description.classList.contains('expanded') ? 'Show less' : 'Read more';
});
});
});
</script>
{% endblock %}