[UPDT] HORILLA: Bring up changes/fix from the master branch

This commit is contained in:
Horilla
2025-07-04 14:52:39 +05:30
parent b0e067a95b
commit e9f4756e89
33 changed files with 481 additions and 315 deletions

View File

@@ -10,6 +10,7 @@ from django.db.models import Count, Q
from django.db.models.query import QuerySet
from django.urls import resolve, reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from base.methods import get_subordinates
@@ -70,16 +71,6 @@ class ProjectDetailView(HorillaDetailedView):
model = Project
title = _("Details")
header = {"title": "title", "subtitle": "", "avatar": "get_avatar"}
body = [
(_("Manager"), "manager"),
(_("Members"), "get_members"),
(_("Status"), "status_column"),
(_("No of Tasks"), "task_count"),
(_("Start date"), "start_date"),
(_("End date"), "end_date"),
(_("Document"), "get_document_html"),
(_("Description"), "description"),
]
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -106,3 +97,17 @@ class ProjectDetailView(HorillaDetailedView):
queryset = super().get_queryset()
queryset = queryset.annotate(task_count=Count("task"))
return queryset
@cached_property
def body(self):
get_field = self.model()._meta.get_field
return [
(get_field("managers").verbose_name, "get_managers"),
(get_field("members").verbose_name, "get_members"),
(get_field("status").verbose_name, "get_status_display"),
(_("No of Tasks"), "task_count"),
(get_field("start_date").verbose_name, "start_date"),
(get_field("end_date").verbose_name, "end_date"),
(get_field("document").verbose_name, "get_document_html"),
(get_field("description").verbose_name, "description"),
]

View File

