Merge branch 'group-manager' into 'master'

[Feature] Group Management - Async Join/Leave and Datatables

See merge request allianceauth/allianceauth!1784
This commit is contained in:
Ariel Rin
2026-01-20 05:04:15 +00:00
2 changed files with 220 additions and 13 deletions

View File

@@ -19,7 +19,7 @@
{% translate "Join Requests" %} {% translate "Join Requests" %}
{% if acceptrequests %} {% if acceptrequests %}
<span class="badge text-bg-secondary">{{ acceptrequests|length }}</span> <span id="acceptRequestsCounter" class="badge text-bg-secondary">{{ acceptrequests|length }}</span>
{% endif %} {% endif %}
</a> </a>
</li> </li>
@@ -30,7 +30,7 @@
{% translate "Leave Requests" %} {% translate "Leave Requests" %}
{% if leaverequests %} {% if leaverequests %}
<span class="badge text-bg-secondary">{{ leaverequests|length }}</span> <span id="leaveRequestsCounter" class="badge text-bg-secondary">{{ leaverequests|length }}</span>
{% endif %} {% endif %}
</a> </a>
</li> </li>
@@ -43,19 +43,19 @@
</li> </li>
{% endblock header_nav_collapse_left %} {% endblock header_nav_collapse_left %}
{% block content %} {% block content %}
<div class="tab-content"> <div class="tab-content">
<div id="add" class="tab-pane active"> <div id="add" class="tab-pane active">
{% if acceptrequests %} {% if acceptrequests %}
<div class="table-responsive"> <div>
<table class="table"> <table id="table-add" class="table table-responsive w-100">
<thead> <thead>
<tr> <tr>
<th>{% translate "Character" %}</th> <th>{% translate "Character" %}</th>
<th>{% translate "Organization" %}</th> <th>{% translate "Organization" %}</th>
<th>{% translate "Group" %}</th> <th>{% translate "Group" %}</th>
<th></th> <th></th>
<th>{% translate "Corporation" %}</th>
</tr> </tr>
</thead> </thead>
@@ -89,13 +89,24 @@
<td>{{ acceptrequest.group.name }}</td> <td>{{ acceptrequest.group.name }}</td>
<td class="text-end"> <td class="text-end">
<a href="{% url 'groupmanagement:accept_request' acceptrequest.id %}" class="btn btn-success"> <div class="spinner-border spinner-border-sm mt-2 btns-join-{{acceptrequest.id}} d-none" role="status">
<span class="sr-only">Loading...</span>
</div>
<a id="{{acceptrequest.id}}" class="btn btn-success join-accept btns-join-{{acceptrequest.id}}">
{% translate "Accept" %} {% translate "Accept" %}
</a> </a>
<a href="{% url 'groupmanagement:reject_request' acceptrequest.id %}" class="btn btn-danger"> <a id="{{acceptrequest.id}}" class="btn btn-danger join-reject btns-join-{{acceptrequest.id}}">
{% translate "Reject" %} {% translate "Reject" %}
</a> </a>
</td> </td>
<td>
{% if acceptrequest.main_char %}
{{ acceptrequest.main_char.corporation_name }}
{% else %}
{% translate "(unknown)" %}
{% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -111,14 +122,15 @@
{% if not show_leave_tab %} {% if not show_leave_tab %}
<div id="leave" class="tab-pane"> <div id="leave" class="tab-pane">
{% if leaverequests %} {% if leaverequests %}
<div class="table-responsive"> <div>
<table class="table"> <table id="table-rem" class="table table-responsive w-100">
<thead> <thead>
<tr> <tr>
<th>{% translate "Character" %}</th> <th>{% translate "Character" %}</th>
<th>{% translate "Organization" %}</th> <th>{% translate "Organization" %}</th>
<th>{% translate "Group" %}</th> <th>{% translate "Group" %}</th>
<th></th> <th></th>
<th>{% translate "Corporation" %}</th>
</tr> </tr>
</thead> </thead>
@@ -152,14 +164,23 @@
<td>{{ leaverequest.group.name }}</td> <td>{{ leaverequest.group.name }}</td>
<td class="text-end"> <td class="text-end">
<a href="{% url 'groupmanagement:leave_accept_request' leaverequest.id %}" class="btn btn-success"> <div class="spinner-border spinner-border-sm mt-2 btns-leave-{{leaverequest.id}} d-none" role="status">
<span class="sr-only">Loading...</span>
</div>
<a id="{{leaverequest.id}}" class="btn btn-success accept leave-accept btns-leave-{{leaverequest.id}}">
{% translate "Accept" %} {% translate "Accept" %}
</a> </a>
<a id="{{leaverequest.id}}" class="btn btn-danger reject leave-reject btns-leave-{{leaverequest.id}}">
<a href="{% url 'groupmanagement:leave_reject_request' leaverequest.id %}" class="btn btn-danger">
{% translate "Reject" %} {% translate "Reject" %}
</a> </a>
</td> </td>
<td>
{% if leaverequest.main_char %}
{{ leaverequest.main_char.corporation_name }}
{% else %}
{% translate "(unknown)" %}
{% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -172,3 +193,189 @@
{% endif %} {% endif %}
</div> </div>
{% endblock content %} {% endblock content %}
{% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %}
{% include "bundles/filterdropdown-js.html" %}
<script>
$(document).ready(function () {
let tableAdd = $("#table-add").DataTable({
filterDropDown: {
columns: [
{
idx: 4,
},
{
idx: 2,
}
],
bootstrap: true,
bootstrap_version: 5
},
columnDefs: [
{
target: 4,
visible: false,
},
],
paging: false,
responsive: true,
pageLength: -1
});
let tableRem = $("#table-rem").DataTable({
filterDropDown: {
columns: [
{
idx: 4,
},
{
idx: 2,
}
],
bootstrap: true,
bootstrap_version: 5
},
columnDefs: [
{
target: 4,
visible: false,
},
],
paging: false,
responsive: true,
pageLength: -1
});
// URL's for fetch requests
let acceptJoinURL = "{% url 'groupmanagement:accept_request' 0 %}";
acceptJoinURL = acceptJoinURL.substring(0, acceptJoinURL.length-2);
let rejectJoinURL = "{% url 'groupmanagement:reject_request' 0 %}";
rejectJoinURL = rejectJoinURL.substring(0, rejectJoinURL.length-2);
let acceptLeaveURL = "{% url 'groupmanagement:leave_accept_request' 0 %}";
acceptLeaveURL = acceptLeaveURL.substring(0, acceptLeaveURL.length-2);
let rejectLeaveURL = "{% url 'groupmanagement:leave_reject_request' 0 %}";
rejectLeaveURL = rejectLeaveURL.substring(0, rejectLeaveURL.length-2);
function removeRow(table, classLookup){
let btn = $(classLookup);
table.row($(btn[0]).parents('tr')).remove().draw();
}
function toggleButtons(classLookup){
let elems = document.querySelectorAll(classLookup);
elems.forEach(
function(e) {
e.classList.toggle('d-none');
}
);
}
function hitAuth(classLookup, table, URL){
toggleButtons(classLookup);
let output = fetch(URL)
.then(response => {
if (!response.ok) {
toggleButtons(classLookup);
return true;
}
removeRow(table, classLookup)
})
.catch(error => {
toggleButtons(classLookup);
return false;
});
toggleButtons(classLookup);
return output;
}
function decreaseCounterElement(elem){
count = Number(elem.innerText);
count -= 1;
if (!count){
elem.classList.add("d-none");
} else {
elem.innerText = count;
}
}
function decreaseCounter(id){
elem = document.getElementById(id);
if (elem){decreaseCounterElement(elem)}
}
function decreaseMenuCounter(){
decreaseCounter("globalNotificationCount");
let elem = document.querySelector("a[href='{% url "groupmanagement:management" %}']");
if (elem) {
let badge = elem.parentElement.querySelector("span");
if (badge){decreaseCounterElement(badge)}
if (elem.parentElement.parentElement.parentElement.tagName === "LI"){
let folderBadge = elem.parentElement.parentElement.parentElement.querySelector("span");
if (folderBadge){decreaseCounterElement(folderBadge)}
}
}
}
let acceptJoinButtons = document.querySelectorAll(".join-accept");
acceptJoinButtons.forEach(function(elem) {
elem.addEventListener("click", function(event) {
url = `${acceptJoinURL}${event.target.id}/`
let elemClass = `.btns-join-${event.target.id}`
if (hitAuth(elemClass, tableAdd, url)){
decreaseCounter("acceptRequestsCounter")
decreaseMenuCounter()
}
});
});
let rejectJoinButtons = document.querySelectorAll(".join-reject");
rejectJoinButtons.forEach(function(elem) {
elem.addEventListener("click", function(event) {
url = `${rejectJoinURL}${event.target.id}/`
let elemClass = `.btns-join-${event.target.id}`
if (hitAuth(elemClass, tableAdd, url)){
decreaseCounter("acceptRequestsCounter")
decreaseMenuCounter()
}
});
});
let acceptLeaveButtons = document.querySelectorAll(".leave-accept");
acceptLeaveButtons.forEach(function(elem) {
elem.addEventListener("click", function(event) {
url = `${acceptLeaveURL}${event.target.id}/`
let elemClass = `.btns-leave-${event.target.id}`
if (hitAuth(elemClass, tableRem, url)){
decreaseCounter("leaveRequestsCounter")
decreaseMenuCounter()
}
});
});
let rejectLeaveButtons = document.querySelectorAll(".leave-reject");
rejectLeaveButtons.forEach(function(elem) {
elem.addEventListener("click", function(event) {
url = `${rejectLeaveURL}${event.target.id}/`
let elemClass = `.btns-leave-${event.target.id}`
if (hitAuth(elemClass, tableRem, url)){
decreaseCounter("leaveRequestsCounter")
decreaseMenuCounter()
}
});
});
// Filter Dropdown sets widths so lets remove them when we tab change so they actually show.
$('a[data-bs-toggle="tab"]').on('shown.bs.tab', function (e) {
let elems = document.querySelectorAll(".form-select");
elems.forEach(
function(e) {
e.style.maxWidth = "";
}
);
});
});
</script>
{% endblock extra_javascript %}
{% block extra_css %}
{% include 'bundles/datatables-css-bs5.html' %}
{% endblock %}

View File

@@ -20,7 +20,7 @@ $(document).ready(() => {
if (badges.length > 0 && notificationCount > 0) { if (badges.length > 0 && notificationCount > 0) {
const notificationBadge = document.createElement('span'); const notificationBadge = document.createElement('span');
notificationBadge.id = "globalNotificationCount";
notificationBadge.classList.add( notificationBadge.classList.add(
'badge', 'badge',
'text-bg-danger', 'text-bg-danger',