reset_password.html 164 lignes · 6692 octets
{% extends "base.html" %}

{% block title %}{{ page_title }} - {{ app_name }}{% endblock %}

{% block content %}
<div class="min-h-screen flex items-center justify-center bg-base-200 px-4">
    <div class="max-w-lg w-full space-y-6">
        <div class="text-center">
            <div class="inline-flex items-center justify-center w-16 h-16 mb-4">
                <img src="/static/images/logo.svg" alt="{{ app_name }} Logo" class="h-16 w-16">
            </div>
            <h1 class="text-3xl font-bold text-base-content mb-2">{{ reset_password_title }}</h1>
            <p class="text-base-content/70">
                {{ subtitle }}
            </p>
        </div>

        <div id="alert-error" class="hidden alert alert-error">
            <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="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
            <span id="alert-error-text"></span>
        </div>

        <div id="alert-success" class="hidden alert alert-success">
            <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="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
            <span id="alert-success-text"></span>
        </div>

        <div class="card bg-base-100 shadow-xl p-8">
            <form id="reset-form" class="space-y-4">
                <input type="hidden" id="token" name="token" value="">
                
                <div class="form-control">
                    <label for="new_password" class="label">
                        <span class="label-text">{{ new_password_label }}</span>
                    </label>
                    <input
                        type="password"
                        id="new_password"
                        name="new_password"
                        required
                        autocomplete="new-password"
                        class="input input-bordered w-full"
                        placeholder="{{ new_password_placeholder }}">
                    <label class="label">
                        <span class="label-text-alt text-base-content/70">{{ password_requirements }}</span>
                    </label>
                </div>

                <div class="form-control">
                    <label for="confirm_password" class="label">
                        <span class="label-text">{{ confirm_password_label }}</span>
                    </label>
                    <input
                        type="password"
                        id="confirm_password"
                        name="confirm_password"
                        required
                        autocomplete="new-password"
                        class="input input-bordered w-full"
                        placeholder="{{ confirm_password_placeholder }}">
                </div>

                <div class="form-control mt-6">
                    <button type="submit" class="btn btn-primary w-full">
                        {{ submit_button }}
                    </button>
                </div>
            </form>

            <div class="mt-4 text-sm text-base-content/70 text-center space-y-2">
                <a href="/login" class="text-primary hover:underline">{{ back_to_login }}</a>
            </div>
        </div>
    </div>
</div>

<script>
    const MSG_MISSING_TOKEN_URL = "{{ missing_token_url }}";
    const MSG_MISSING_TOKEN = "{{ missing_token }}";
    const MSG_MISSING_NEW_PASSWORD = "{{ missing_new_password }}";
    const MSG_PASSWORDS_NOT_MATCH = "{{ passwords_not_match }}";
    const MSG_RESET_SUCCESS = "{{ reset_success_message }}";
    const MSG_ERROR_LINK_INVALID = "{{ error_link_invalid }}";
    const MSG_ERROR_GENERIC = "{{ error_generic }}";
    const MSG_ERROR_DEFAULT = "{{ error_message }}";
    const MSG_SUCCESS_DEFAULT = "{{ success_message }}";

    const urlParams = new URLSearchParams(window.location.search);
    const token = urlParams.get('token');
    
    if (token) {
        document.getElementById('token').value = token;
    } else {
        showError(MSG_MISSING_TOKEN_URL);
    }

    const form = document.getElementById('reset-form');
    const alertError = document.getElementById('alert-error');
    const alertErrorText = document.getElementById('alert-error-text');
    const alertSuccess = document.getElementById('alert-success');
    const alertSuccessText = document.getElementById('alert-success-text');

    function showError(msg) {
        if (alertError && alertErrorText) {
            alertErrorText.textContent = msg || MSG_ERROR_DEFAULT;
            alertError.classList.remove('hidden');
        }
        if (alertSuccess) alertSuccess.classList.add('hidden');
    }

    function showSuccess(msg) {
        if (alertSuccess && alertSuccessText) {
            alertSuccessText.textContent = msg || MSG_SUCCESS_DEFAULT;
            alertSuccess.classList.remove('hidden');
        }
        if (alertError) alertError.classList.add('hidden');
    }

    form?.addEventListener('submit', (e) => {
        e.preventDefault();
        const token = document.getElementById('token')?.value;
        const newPassword = document.getElementById('new_password')?.value;
        const confirmPassword = document.getElementById('confirm_password')?.value;

        if (!token) {
            showError(MSG_MISSING_TOKEN);
            return;
        }

        if (!newPassword) {
            showError(MSG_MISSING_NEW_PASSWORD);
            return;
        }

        if (newPassword !== confirmPassword) {
            showError(MSG_PASSWORDS_NOT_MATCH);
            return;
        }

        fetch('/api/v1/auth/reset-password', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ token, new_password: newPassword }),
            credentials: 'include'
        })
        .then(async (resp) => {
            let msg = '';
            try {
                const data = await resp.json();
                msg = data.message || data.error || msg;
            } catch (_) {}
            if (resp.ok) {
                showSuccess(msg || MSG_RESET_SUCCESS);
                setTimeout(() => {
                    window.location.href = '/login';
                }, 3000);
            } else {
                showError(msg || MSG_ERROR_LINK_INVALID);
            }
        })
        .catch(() => showError(MSG_ERROR_GENERIC));
    });
</script>
{% endblock %}