227 lines
No EOL
12 KiB
HTML
227 lines
No EOL
12 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n static crispy_forms_tags gravatar %}
|
|
|
|
{% block head_title %}{{ _('Dashboard') }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container mx-auto" x-data="{ activeTab: '{{ active_tab|default:'dash' }}' }">
|
|
|
|
<!-- Tab Navigation -->
|
|
<div class="tabs tabs-border mb-8">
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'dash'}" @click="activeTab = 'dash'">{{ _('Dash') }}</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'dashboard_offers'}" @click="activeTab = 'dashboard_offers'">{{ _('Your Trade Offers') }} ({{ dashboard_offers_paginated.page_obj.count }})</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'waiting_on_you'}" @click="activeTab = 'waiting_on_you'">{{ _('Waiting on You') }} ({{ trade_acceptances_waiting_paginated.page_obj.count }})</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'waiting_on_them'}" @click="activeTab = 'waiting_on_them'">{{ _('Waiting on Them') }} ({{ other_party_trade_acceptances_paginated.page_obj.count }})</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'trade_history'}" @click="activeTab = 'trade_history'">{{ _('Trade History') }}</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'profile'}" @click="activeTab = 'profile'">{{ _('Profile') }}</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'friend_codes'}" @click="activeTab = 'friend_codes'">{{ _('Friend Codes') }}</button>
|
|
<button type="button" :class="{'tab': true, 'tab-active': activeTab === 'settings'}" @click="activeTab = 'settings'">{{ _('Settings') }}</button>
|
|
</div>
|
|
|
|
<!-- Tab Panels -->
|
|
|
|
<!-- Dash Tab - Dashboard Summary -->
|
|
<div x-show="activeTab === 'dash'">
|
|
<div class="card bg-base-100 shadow-xl mb-4">
|
|
<div class="card-body">
|
|
<h2 class="card-title mb-2">{{ _('Trade Summary') }}</h2>
|
|
<div class="flex flex-col md:flex-row justify-center gap-4">
|
|
<div class="stats shadow-lg bg-base-300">
|
|
<div class="stat">
|
|
<div class="stat-title">{{ _('Your Reputation') }}</div>
|
|
<div class="stat-value">{{ request.user.reputation_score }}</div>
|
|
<div class="stat-desc">{{ _('Current Score') }}</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-title">{{ _('Your Trade Offers') }}</div>
|
|
<div class="stat-value">{{ dashboard_offers_paginated.page_obj.count }}</div>
|
|
<div class="stat-desc">{{ _('Active Offers') }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="stats shadow-lg bg-base-300">
|
|
<div class="stat">
|
|
<div class="stat-title">{{ _('Waiting on You') }}</div>
|
|
<div class="stat-value">{{ trade_acceptances_waiting_paginated.page_obj.count }}</div>
|
|
<div class="stat-desc">{{ _('Pending Requests') }}</div>
|
|
</div>
|
|
<div class="stat">
|
|
<div class="stat-title">{{ _('Waiting on Them') }}</div>
|
|
<div class="stat-value">{{ other_party_trade_acceptances_paginated.page_obj.count }}</div>
|
|
<div class="stat-desc">{{ _('Pending Responses') }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card bg-base-100 shadow-xl">
|
|
<div class="card-body">
|
|
<h2 class="card-title mb-2">{{ _('Quick Actions') }}</h2>
|
|
<div class="flex flex-wrap gap-4">
|
|
<a href="{% url 'trade_offer_create' %}" class="btn btn-primary grow">{{ _('Create New Offer') }}</a>
|
|
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary grow">{{ _('View All Offers') }}</a>
|
|
<a href="{% url 'account_logout' %}" class="btn btn-warning grow">{{ _('Sign Out') }}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Your Trade Offers Tab -->
|
|
<div x-show="activeTab === 'dashboard_offers'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=dashboard_offers')">
|
|
{% include 'trades/_trade_offer_list.html' with offers=dashboard_offers_paginated.object_list page_obj=dashboard_offers_paginated.page_obj %}
|
|
</div>
|
|
|
|
<!-- Waiting on You Tab -->
|
|
<div x-show="activeTab === 'waiting_on_you'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=waiting_acceptances')">
|
|
{% include 'trades/_trade_offer_list.html' with offers=trade_acceptances_waiting_paginated.object_list page_obj=trade_acceptances_waiting_paginated.page_obj %}
|
|
</div>
|
|
|
|
<!-- Waiting on Them Tab -->
|
|
<div x-show="activeTab === 'waiting_on_them'" x-data="tradeOffersPagination('{% url 'dashboard' %}?ajax_section=other_party_acceptances')">
|
|
{% include 'trades/_trade_offer_list.html' with offers=other_party_trade_acceptances_paginated.object_list page_obj=other_party_trade_acceptances_paginated.page_obj %}
|
|
</div>
|
|
|
|
<!-- Trade History Tab -->
|
|
<div x-show="activeTab === 'trade_history'">
|
|
<div class="divider">{{ _('Closed Offers') }} ({{ closed_offers_paginated.page_obj.count }})</div>
|
|
<div class="mb-8">
|
|
{% include 'trades/_trade_offer_list.html' with offers=closed_offers_paginated.object_list page_obj=closed_offers_paginated.page_obj %}
|
|
</div>
|
|
<div class="divider">{{ _('Closed Acceptances') }} ({{ closed_acceptances_paginated.page_obj.count }})</div>
|
|
<div class="mb-8">
|
|
{% include 'trades/_trade_offer_list.html' with offers=closed_acceptances_paginated.object_list page_obj=closed_acceptances_paginated.page_obj %}
|
|
</div>
|
|
<div class="divider">{{ _('Rejected by Them') }} ({{ rejected_by_them_paginated.page_obj.count }})</div>
|
|
<div class="mb-8">
|
|
{% include 'trades/_trade_offer_list.html' with offers=rejected_by_them_paginated.object_list page_obj=rejected_by_them_paginated.page_obj %}
|
|
</div>
|
|
<div class="divider">{{ _('Rejected by Me') }} ({{ rejected_by_me_paginated.page_obj.count }})</div>
|
|
<div class="mb-8">
|
|
{% include 'trades/_trade_offer_list.html' with offers=rejected_by_me_paginated.object_list page_obj=rejected_by_me_paginated.page_obj %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Profile Tab -->
|
|
<div x-show="activeTab === 'profile'">
|
|
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
|
{% with gravatar_profile=request.user.email|gravatar_profile_data %}
|
|
{% if gravatar_profile %}
|
|
<div class="hovercard-profile mb-4 text-center">
|
|
<img src="{{ gravatar_profile.thumbnailUrl }}" alt="{{ gravatar_profile.displayName|default:request.user.username }}" class="rounded-full mb-2 mx-auto" />
|
|
<a href="https://gravatar.com/profile/avatars" target="_blank" rel="noopener noreferrer" class="btn btn-primary mt-4">
|
|
Edit Avatar on Gravatar
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
|
</svg>
|
|
</a>
|
|
|
|
</div>
|
|
{% else %}
|
|
<p class="text-center">{{ _('No Gravatar profile data available.') }}</p>
|
|
{% endif %}
|
|
{% endwith %}
|
|
<div class="divider"></div>
|
|
<h2 class="text-base font-semibold pt-0">What is Gravatar?</h2>
|
|
<p class="mb-4 text-sm">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture. Many websites, including this one, use Gravatar to display your preferred avatar automatically.</p>
|
|
|
|
<h2 class="text-base font-semibold">How does it work?</h2>
|
|
<p class="mb-4 text-sm">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. If you don't have a Gravatar yet, you'll see a default image instead.</p>
|
|
|
|
<h2 class="text-base font-semibold">Is it safe? What about privacy?</h2>
|
|
<p class="mb-4 text-sm">Gravatar is completely optional, opt-in, and prioritizes your security and privacy. Your email is never visible to anyone and only a hashed version is shown on the page and sent to Gravatar, protecting your identity while ensuring that your email address is not exposed to bots or scrapers.</p>
|
|
|
|
<h2 class="text-base font-semibold">Want to update or add a Gravatar?</h2>
|
|
<p class="mb-4 text-sm">Go to Gravatar.com to set up or change your avatar. Your updates will appear here once saved!</p>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Friend Codes Tab -->
|
|
<div x-show="activeTab === 'friend_codes'">
|
|
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
|
{% if friend_codes %}
|
|
<ul class="space-y-2">
|
|
{% for code in friend_codes %}
|
|
<li class="w-full grid grid-cols-2 grid-rows-2 md:grid-cols-8 md:grid-rows-1 items-center {% if code.is_default %}bg-green-200 dark:bg-green-300 dark:text-base-100{% else %}bg-base-100 dark:bg-base-900 dark:text-white{% endif %} p-4 rounded shadow">
|
|
<div class="row-start-1 md:col-span-3">
|
|
<span class="align-baseline"><a href="{% url 'edit_friend_code' code.id %}" class="link link-hover">{{ code.in_game_name }}</a></span>
|
|
{% if code.is_default %}
|
|
<span class="badge badge-success ml-2 align-baseline">Default</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="row-start-2 col-start-1 md:row-start-1 md:col-span-3 {% if not code.is_default %}mr-4{% endif %}">
|
|
<span class="font-mono text-sm sm:text-base align-baseline">{{ code.friend_code }}</span>
|
|
</div>
|
|
<div class="row-start-2 col-start-2 md:row-start-1 md:col-span-2 flex justify-end space-x-2">
|
|
{% if not code.is_default %}
|
|
<form method="post" action="{% url 'change_default_friend_code' code.id %}">
|
|
{% csrf_token %}
|
|
<button type="submit" class="btn btn-secondary btn-sm align-baseline">Set Default</button>
|
|
</form>
|
|
{% endif %}
|
|
<a href="{% url 'delete_friend_code' code.id %}" class="btn btn-error btn-sm align-baseline">Delete</a>
|
|
</div>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p>You do not have any friend codes added yet.</p>
|
|
{% endif %}
|
|
|
|
<div class="mt-4 flex justify-end">
|
|
<a href="{% url 'add_friend_code' %}" class="btn btn-primary">Add a New Friend Code</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Tab -->
|
|
<div x-show="activeTab === 'settings'">
|
|
<div class="card card-border bg-base-100 shadow-lg mx-auto p-6 mb-4">
|
|
<form method="post" action="{% url 'dashboard' %}">
|
|
{% csrf_token %}
|
|
{{ settings_form|crispy }}
|
|
<div class="flex justify-end">
|
|
<button type="submit" name="update_settings" class="btn btn-success mt-4">{{ _('Save Settings') }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
function tradeOffersPagination(baseUrl) {
|
|
return {
|
|
baseUrl: baseUrl,
|
|
_hasChangePageListener: false,
|
|
loadPage(page) {
|
|
let url = new URL(this.baseUrl, window.location.origin);
|
|
url.searchParams.set("page", page);
|
|
fetch(url, {
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
})
|
|
.then(response => response.text())
|
|
.then(html => {
|
|
this.$el.innerHTML = html;
|
|
this.init();
|
|
});
|
|
},
|
|
init() {
|
|
if (!this._hasChangePageListener) {
|
|
this.$el.addEventListener('change-page', event => {
|
|
let page = event.detail.page;
|
|
this.loadPage(page);
|
|
});
|
|
this._hasChangePageListener = true;
|
|
}
|
|
this.$el.querySelectorAll("a.ajax-page-link").forEach(link => {
|
|
link.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
let page = link.getAttribute("data-page");
|
|
this.loadPage(page);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %} |