@@ -56,7 +56,12 @@ class ProjectsNavView(HorillaNavView):
Nav bar
"""
filter_form_context_name = "form"
filter_instance = ProjectFilter()
search_swap_target = "#listContainer"
group_by_fields = ["status", "is_active"]
template_name = "cbv/projects/project_nav.html"
filter_body_template = "cbv/projects/filter.html"
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -139,16 +144,6 @@ class ProjectsNavView(HorillaNavView):
hx-get="{reverse('create-project')}"
"""
group_by_fields = [
("status", _("Status")),
("is_active", _("Is active")),
]
nav_title = Project._meta.verbose_name_plural
filter_instance = ProjectFilter()
filter_form_context_name = "form"
filter_body_template = "cbv/projects/filter.html"
search_swap_target = "#listContainer"
@method_decorator(login_required, name="dispatch")
@method_decorator(
@@ -159,6 +154,9 @@ class ProjectsList(HorillaListView):
Projects list view
"""
model = Project
filter_class = ProjectFilter
def get_queryset(self):
queryset = super().get_queryset()
if not self.request.user.has_perm("project.view_project"):
@@ -178,26 +176,26 @@ class ProjectsList(HorillaListView):
@cached_property
def columns(self):
instance = self.model()
get_field = self.model()._meta.get_field
return [
(instance._meta.get_field("title").verbose_name, "title"),
(instance._meta.get_field("managers").verbose_name, "get_managers"),
(instance._meta.get_field("members").verbose_name, "get_members"),
(instance._meta.get_field("status").verbose_name, "status_column"),
(instance._meta.get_field("start_date").verbose_name, "start_date"),
(instance._meta.get_field("end_date").verbose_name, "end_date"),
(instance._meta.get_field("document").verbose_name, "get_document_html"),
(instance._meta.get_field("description").verbose_name, "get_description"),
(get_field("title").verbose_name, "title"),
(get_field("managers").verbose_name, "get_managers"),
(get_field("members").verbose_name, "get_members"),
(get_field("status").verbose_name, "get_status_display"),
(get_field("start_date").verbose_name, "start_date"),
(get_field("end_date").verbose_name, "end_date"),
(get_field("document").verbose_name, "get_document_html"),
(get_field("description").verbose_name, "get_description"),
]
model = Project
filter_class = ProjectFilter
sortby_mapping = [
("Project", "title"),
("Start Date", "start_date"),
("End Date", "end_date"),
]
@cached_property
def sortby_mapping(self):
get_field = self.model()._meta.get_field
return [
(get_field("title").verbose_name, "title"),
(get_field("start_date").verbose_name, "start_date"),
(get_field("end_date").verbose_name, "end_date"),
]
row_status_indications = [
(
@@ -281,8 +279,8 @@ class ProjectFormView(HorillaFormView):
form view for create project
"""
form_class = ProjectForm
model = Project
form_class = ProjectForm
new_display_title = _("Create") + " " + model._meta.verbose_name
def __init__(self, **kwargs):
@@ -393,7 +391,7 @@ class ProjectCardView(HorillaCardView):
details = {
"image_src": "get_avatar",
"title": "{get_task_badge_html}",
"subtitle": "Status : {status_column} <br> Start date : {start_date} <br>End date : {end_date}",
"subtitle": "Status : {get_status_display} <br> Start date : {start_date} <br>End date : {end_date}",
}
card_status_class = "status-{status}"

View File

@@ -12,6 +12,7 @@ from django.http import HttpResponse
from django.shortcuts import render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from base.methods import get_subordinates
@@ -52,6 +53,7 @@ class TaskListView(HorillaListView):
model = Task
filter_class = TaskAllFilter
action_method = "actions"
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -81,26 +83,30 @@ class TaskListView(HorillaListView):
)
return queryset.distinct()
columns = [
(_("Task"), "title"),
(_("Project"), "project"),
(_("Stage"), "stage"),
(_("Mangers"), "get_managers"),
(_("Members"), "get_members"),
(_("End Date"), "end_date"),
(_("Status"), "status_column"),
(_("Description"), "description"),
]
@cached_property
def columns(self):
get_field = self.model()._meta.get_field
return [
(get_field("title").verbose_name, "title"),
(get_field("project").verbose_name, "project"),
(get_field("stage").verbose_name, "stage"),
(get_field("task_managers").verbose_name, "get_managers"),
(get_field("task_members").verbose_name, "get_members"),
(get_field("end_date").verbose_name, "end_date"),
(get_field("status").verbose_name, "get_status_display"),
(get_field("description").verbose_name, "get_description"),
]
sortby_mapping = [
("Task", "title"),
("Project", "project__title"),
("Stage", "stage"),
("End Date", "end_date"),
("Status", "status"),
]
action_method = "actions"
@cached_property
def sortby_mapping(self):
get_field = self.model()._meta.get_field
return [
(get_field("title").verbose_name, "title"),
(get_field("project").verbose_name, "project__title"),
(get_field("stage").verbose_name, "stage"),
(get_field("end_date").verbose_name, "end_date"),
(get_field("status").verbose_name, "status"),
]
row_status_indications = [
(
@@ -164,11 +170,15 @@ class TasksNavBar(HorillaNavView):
navbar of teh page
"""
nav_title = _("Tasks")
filter_instance = TaskAllFilter()
group_by_fields = [
"project",
"stage",
"status",
]
filter_form_context_name = "form"
filter_body_template = "cbv/tasks/task_filter.html"
filter_instance = TaskAllFilter()
search_swap_target = "#listContainer"
filter_body_template = "cbv/tasks/task_filter.html"
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -237,12 +247,6 @@ class TasksNavBar(HorillaNavView):
},
]
group_by_fields = [
("project", _("Project")),
("stage", _("Stage")),
("status", _("Status")),
]
@method_decorator(login_required, name="dispatch")
class TaskCreateForm(HorillaFormView):
@@ -250,10 +254,10 @@ class TaskCreateForm(HorillaFormView):
Form view for create and update tasks
"""
form_class = TaskAllForm
model = Task
form_class = TaskAllForm
template_name = "cbv/tasks/task_form.html"
new_display_title = _("Create Task")
new_display_title = _("Create") + " " + model._meta.verbose_name
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -407,22 +411,23 @@ class TaskDetailView(HorillaDetailedView):
model = Task
title = _("Task Details")
action_method = "detail_view_actions"
header = {"title": "title", "subtitle": "project", "avatar": "get_avatar"}
body = [
(_("Task"), "title"),
(_("Project"), "project"),
(_("Stage"), "stage"),
(_("Task Mangers"), "get_managers"),
(_("Task Members"), "get_members"),
(_("Status"), "status_column"),
(_("End Date"), "end_date"),
(_("Description"), "description"),
(_("Document"), "document_col", True),
]
action_method = "detail_view_actions"
@cached_property
def body(self):
get_field = self.model()._meta.get_field
return [
(get_field("title").verbose_name, "title"),
(get_field("project").verbose_name, "project"),
(get_field("stage").verbose_name, "stage"),
(get_field("task_managers").verbose_name, "get_managers"),
(get_field("task_members").verbose_name, "get_members"),
(get_field("status").verbose_name, "get_status_display"),
(get_field("end_date").verbose_name, "end_date"),
(get_field("description").verbose_name, "description"),
(get_field("document").verbose_name, "document_col", True),
]
@method_decorator(login_required, name="dispatch")

View File

@@ -11,6 +11,7 @@ from django.http import HttpResponse
from django.shortcuts import render
from django.urls import resolve, reverse
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from employee.models import Employee
@@ -52,7 +53,22 @@ class TimeSheetNavView(HorillaNavView):
Nav bar
"""
filter_form_context_name = "form"
filter_instance = TimeSheetFilter()
search_swap_target = "#listContainer"
template_name = "cbv/timesheet/timesheet_nav.html"
filter_body_template = "cbv/timesheet/filter.html"
group_by_fields = [
"employee_id",
"project_id",
"date",
"status",
"employee_id__employee_work_info__reporting_manager_id",
"employee_id__employee_work_info__department_id",
"employee_id__employee_work_info__job_position_id",
"employee_id__employee_work_info__employee_type_id",
"employee_id__employee_work_info__company_id",
]
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
@@ -105,27 +121,6 @@ class TimeSheetNavView(HorillaNavView):
hx-get="{reverse('create-time-sheet')}"
"""
group_by_fields = [
("employee_id", _("Employee")),
("project_id", _("Project")),
("date", _("Date")),
("status", _("Status")),
(
"employee_id__employee_work_info__reporting_manager_id",
_("Reporting Manager"),
),
("employee_id__employee_work_info__department_id", _("Department")),
("employee_id__employee_work_info__job_position_id", _("Job Position")),
("employee_id__employee_work_info__employee_type_id", _("Employement Type")),
("employee_id__employee_work_info__company_id", _("Company")),
]
nav_title = _("Time Sheet")
filter_instance = TimeSheetFilter()
filter_form_context_name = "form"
filter_body_template = "cbv/timesheet/filter.html"
search_swap_target = "#listContainer"
@method_decorator(login_required, name="dispatch")
@method_decorator(
@@ -136,6 +131,9 @@ class TimeSheetList(HorillaListView):
Time sheet list view
"""
model = TimeSheet
filter_class = TimeSheetFilter
def get_queryset(self):
queryset = super().get_queryset()
if not self.request.user.has_perm("project.view_timesheet"):
@@ -153,26 +151,37 @@ class TimeSheetList(HorillaListView):
self.search_url = reverse("time-sheet-list")
self.action_method = "actions"
model = TimeSheet
filter_class = TimeSheetFilter
@cached_property
def columns(self):
get_field = self.model()._meta.get_field
return [
(
get_field("employee_id").verbose_name,
"employee_id",
"employee_id__get_avatar",
),
(get_field("project_id").verbose_name, "project_id"),
(get_field("task_id").verbose_name, "task_id"),
(get_field("date").verbose_name, "date"),
(get_field("time_spent").verbose_name, "time_spent"),
(get_field("status").verbose_name, "get_status_display"),
(get_field("description").verbose_name, "description"),
]
columns = [
(_("Employee"), "employee_id", "employee_id__get_avatar"),
(_("Project"), "project_id"),
(_("Task"), "task_id"),
(_("Date"), "date"),
(_("Time Spent"), "time_spent"),
(_("Status"), "status_column"),
(_("Description"), "description"),
]
sortby_mapping = [
("Employee", "employee_id__employee_first_name", "employee_id__get_avatar"),
("Project", "project_id__title"),
("Task", "task_id__title"),
("Time Spent", "time_spent"),
("Date", "date"),
]
@cached_property
def sortby_mapping(self):
get_field = self.model()._meta.get_field
return [
(
get_field("employee_id").verbose_name,
"employee_id__employee_first_name",
"employee_id__get_avatar",
),
(get_field("project_id").verbose_name, "project_id__title"),
(get_field("task_id").verbose_name, "task_id__title"),
(get_field("time_spent").verbose_name, "time_spent"),
(get_field("date").verbose_name, "date"),
]
row_status_indications = [
(
@@ -267,6 +276,10 @@ class TimeSheetFormView(HorillaFormView):
form view for create project
"""
form_class = TimeSheetForm
model = TimeSheet
new_display_title = _("Create") + " " + model._meta.verbose_name
def __init__(self, **kwargs: Any) -> None:
super().__init__(**kwargs)
self.dynamic_create_fields = [
@@ -279,7 +292,7 @@ class TimeSheetFormView(HorillaFormView):
form_class = TimeSheetForm
model = TimeSheet
new_display_title = _("Create Time Sheet")
new_display_title = _("Create") + " " + model._meta.verbose_name
# template_name = "cbv/timesheet/form.html"
def get_initial(self) -> dict:
@@ -485,13 +498,15 @@ class TimeSheetDetailView(HorillaDetailedView):
"subtitle": "project_id",
"avatar": "employee_id__get_avatar",
}
body = [
(_("Task"), "task_id"),
(_("Date"), "date"),
(_("Time Spent"), "time_spent"),
(_("Status"), "status_column"),
(_("Description"), "description"),
]
action_method = "detail_actions"
@cached_property
def body(self):
get_field = self.model()._meta.get_field
return [
(get_field("task_id").verbose_name, "task_id"),
(get_field("date").verbose_name, "date"),
(get_field("time_spent").verbose_name, "time_spent"),
(get_field("status").verbose_name, "get_status_display"),
(get_field("description").verbose_name, "description"),
]

View File

@@ -77,6 +77,7 @@ class TaskAllFilter(HorillaFilterSet):
field_name="end_date",
lookup_expr="lte",
widget=forms.DateInput(attrs={"type": "date"}),
label=_("End Till"),
)
class Meta:
@@ -91,6 +92,12 @@ class TaskAllFilter(HorillaFilterSet):
"status",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form.fields["end_till"].label = (
f"{self.Meta.model()._meta.get_field('end_date').verbose_name} Till"
)
def filter_by_task(self, queryset, _, value):
queryset = queryset.filter(title__icontains=value)
return queryset
@@ -140,6 +147,11 @@ class TimeSheetFilter(HorillaFilterSet):
"status",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form.fields["start_from"].label = _("Start Date From")
self.form.fields["end_till"].label = _("End Date Till")
def filter_by_employee(self, queryset, _, value):
"""
Filter queryset by first name or last name.

View File

@@ -236,9 +236,6 @@ class Project(HorillaModel):
def __str__(self):
return self.title
def status_column(self):
return dict(self.PROJECT_STATUS).get(self.status)
class Meta:
"""
Meta class to add the additional info
@@ -540,7 +537,7 @@ class TimeSheet(HorillaModel):
on_delete=models.CASCADE,
verbose_name=_("Employee"),
)
date = models.DateField(default=timezone.now)
date = models.DateField(default=timezone.now, verbose_name=_("Date"))
time_spent = models.CharField(
null=True,
default="00:00",
@@ -635,5 +632,5 @@ class TimeSheet(HorillaModel):
return url
class Meta:
verbose_name = _("TimeSheet")
verbose_name_plural = _("TimeSheets")
verbose_name = _("Time Sheet")
verbose_name_plural = _("Time Sheets")

View File

@@ -9,32 +9,31 @@
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{form.task_manager}}
<label class="oh-label"
for="{{ form.task_managers.id_for_label }}">{{form.task_managers.label}}</label>
{{form.task_managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
<label class="oh-label" for="{{ form.stage.id_for_label }}">{{form.stage.label}}</label>
{{form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
<label class="oh-label" for="{{ form.project.id_for_label }}">{{form.project.label}}</label>
{{form.project}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
<label class="oh-label" for="{{ form.status.id_for_label }}">{{form.status.label}}</label>
{{form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
<label class="oh-label" for="{{ form.end_till.id_for_label }}">{{form.end_till.label}}</label>
{{form.end_till}}
</div>
</div>
</div>
</div>
</div>

View File

@@ -6,29 +6,29 @@
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
<label class="oh-label" for="{{form.project_id.id_for_label}}">{{form.project_id.label}}</label>
{{form.project_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
<label class="oh-label" for="{{form.status.id_for_label}}">{{form.status.label}}</label>
{{form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Task" %}</label>
<label class="oh-label" for="{{form.task.id_for_label}}">{{form.task.label}}</label>
{{form.task}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Date" %}</label>
<label class="oh-label" for="{{form.date.id_for_label}}">{{form.date.label}}</label>
{{form.date}}
</div>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Employee" %}</label>
<label class="oh-label" for="{{form.employee_id.id_for_label}}">{{form.employee_id.label}}</label>
{{form.employee_id}}
</div>
</div>
@@ -43,13 +43,13 @@
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date From" %}</label>
<label class="oh-label" for="{{form.start_from.id_for_label}}">{{form.start_from.label}}</label>
{{form.start_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Till End Date" %}</label>
<label class="oh-label" for="{{form.end_till.id_for_label}}">{{form.end_till.label}}</label>
{{form.end_till}}
</div>
</div>

View File

@@ -7,27 +7,27 @@
<input type="text" value="{{project_id}}" name="project" style="display:none;">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Managers" %}</label>
<label class="oh-label" for="{{f.form.task_managers.id_for_label}}">{{f.form.task_managers.label}}</label>
{{f.form.task_managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
<label class="oh-label" for="{{f.form.stage.id_for_label}}">{{f.form.stage.label}}</label>
{{f.form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Members" %}</label>
<label class="oh-label" for="{{f.form.task_members.id_for_label}}">{{f.form.task_members.label}}</label>
{{f.form.task_members}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
<label class="oh-label" for="{{f.form.status.id_for_label}}">{{f.form.status.label}}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
<label class="oh-label" for="{{f.form.end_till.id_for_label}}">{{f.form.end_till.label}}</label>
{{f.form.end_till}}
</div>
</div>

View File

@@ -122,8 +122,8 @@
</span>
</span><br>
<div style="display: flex;" class="mb-2">
<label class="oh-kanban-card__subtitle">{% trans "Status" %} </label>
<select class="oh-table__editable-input w-100 ml-2"
<label class="oh-kanban-card__subtitle" for="id_{{task.id}}_task_status">{% trans "Status" %} </label>
<select class="oh-table__editable-input w-100 ml-2" id="id_{{task.id}}_task_status"
hx-get="{% url 'update-project-task-status' task.id %}?view=card"
hx-trigger="change" hx-swap="afterend" tabindex="-1" aria-hidden="true"
name="status">