[UPDT] EMPLOYEE: Updated pagination to document request
This commit is contained in:
@@ -13,7 +13,7 @@ from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from base.methods import choosesubordinates, is_reportingmanager
|
||||
from employee.filters import DocumentRequestFilter
|
||||
from employee.filters import DocumentPipelineFilter, DocumentRequestFilter
|
||||
from employee.models import Employee
|
||||
from horilla.decorators import manager_can_enter
|
||||
from horilla_documents.forms import DocumentForm
|
||||
@@ -21,7 +21,12 @@ from horilla_documents.forms import DocumentRejectCbvForm as RejectForm
|
||||
from horilla_documents.forms import DocumentRequestForm, DocumentUpdateForm
|
||||
from horilla_documents.models import Document, DocumentRequest
|
||||
from horilla_views.cbv_methods import login_required
|
||||
from horilla_views.generic.cbv.views import HorillaFormView, HorillaNavView
|
||||
from horilla_views.generic.cbv.pipeline import Pipeline
|
||||
from horilla_views.generic.cbv.views import (
|
||||
HorillaFormView,
|
||||
HorillaListView,
|
||||
HorillaNavView,
|
||||
)
|
||||
from notifications.signals import notify
|
||||
|
||||
|
||||
@@ -179,6 +184,7 @@ class DocumentUploadForm(HorillaFormView):
|
||||
)
|
||||
except:
|
||||
pass
|
||||
form.instance.status = "requested"
|
||||
form.save()
|
||||
return HttpResponse("<script>window.location.reload();</script>")
|
||||
return super().form_valid(form)
|
||||
@@ -239,3 +245,65 @@ class DocumentRequestNav(HorillaNavView):
|
||||
filter_instance = DocumentRequestFilter()
|
||||
filter_form_context_name = "form"
|
||||
search_swap_target = "#view-container"
|
||||
|
||||
|
||||
class DocumentRequestPipelineView(Pipeline):
|
||||
"""
|
||||
Pipeline view for document request
|
||||
"""
|
||||
|
||||
model = Document
|
||||
filter_class = DocumentRequestFilter
|
||||
grouper = "document_request_id"
|
||||
template_name = "cbv/documents/pipeline.html"
|
||||
|
||||
allowed_fields = [
|
||||
{
|
||||
"field": "document_request_id",
|
||||
"model": DocumentRequest,
|
||||
"filter": DocumentPipelineFilter,
|
||||
"url": reverse_lazy("document-request-list"),
|
||||
"parameters": [
|
||||
"document_request_id={pk}",
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"action": _("Edit"),
|
||||
"attrs": """
|
||||
class="oh-dropdown__link oh-dropdown__link"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#objectCreateModal"
|
||||
hx-get="{get_edit_url}"
|
||||
hx-target="#objectCreateModalTarget"
|
||||
""",
|
||||
},
|
||||
{
|
||||
"action": _("Delete"),
|
||||
"attrs": """
|
||||
class="oh-dropdown__link oh-dropdown__link"
|
||||
hx-confirm="Are you sure you want to delete this document request?"
|
||||
hx-post="{get_delete_url}"
|
||||
hx-target="body"
|
||||
""",
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
class DocumentListView(HorillaListView):
|
||||
"""
|
||||
List view for document request
|
||||
"""
|
||||
|
||||
model = Document
|
||||
filter_class = DocumentRequestFilter
|
||||
template_name = "cbv/documents/document_list.html"
|
||||
filter_keys_to_remove = ["document_request_id"]
|
||||
|
||||
def get_queryset(self, queryset=None, filtered=False, *args, **kwargs):
|
||||
queryset = super().get_queryset(queryset, filtered, *args, **kwargs)
|
||||
queryset = queryset.filter(
|
||||
document_request_id__pk=self.request.GET.get("document_request_id")
|
||||
)
|
||||
return queryset
|
||||
|
||||
@@ -24,7 +24,7 @@ from employee.models import (
|
||||
)
|
||||
from horilla.filters import FilterSet, HorillaFilterSet, filter_by_name
|
||||
from horilla.horilla_middlewares import _thread_locals
|
||||
from horilla_documents.models import Document
|
||||
from horilla_documents.models import Document, DocumentRequest
|
||||
from horilla_views.templatetags.generic_template_filters import getattribute
|
||||
|
||||
|
||||
@@ -298,6 +298,25 @@ class DocumentRequestFilter(FilterSet):
|
||||
]
|
||||
|
||||
|
||||
class DocumentPipelineFilter(HorillaFilterSet):
|
||||
"""
|
||||
Filter set class for TaxBracket model.
|
||||
"""
|
||||
|
||||
search = django_filters.CharFilter(method="search_method")
|
||||
|
||||
class Meta:
|
||||
model = DocumentRequest
|
||||
fields = "__all__"
|
||||
|
||||
def search_method(self, queryset, _, value):
|
||||
"""
|
||||
This method is used to search
|
||||
"""
|
||||
|
||||
return queryset.filter(title__icontains=value).distinct()
|
||||
|
||||
|
||||
class DisciplinaryActionFilter(FilterSet):
|
||||
"""
|
||||
Custom filter for Disciplinary Action.
|
||||
|
||||
274
employee/templates/cbv/documents/document_list.html
Normal file
274
employee/templates/cbv/documents/document_list.html
Normal file
@@ -0,0 +1,274 @@
|
||||
{% load static i18n generic_template_filters %}
|
||||
|
||||
<div id="{{view_id}}" class="hlv-container">
|
||||
|
||||
<script>
|
||||
if (!$(".HTV").length) {
|
||||
reloadMessage(null);
|
||||
}
|
||||
</script>
|
||||
<button
|
||||
class="reload-record"
|
||||
id="{{view_id}}Reload"
|
||||
hidden
|
||||
hx-get="{{request.path}}?{{saved_filters.urlencode}}"
|
||||
hx-target="#{{view_id}}"
|
||||
hx-swap="outerHTML"
|
||||
hx-on:click="htmxLoadIndicator(this);"
|
||||
></button>
|
||||
|
||||
{% if show_filter_tags %} {% include "generic/filter_tags.html" %} {% endif %}
|
||||
|
||||
{% if queryset|length %}
|
||||
|
||||
<div class="bg-white p-5 pe-2 pt-3 rounded-md shadow-card relative">
|
||||
<div
|
||||
class="max-h-[600px] overflow-hidden overflow-y-auto overflow-x-auto heightContainer"
|
||||
>
|
||||
<div class="bg-white rounded-xl shadow-sm overflow-hidden">
|
||||
<div
|
||||
class="hidden md:grid grid-cols-[60px_1fr_120px_120px_180px] gap-4 px-6 py-4 bg-slate-50 border-b border-slate-200 text-xs font-semibold uppercase tracking-wider text-slate-500 items-center"
|
||||
>
|
||||
<div></div>
|
||||
<div>{% trans "Document" %}</div>
|
||||
<div>{% trans "Status" %}</div>
|
||||
<div>{% trans "Date" %}</div>
|
||||
<div>{% trans "Actions" %}</div>
|
||||
</div>
|
||||
|
||||
{% for document in queryset %}
|
||||
<div
|
||||
class="grid md:grid-cols-[60px_1fr_120px_120px_180px] gap-4 px-6 py-4 border-b border-slate-100 items-center hover:bg-slate-50 cursor-pointer
|
||||
{% if document.document %}
|
||||
{% if document.status == "approved" %}row-status--yellow
|
||||
{% elif document.status == 'rejected' %}row-status--red
|
||||
{% elif document.status == 'requested' %}row-status--blue
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
"
|
||||
hx-get="{% url 'view-file' document.id %}"
|
||||
hx-target="#viewFile"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#viewFileModal"
|
||||
id="document{{document.id}}"
|
||||
>
|
||||
{% if document.document %}
|
||||
{% if document.status == "approved" %}
|
||||
<div
|
||||
class="w-8 h-8 rounded-md bg-green-100 text-green-700 flex items-center justify-center shrink-0"
|
||||
>
|
||||
<ion-icon name="checkmark-outline"></ion-icon>
|
||||
</div>
|
||||
{% elif document.status == 'rejected' %}
|
||||
<div
|
||||
class="w-8 h-8 rounded-md bg-red-100 text-red-600 flex items-center justify-center shrink-0"
|
||||
>
|
||||
<ion-icon name="warning-outline"></ion-icon>
|
||||
</div>
|
||||
{% else %}
|
||||
<div
|
||||
class="w-8 h-8 rounded-md bg-blue-100 text-blue-600 flex items-center justify-center shrink-0"
|
||||
>
|
||||
<ion-icon name="document-text-outline"></ion-icon>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div
|
||||
class="w-8 h-8 rounded-md bg-amber-100 text-amber-700 flex items-center justify-center shrink-0"
|
||||
>
|
||||
<ion-icon name="cloud-upload-outline"></ion-icon>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="w-full">
|
||||
<div class="title-wrapper">
|
||||
<div
|
||||
onclick="event.stopPropagation()"
|
||||
class="font-semibold text-slate-900 mb-1 hover:bg-slate-100 px-1 rounded inline-block {% if document.document_request_id %} cursor-pointer {% else %} cursor-text title-display {% endif %}">
|
||||
{{ document.title }} -- {{document.employee_id.get_full_name}}
|
||||
</div>
|
||||
|
||||
<input type="text"
|
||||
name="title"
|
||||
data-id="{{document.id}}"
|
||||
class="hidden font-semibold text-slate-900 mb-1 cursor-text border border-slate-300 rounded px-2 py-1 focus:outline-none focus:ring focus:ring-blue-300 w-full title-input"
|
||||
value="{{ document.title }}"
|
||||
onclick="event.stopPropagation();"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="text-sm font-medium px-3 py-1 rounded-xl text-center {% if document.document %} {% if document.status == 'approved' %}bg-green-100 text-green-700 {% elif document.status == 'rejected' %}bg-red-100 text-red-700 {% elif document.status == 'requested' %}bg-blue-100 text-blue-700 {% endif %} {% else %} bg-amber-100 text-amber-700 {% endif %}"
|
||||
>
|
||||
{% if document.document %} {{document.status}}
|
||||
{% else %} {% trans "No Document" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-slate-500 font-medium">
|
||||
{% if document.issue_date %}<span class="dateformat_changer"> {{document.issue_date}}</span>{% else %} - {% endif %}
|
||||
</div>
|
||||
|
||||
<div class="flex gap-1 w-full">
|
||||
{% if document.document %}
|
||||
{% if perms.horilla_document.change_documentrequest %}
|
||||
{% if document.status == "approved" %}
|
||||
<button
|
||||
class="oh-btn flex-1 text-base bg-green-100 text-green-700 hover:bg-green-200 hover:scale-105 opacity-50 cursor-not-allowed"
|
||||
title="{% trans 'Approve' %}"
|
||||
>
|
||||
<ion-icon class="me-1" name="checkmark-outline"></ion-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="oh-btn flex-1 text-base bg-red-100 text-red-600 hover:bg-red-200 hover:scale-105 transition"
|
||||
hx-get="{% url 'document-reject' document.id %}"
|
||||
hx-target="#genericModalBody"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#genericModal"
|
||||
title="{% trans 'Reject' %}"
|
||||
onclick="event.stopPropagation()"
|
||||
>
|
||||
<ion-icon class="me-1" name="close-outline"></ion-icon>
|
||||
</button>
|
||||
{% else %}
|
||||
<button
|
||||
class="oh-btn flex-1 text-base bg-green-100 text-green-700 hover:bg-green-200 hover:scale-105"
|
||||
hx-get="{% url 'document-approve' document.id %}"
|
||||
title="{% trans 'Approve' %}"
|
||||
onclick="event.stopPropagation()"
|
||||
>
|
||||
<ion-icon class="me-1" name="checkmark-outline"></ion-icon>
|
||||
</button>
|
||||
<button
|
||||
class="oh-btn flex-1 text-base bg-red-100 text-red-600 hover:bg-red-200 hover:scale-105 transition opacity-50 cursor-not-allowed"
|
||||
title="{% trans 'Reject' %}"
|
||||
>
|
||||
<ion-icon class="me-1" name="close-outline"></ion-icon>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<button
|
||||
class="oh-btn flex-1 text-base bg-amber-100 text-amber-600 hover:bg-amber-200 hover:scale-105 transition"
|
||||
title="{% trans 'Upload Document' %}"
|
||||
hx-get="{% url 'file-upload' document.id %}"
|
||||
hx-target="#genericModalBody"
|
||||
data-document-id="{{ document.id }}"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#genericModal"
|
||||
onclick="event.stopPropagation()"
|
||||
>
|
||||
<ion-icon
|
||||
class="me-1"
|
||||
name="cloud-upload-outline"
|
||||
></ion-icon>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
{% if not document.document_request_id or perms.horilla_document.change_documentrequest %}
|
||||
<form
|
||||
hx-confirm="{% trans 'Are you sure you want to delete this Document Request?' %}"
|
||||
hx-post="{% url 'document-delete' document.id %}"
|
||||
hx-target="#document{{document.id}}"
|
||||
hx-swap="outerHTML"
|
||||
hx-on-htmx-after-request="setTimeout(() => { reloadMessage(); }, 300);"
|
||||
onclick="event.stopPropagation()"
|
||||
>
|
||||
{% csrf_token %}
|
||||
<button
|
||||
type="submit"
|
||||
class="oh-btn text-base bg-slate-100 text-slate-500 hover:scale-105 hover:text-red-500 transition"
|
||||
title="{% trans 'Delete' %}"
|
||||
>
|
||||
<ion-icon class="me-1" name="trash-outline"></ion-icon>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% if queryset.has_previous or queryset.has_next %}
|
||||
<div
|
||||
class="flex justify-between items-center mt-4 w-full inset-0"
|
||||
>
|
||||
<p class="text-xs text-[#666]">{% trans "Page" %} {{ queryset.number }} {% trans "of" %} {{ queryset.paginator.num_pages }}</p>
|
||||
<div class="flex gap-3 text-[#666] items-center">
|
||||
{% if queryset.has_previous %}
|
||||
<button
|
||||
class="text-xs hover:text-primary-600 transition duration-300"
|
||||
hx-target="#{{view_id}}"
|
||||
hx-get="{{search_url}}?{{request.GET.urlencode}}&page={{ queryset.previous_page_number }}"
|
||||
>
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{{ queryset.number }} / {{ queryset.paginator.num_pages }}
|
||||
{% if queryset.has_next %}
|
||||
<button
|
||||
class="text-xs hover:text-primary-600 transition duration-300"
|
||||
hx-target="#{{view_id}}"
|
||||
hx-get="{{search_url}}?{{request.GET.urlencode}}&page={{ queryset.next_page_number }}"
|
||||
>
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="oh-wrapper h-full" align="center" >
|
||||
<div class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card h-[70%]">
|
||||
<div class="flex flex-col items-center justify-center h-full">
|
||||
<img src="/static/horilla_theme/assets/img/no-records.svg" alt="" width="300" class="mb-4">
|
||||
<p class="text-[#666] mb-5">{% trans "No Documents available at the moment" %}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
reloadSelectedCount($('#count_{{view_id}}'),'{{selected_instances_key_id}}');
|
||||
reloadSelectedCount($('.count_{{view_id}}'),'{{selected_instances_key_id}}');
|
||||
{% if records_count_in_tab %}
|
||||
var tabId = $("#{{view_id}}").closest(".oh-tabs__content").attr("id");
|
||||
var badge = $(`#badge-${tabId}`);
|
||||
var count = "{{queryset.paginator.count}}";
|
||||
var label = badge.attr("data-badge-label") || "";
|
||||
var title = count + " " + label;
|
||||
badge.html(count);
|
||||
badge.attr("title", title);
|
||||
{% endif %}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
$("ul[data-search-url] a").click(function (e) {
|
||||
e.preventDefault();
|
||||
const url = $(this).attr("hx-get")
|
||||
const $urlObj = $('<a>', { href: url });
|
||||
const searchParams = new URLSearchParams($urlObj[0].search);
|
||||
|
||||
let lastPageParam = null;
|
||||
let lastPageValue = 1;
|
||||
|
||||
searchParams.forEach((value, param) => {
|
||||
if (param === "page") {
|
||||
lastPageParam = param;
|
||||
lastPageValue = value.split(",").pop();
|
||||
}
|
||||
});
|
||||
|
||||
form = $(`form[hx-get="{{search_url}}"]`)
|
||||
pageInput = form.find("#pageInput")
|
||||
pageInput.attr("name",lastPageParam)
|
||||
pageInput.attr("value",lastPageValue)
|
||||
|
||||
});
|
||||
</script>
|
||||
65
employee/templates/cbv/documents/pipeline.html
Normal file
65
employee/templates/cbv/documents/pipeline.html
Normal file
@@ -0,0 +1,65 @@
|
||||
{% load i18n generic_template_filters %}
|
||||
|
||||
<div id="{{view_id}}">
|
||||
{% for group in groups %}
|
||||
<div id="{{selected_instances_key_name}}{{ group.id }}" data-ids="[]"></div>
|
||||
<div class="oh-tabs__movable-area mb-2">
|
||||
<div class="oh-tabs__movable">
|
||||
<div class="oh-tabs__movable-header" style="cursor:pointer;" onclick="$(this).parent().find('.oh-tabs__movable-body').toggleClass('d-none');">
|
||||
<span class"oh-tabs__movable-title">{{group}}</span>
|
||||
|
||||
<div onclick="event.stopPropagation()" class="oh-dropdown" x-data="{open: false}">
|
||||
<div style="cursor: pointer;" onclick="event.stopPropagation()" class="oh-dropdown" x-data="{open: false}">
|
||||
<button
|
||||
class="oh-btn oh-stop-prop oh-btn--transparent oh-accordion-meta__btn"
|
||||
@click="open = !open"
|
||||
@click.outside="open = false"
|
||||
title="Actions"
|
||||
style="color: #e54f38;"
|
||||
>
|
||||
<ion-icon
|
||||
name="ellipsis-vertical"
|
||||
role="img"
|
||||
class="md hydrated"
|
||||
aria-label="ellipsis vertical"
|
||||
></ion-icon>
|
||||
</button>
|
||||
<div
|
||||
class="oh-dropdown__menu oh-dropdown__menu--right mwidth"
|
||||
x-show="open"
|
||||
style="display: none; min-width:135px !important;"
|
||||
>
|
||||
<ul class="oh-dropdown__items">
|
||||
{% for action in actions %}
|
||||
{% if action.accessibility|accessibility:group %}
|
||||
<li class="oh-dropdown__item">
|
||||
<a {{action.attrs|format:group|safe}}>{{action.action}}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="oh-tabs__movable-body position-relative d-none"
|
||||
hx-get="{{url}}?{% for parameter in parameters %}{{parameter|format:group}}&{% endfor %}{{request.GET.urlencode}}"
|
||||
hx-trigger="load"
|
||||
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script>
|
||||
var tabId = $("#{{view_id}}").closest(".oh-tabs__content").attr("id");
|
||||
var badge = $(`#badge-${tabId}`);
|
||||
var count = "{{groups|length}}";
|
||||
var label = badge.attr("data-badge-label") || "";
|
||||
var title = count + " " + label;
|
||||
badge.html(count);
|
||||
badge.attr("title", title);
|
||||
|
||||
</script>
|
||||
@@ -430,11 +430,11 @@ urlpatterns = [
|
||||
views.document_request_view,
|
||||
name="document-request-view",
|
||||
),
|
||||
path(
|
||||
"document-request-filter-view",
|
||||
views.document_filter_view,
|
||||
name="document-request-filter-view",
|
||||
),
|
||||
# path(
|
||||
# "document-request-filter-view",
|
||||
# views.document_filter_view,
|
||||
# name="document-request-filter-view",
|
||||
# ),
|
||||
path(
|
||||
"document-request-create",
|
||||
document_request.DocumentRequestCreateForm.as_view(),
|
||||
@@ -450,6 +450,16 @@ urlpatterns = [
|
||||
document_request.DocumentRequestNav.as_view(),
|
||||
name="document-request-nav-cbv",
|
||||
),
|
||||
path(
|
||||
"document-request-filter-view",
|
||||
document_request.DocumentRequestPipelineView.as_view(),
|
||||
name="document-request-filter-view",
|
||||
),
|
||||
path(
|
||||
"document-request-list",
|
||||
document_request.DocumentListView.as_view(),
|
||||
name="document-request-list",
|
||||
),
|
||||
path(
|
||||
"document-request-update/<int:pk>/",
|
||||
document_request.DocumentRequestCreateForm.as_view(),
|
||||
|
||||
Reference in New Issue
Block a user