fix: restore and stabilize tab navigation with Alpine.js
CI / Test (Python 3.11) (push) Has been cancelled
CI / Test (Python 3.12) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Type Check (push) Has been cancelled
CI / Summary (push) Has been cancelled

- Fixed navigation blockage by moving Alpine state to body scope
- Resolved CSS display conflicts between legacy .active class and x-show
- Synchronized legacy auth logic with Alpine global state
- Redirected legacy switchTab calls to Alpine events
- Removed obsolete tabs.js and updated home section initialization
- Added E2E navigation test placeholder
This commit is contained in:
root
2026-03-24 12:19:57 +00:00
parent 5c7116557d
commit 69e14afedf
7 changed files with 64 additions and 64 deletions
+1 -1
View File
@@ -172,7 +172,7 @@
}
.tab-content {
display: none;
/* Managed by Alpine.js x-show */
}
.tab-content.active {
+16 -3
View File
@@ -91,14 +91,27 @@ async function checkAuth() {
if (response.ok) {
const data = await response.json();
// Log for debugging
console.log('Auth check successful for:', data.user.username);
// Dispatch event for Alpine.js global state
window.dispatchEvent(new CustomEvent('auth-success', {
detail: { username: data.user.full_name || data.user.username }
}));
// Set global auth state in case dispatch was too early
if (window.Alpine) {
const body = document.querySelector('body');
if (body && body.__x) {
body.__x.$data.isAuthenticated = true;
body.__x.$data.username = data.user.full_name || data.user.username;
}
}
return true;
} else {
}
else {
// Token invalid, remove it and redirect
removeToken();
redirectToLogin();
+3 -55
View File
@@ -195,63 +195,11 @@ async function handleDownloadProviderEpisode(providerId) {
}
/**
* Switch between tabs
* Switch between tabs (Modernized to Alpine.js)
*/
function switchTab(tabName) {
// Hide all tabs
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.remove('active');
});
document.querySelectorAll('.tab').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab
const tabElement = document.getElementById(`tab-${tabName}`);
if (tabElement) {
tabElement.classList.add('active');
}
// Find and activate the button
const buttons = document.querySelectorAll('.tab');
buttons.forEach(btn => {
const tabType = btn.getAttribute('data-tab-type');
if (tabType === 'home' && tabName === 'home') {
btn.classList.add('active');
} else if (tabType === 'anime' && tabName === 'anime') {
// Static anime tab
btn.classList.add('active');
} else if (tabType === 'series' && tabName === 'series') {
// Static series tab
btn.classList.add('active');
} else if (tabType === 'providers' && tabName === 'providers') {
// Static providers tab
btn.classList.add('active');
} else if (tabType === 'anime' && btn.getAttribute('data-provider') === tabName.replace('anime-', '')) {
btn.classList.add('active');
} else if (tabType === 'series' && btn.getAttribute('data-provider') === tabName.replace('series-', '')) {
btn.classList.add('active');
}
});
// Load home content when switching to home tab
if (tabName === 'home') {
// Content is already loaded on init, but you can reload if needed
if (typeof loadHomeContent === 'function' && !document.getElementById('recommendationsList').hasChildNodes()) {
loadHomeContent();
}
}
// Load watchlist content when switching to watchlist tab
if (tabName === 'watchlist') {
if (typeof loadSchedulerStatus === 'function') {
loadSchedulerStatus();
}
if (typeof displayWatchlist === 'function') {
displayWatchlist();
}
}
console.log('Switching tab to:', tabName);
window.dispatchEvent(new CustomEvent('set-tab', { detail: { tab: tabName } }));
}
-1
View File
@@ -23,7 +23,6 @@
<script src="/static/js/anime-details.js?v=1.12" defer></script>
<script src="/static/js/series-search.js?v=1.11" defer></script>
<script src="/static/js/recommendations.js?v=1.11" defer></script>
<script src="/static/js/tabs.js?v=1.11" defer></script>
<script src="/static/js/watchlist.js?v=1.11" defer></script>
<script src="/static/js/watchlist-ui.js?v=1.11" defer></script>
<script src="/static/js/main.js?v=1.11" defer></script>
+2 -3
View File
@@ -26,9 +26,8 @@
<p style="color: #00d9ff; margin: 0 0 10px 0;">👋 Bienvenue! <a href="/login" style="color: #00d9ff; text-decoration: underline;">Connectez-vous</a> pour télécharger des vidéos</p>
</div>
<!-- Tabs - Shown only when authenticated -->
<div id="mainTabs" class="tabs" x-show="isAuthenticated" x-cloak style="visibility: visible;">
<button class="tab" :class="{ 'active': activeTab === 'home' }" @click="activeTab = 'home'">
<div id="mainTabs" class="tabs" x-show="isAuthenticated" x-cloak style="visibility: visible; display: flex;">
<button class="tab" :class="{ 'active': activeTab === 'home' }" @click="activeTab = 'home'; if (typeof loadHomeContent === 'function') loadHomeContent()">
<svg style="width:16px;height:16px;vertical-align:middle;margin-right:5px" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path>
</svg>
+1 -1
View File
@@ -1,5 +1,5 @@
<!-- Home Section: Recommendations & Latest Releases -->
<div id="tab-home" class="tab-content active">
<div id="tab-home" class="tab-content" x-show="activeTab === 'home'" x-init="if (activeTab === 'home') loadHomeContent()">
<!-- Loading State -->
<div id="homeLoading" class="loading-spinner">Chargement des recommandations...</div>
+41
View File
@@ -0,0 +1,41 @@
import pytest
from playwright.sync_api import Page, expect
# Since we don't have a full running environment with auth easily mockable in pure Python Playwright
# without starting the server, I will write a test that can be run if the server is up.
# For CI/CD, we'd use a fixture to start the uvicorn server.
@pytest.mark.skip(reason="Requires running server and complex auth mock")
def test_tab_navigation(page: Page):
# Navigate to the app
page.goto("http://localhost:3000/web")
# Mock authentication state in localStorage and Alpine
page.evaluate("""() => {
localStorage.setItem('auth_token', 'mock-token');
document.body.__x.$data.isAuthenticated = true;
document.body.__x.$data.username = 'TestUser';
}""")
# Reload or wait for Alpine to react
page.reload()
# Verify Home tab is active by default
expect(page.locator("#tab-home")).to_be_visible()
expect(page.locator("button.tab:has-text('Accueil')")).to_have_class(/active/)
# Click on Anime tab
page.click("button.tab:has-text('Anime')")
# Verify Anime tab is shown and Home is hidden
expect(page.locator("#tab-anime")).to_be_visible()
expect(page.locator("#tab-home")).to_be_hidden()
expect(page.locator("button.tab:has-text('Anime')")).to_have_class(/active/)
# Click on Watchlist tab
page.click("button.tab:has-text('Watchlist')")
# Verify Watchlist tab is shown
expect(page.locator("#tab-watchlist")).to_be_visible()
expect(page.locator("#tab-anime")).to_be_hidden()
expect(page.locator("button.tab:has-text('Watchlist')")).to_have_class(/active/)