preferences.html 406 lignes · 22024 octets
{% extends "base.html" %}

{% block title %}{{ page_title }}{% endblock %}

{% block navbar %}
{% include "components/navbar.html" %}
{% endblock %}

{% block content %}
<div class="container mx-auto px-4 py-8">
    <!-- Quick Language Selector (Feature 014) -->
    <div class="flex justify-end mb-4">
        <div class="dropdown dropdown-end">
            <label tabindex="0" class="btn btn-ghost btn-sm gap-2">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" />
                </svg>
                <span id="current-lang-display">
                    {% for (code, name, is_current) in supported_languages %}
                        {% if is_current %}{{ name }}{% endif %}
                    {% endfor %}
                </span>
                <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" 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>
            </label>
            <ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
                    {% for (code, name, is_current) in supported_languages %}
                    <li>
                        <a href="#" onclick="return window.changeLanguage('{{ code }}');" class="{% if is_current %}active{% endif %}">
                            {{ name }}
                            {% if is_current %}
                            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                                <path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
                            </svg>
                            {% endif %}
                        </a>
                    </li>
                    {% endfor %}
            </ul>
        </div>
    </div>

    <!-- Breadcrumb -->
    <div class="text-sm breadcrumbs mb-6">
        <ul>
            <li><a href="/">{{ breadcrumb_home }}</a></li>
            <li><a href="/profile">{{ breadcrumb_settings }}</a></li>
            <li>{{ breadcrumb_preferences }}</li>
        </ul>
    </div>

    <!-- Page Header -->
    <div class="mb-8">
        <h1 class="text-3xl font-bold mb-2">{{ title }}</h1>
        <p class="text-base-content/70">{{ subtitle }}</p>
    </div>

    <!-- Preferences Form -->
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 max-w-6xl">
        <!-- Theme Preferences Card -->
        <div class="card bg-base-100 shadow-xl">
            <div class="card-body">
                <h2 class="card-title mb-4">
                    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" /></svg>
                    {{ theme_label }}
                </h2>
                
                <form id="preferencesForm" class="space-y-4">
                    <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
                    
                    <!-- Theme Options -->
                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="theme" 
                                value="light" 
                                class="radio radio-primary" 
                                {% if is_theme_light %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-warning" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
                                    <span class="label-text font-semibold">{{ theme_light }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ theme_light_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="theme" 
                                value="dark" 
                                class="radio radio-primary" 
                                {% if is_theme_dark %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-info" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" /></svg>
                                    <span class="label-text font-semibold">{{ theme_dark }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ theme_dark_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="theme" 
                                value="auto" 
                                class="radio radio-primary" 
                                {% if is_theme_auto %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-secondary" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" /></svg>
                                    <span class="label-text font-semibold">{{ theme_auto }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ theme_auto_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <!-- Theme Preview -->
                    <div class="alert" id="themePreview">
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><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"></path></svg>
                        <span>{{ theme_preview }}</span>
                    </div>
                </form>
            </div>
        </div>

        <!-- Language Preferences Card -->
        <div class="card bg-base-100 shadow-xl">
            <div class="card-body">
                <h2 class="card-title mb-4">
                    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129" /></svg>
                    {{ language_label }}
                </h2>
                
                <div class="space-y-4">
                    <!-- Language Options -->
                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="language" 
                                value="fr" 
                                class="radio radio-primary" 
                                form="preferencesForm"
                                {% if is_language_fr %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇫🇷</span>
                                    <span class="label-text font-semibold">{{ language_fr }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_fr_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="language" 
                                value="en" 
                                class="radio radio-primary" 
                                form="preferencesForm"
                                {% if is_language_en %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇬🇧</span>
                                    <span class="label-text font-semibold">{{ language_en }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_en_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="language" 
                                value="de" 
                                class="radio radio-primary" 
                                form="preferencesForm"
                                {% if is_language_de %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇩🇪</span>
                                    <span class="label-text font-semibold">{{ language_de }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_de_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input 
                                type="radio" 
                                name="language" 
                                value="es" 
                                class="radio radio-primary" 
                                form="preferencesForm"
                                {% if is_language_es %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇪🇸</span>
                                    <span class="label-text font-semibold">{{ language_es }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_es_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input
                                type="radio"
                                name="language"
                                value="it"
                                class="radio radio-primary"
                                form="preferencesForm"
                                {% if is_language_it %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇮🇹</span>
                                    <span class="label-text font-semibold">{{ language_it }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_it_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="form-control">
                        <label class="label cursor-pointer justify-start gap-4 p-4 rounded-lg border border-base-300 hover:bg-base-200 transition-colors">
                            <input
                                type="radio"
                                name="language"
                                value="pt"
                                class="radio radio-primary"
                                form="preferencesForm"
                                {% if is_language_pt %}checked{% endif %}
                            >
                            <div class="flex-1">
                                <div class="flex items-center gap-2">
                                    <span class="text-2xl">🇧🇷</span>
                                    <span class="label-text font-semibold">{{ language_pt }}</span>
                                </div>
                                <span class="label-text-alt text-base-content/70">{{ language_pt_desc }}</span>
                            </div>
                        </label>
                    </div>

                    <div class="alert alert-warning">
                        <svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
                        <span class="text-xs">{{ translation_warning }}</span>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Save Button (Fixed at Bottom) -->
    <div class="sticky bottom-4 mt-8 max-w-6xl">
        <div class="card bg-base-100 shadow-2xl border-2 border-primary">
            <div class="card-body flex-row items-center justify-between p-4">
                <div>
                    <h3 class="font-semibold">Préférences modifiées</h3>
                    <p class="text-sm text-base-content/70">Enregistrez vos changements pour les appliquer</p>
                </div>
                <div class="flex gap-2">
                    <a href="/profile" class="btn btn-ghost">Annuler</a>
                    <button type="submit" form="preferencesForm" class="btn btn-primary" id="saveBtn">
                        <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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>
                        Enregistrer les préférences
                    </button>
                </div>
            </div>
        </div>
    </div>

    <!-- Info Card -->
    <div class="card bg-info text-info-content shadow-xl max-w-6xl mt-6">
        <div class="card-body">
            <h3 class="card-title text-base">
                <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" 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>
                {{ sync_title }}
            </h3>
            <p class="text-sm">
                {{ sync_desc }}
            </p>
        </div>
    </div>
</div>

<script>
var SAVED_RELOAD_MSG = {{ saved_reload_json|safe }};
var SAVED_SUCCESS_MSG = {{ saved_success_json|safe }};
var ERROR_MSG = {{ error_prefix_json|safe }};
var NETWORK_ERROR_MSG = {{ network_error_json|safe }};
var SAVE_BUTTON_TEXT = {{ save_button_json|safe }};
var SAVE_BUTTON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" 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> ';

function resetSaveBtn() {
    var saveBtn = document.getElementById('saveBtn');
    saveBtn.disabled = false;
    saveBtn.innerHTML = SAVE_BUTTON_SVG + SAVE_BUTTON_TEXT;
}

document.getElementById('preferencesForm').addEventListener('submit', async function(e) {
    e.preventDefault();
    
    var formData = new FormData(this);
    var saveBtn = document.getElementById('saveBtn');
    var theme = formData.get('theme');
    var language = formData.get('language');
    
    saveBtn.disabled = true;
    saveBtn.innerHTML = '<span class="loading loading-spinner"></span> ' + "{{ saving }}";
    
    try {
        var params = new URLSearchParams();
        if (theme) params.set('theme', theme);
        if (language) params.set('language', language);
        params.set('csrf_token', formData.get('csrf_token') || '');

        var response = await fetch('/settings/preferences', {
            method: 'PUT',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-CSRF-Token': '{{ csrf_token }}' },
            credentials: 'include',
            body: params.toString()
        });
        
        var result = await response.json();
        
        if (response.ok) {
            document.cookie = 'theme=' + (theme || 'auto') + '; path=/; max-age=31536000; SameSite=Lax';
            
            if (theme === 'dark') {
                document.documentElement.setAttribute('data-theme', 'dark');
            } else if (theme === 'light') {
                document.documentElement.setAttribute('data-theme', 'light');
            } else {
                var prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
                document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
            }
            
            if (result.reload_required) {
                document.cookie = 'locale=' + language + '; path=/; max-age=31536000; SameSite=Lax';
                if (confirm('✅ ' + SAVED_RELOAD_MSG)) {
                    window.location.reload();
                    return;
                }
            } else {
                alert('✅ ' + SAVED_SUCCESS_MSG);
            }
            resetSaveBtn();
        } else {
            var detail = result.error || result.message || '';
            alert('❌ ' + ERROR_MSG.replace('{message}', detail));
            resetSaveBtn();
        }
    } catch (error) {
        console.error('Error:', error);
        alert('❌ ' + NETWORK_ERROR_MSG);
        resetSaveBtn();
    }
});

// Preview theme changes in real-time
document.querySelectorAll('input[name="theme"]').forEach(radio => {
    radio.addEventListener('change', function() {
        const preview = document.getElementById('themePreview');
        const previewDark = "{{ preview_dark }}";
        const previewLight = "{{ preview_light }}";
        const previewAuto = "{{ preview_auto }}";
        
        if (this.value === 'dark') {
            preview.className = 'alert alert-info';
            preview.querySelector('span').textContent = previewDark;
        } else if (this.value === 'light') {
            preview.className = 'alert alert-success';
            preview.querySelector('span').textContent = previewLight;
        } else {
            preview.className = 'alert';
            preview.querySelector('span').textContent = previewAuto;
        }
    });
});
</script>
{% endblock %}