diff --git a/project/admin.py b/project/admin.py index 9191a4891..3211d17b0 100644 --- a/project/admin.py +++ b/project/admin.py @@ -1,7 +1,9 @@ -from django.contrib import admin -from .models import * -# Register your models here. -admin.site.register(Project) -admin.site.register(ProjectStage) -admin.site.register(Task) -admin.site.register(TimeSheet) +from django.contrib import admin + +from .models import * + +# Register your models here. +admin.site.register(Project) +admin.site.register(ProjectStage) +admin.site.register(Task) +admin.site.register(TimeSheet) diff --git a/project/apps.py b/project/apps.py index 7d1aa97f0..1a57dcddd 100644 --- a/project/apps.py +++ b/project/apps.py @@ -1,12 +1,28 @@ -from django.apps import AppConfig - - -class ProjectConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "project" - - def ready(self): - from horilla.horilla_settings import APPS - - APPS.append("project") - super().ready() +from django.apps import AppConfig + + +class ProjectConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "project" + + def ready(self): + from django.urls import include, path + + from horilla.urls import urlpatterns + + urlpatterns.append( + path("project/", include("project.urls")), + ) + super().ready() + try: + from django.urls import include, path + + from horilla.urls import urlpatterns + + urlpatterns.append( + path("project/", include("project.urls")), + ) + except: + """ + Models not ready yet + """ diff --git a/project/cbv/__init__.py b/project/cbv/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/project/cbv/accessibility.py b/project/cbv/accessibility.py new file mode 100644 index 000000000..0dda4af80 --- /dev/null +++ b/project/cbv/accessibility.py @@ -0,0 +1,31 @@ +""" +Accessibility +""" + + +from django.contrib.auth.context_processors import PermWrapper + +from employee.models import Employee +from project.models import Project, Task + + +def task_crud_accessibility(request, instance: object = None, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + """ + to access crud operations + """ + employee = request.user.employee_get + is_task_manager = employee in instance.task_managers.all() + is_project_manager = employee in instance.project.managers.all() + if (request.user.has_perm("project.view_task") or is_project_manager or is_task_manager): + return True + else: + return False + +def project_manager_accessibility(request, instance: object = None, user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + """ + to access edit Project + """ + return (request.user.employee_get in instance.managers.all() or + request.user.is_superuser) \ No newline at end of file diff --git a/project/cbv/cbv_decorators.py b/project/cbv/cbv_decorators.py new file mode 100644 index 000000000..2fe609ae0 --- /dev/null +++ b/project/cbv/cbv_decorators.py @@ -0,0 +1,33 @@ + + +from project.methods import any_project_manager, any_project_member, any_task_manager, any_task_member, has_subordinates +from django.contrib import messages +from django.http import HttpResponseRedirect +from horilla.horilla_middlewares import _thread_locals +# from project.sidebar import has_subordinates + + +decorator_with_arguments = lambda decorator: lambda *args, **kwargs: lambda func: decorator(func, *args, **kwargs) + + +@decorator_with_arguments +def is_projectmanager_or_member_or_perms(function,perm): + def _function(self, *args, **kwargs): + """ + This method is used to check the employee is project manager or not + """ + request = getattr(_thread_locals,"request") + if not getattr(self,"request",None): + self.request = request + user = request.user + if ( + user.has_perm(perm) or + any_project_manager(user) or + any_project_member(user) or + any_task_manager(user) or + any_task_member(user) + ): + return function(self, *args, **kwargs) + messages.info(request, "You don't have permission.") + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) + return _function diff --git a/project/cbv/dashboard.py b/project/cbv/dashboard.py new file mode 100644 index 000000000..4ecdca142 --- /dev/null +++ b/project/cbv/dashboard.py @@ -0,0 +1,107 @@ +""" +Dashbord of project +""" + +import calendar +import datetime +from typing import Any +from django.db.models import Count +from django.db.models.query import QuerySet +from django.urls import resolve, reverse +from django.utils.translation import gettext_lazy as _ +from django.utils.decorators import method_decorator +from django.db.models import Q +from base.methods import get_subordinates +from horilla_views.generic.cbv.views import HorillaListView, HorillaDetailedView +from horilla_views.cbv_methods import login_required +from project.filters import ProjectFilter +from project.models import Project +from project.cbv.cbv_decorators import is_projectmanager_or_member_or_perms + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_project"), name="dispatch" +) +class ProjectsDueInMonth(HorillaListView): + + model = Project + filter_class = ProjectFilter + bulk_select_option = False + columns = [(_("Project"), "title", "get_avatar")] + show_filter_tags = False + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_project"): + employee = self.request.user.employee_get + task_filter = queryset.filter( + Q(task__task_members=employee) | Q(task__task_manager=employee) + ) + project_filter = queryset.filter(Q(manager=employee) | Q(members=employee)) + queryset = task_filter | project_filter + today = datetime.date.today() + first_day = today.replace(day=1) + last_day = calendar.monthrange(today.year, today.month)[1] + last_day_of_month = today.replace(day=last_day) + queryset = queryset.filter( + Q(end_date__gte=first_day) & Q(end_date__lte=last_day_of_month) + ).exclude(status="expired") + return queryset + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("projects-due-in-this-month") + + row_attrs = """ + hx-get='{get_detail_url}?instance_ids={ordered_ids}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + +class ProjectDetailView(HorillaDetailedView): + """ + detail view of the projects + """ + + 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) + instnce_id = resolve(self.request.path_info).kwargs.get("pk") + employee = self.request.user.employee_get + project = Project.objects.get(id=instnce_id) + if ( + employee in project.managers.all() + or employee in project.members.all() + or self.request.user.has_perm("project.view_project") + ): + self.actions = [ + { + "action": _("View Project"), + "icon": "create-outline", + "attrs": """ + class = "oh-btn oh-btn--info w-100" + {redirect} + """, + } + ] + + def get_queryset(self) -> QuerySet[Any]: + queryset = super().get_queryset() + queryset = queryset.annotate(task_count=Count("task")) + return queryset diff --git a/project/cbv/project_stage.py b/project/cbv/project_stage.py new file mode 100644 index 000000000..b06d31c1f --- /dev/null +++ b/project/cbv/project_stage.py @@ -0,0 +1,135 @@ +""" +This page handles the cbv methods for project stages +""" + +import logging +from typing import Any +from django.http import HttpResponse +from django.utils.translation import gettext_lazy as _ +from django.utils.decorators import method_decorator +from django.contrib import messages +from horilla_views.cbv_methods import login_required +from horilla_views.generic.cbv.views import HorillaFormView + +# from project.decorator import project_delete_permission +from project.forms import ProjectStageForm +from project.methods import you_dont_have_permission +from project.models import Project, ProjectStage + +logger = logging.getLogger(__name__) + + +@method_decorator(login_required, name="dispatch") +# @method_decorator(project_delete_permission, name="dispatch") +class ProjectStageCreateForm(HorillaFormView): + """ + form view fro create and edit stages + """ + + form_class = ProjectStageForm + model = ProjectStage + new_display_title = _("Create Project Stage") + + def get(self, request, *args, pk=None, **kwargs): + if request.GET.get("project_id"): + project_id = request.GET.get("project_id") + else: + project_id = self.kwargs.get("project_id") + stage_id = self.kwargs.get("pk") + if project_id: + try: + if project_id: + project = Project.objects.filter(id=project_id).first() + elif stage_id: + project = ProjectStage.objects.filter(id=stage_id).first().project + if ( + request.user.employee_get in project.managers.all() + or request.user.is_superuser + ): + return super().get(request, *args, pk=pk, **kwargs) + else: + return you_dont_have_permission(request) + except Exception as e: + logger.error(e) + messages.error(request, _("Something went wrong!")) + return HttpResponse("") + else: + return super().get(request, *args, pk=pk, **kwargs) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + project_id = self.kwargs.get("project_id") + if project_id: + project = Project.objects.get(id=project_id) + self.form.fields["project"].initial = project + if self.form.instance.pk: + self.form_class.verbose_name = _("Update Project Stage") + return context + + def form_valid(self, form: ProjectStageForm) -> HttpResponse: + if form.is_valid(): + if form.instance.pk: + project_id = self.form.cleaned_data["project"].id + message = _(f"{self.form.instance} Updated") + else: + project_id = self.kwargs.get("project_id") + message = _("New project stage created") + form.save() + messages.success(self.request, message) + return self.HttpResponse( + f"" + ) + return super().form_valid(form) + + +from django import forms + + +class StageDynamicCreateForm(ProjectStageCreateForm): + """ + dynamic create form for stage + """ + + is_dynamic_create_view = True + template_name = HorillaFormView.template_name + + def get_initial(self): + initial = super().get_initial() + project = self.request.GET.get("project") + initial["project"] = project + return initial + + def init_form(self, *args, data, files, instance=None, **kwargs): + initial = self.get_initial() + form = super().init_form( + *args, data=data, files=files, instance=instance, initial=initial, **kwargs + ) + if not initial.get("project"): + form.fields["project"].widget = forms.Select( + attrs={"class": "oh-select oh-select-2 w-100 oh-select2"} + ) + form.fields["project"].queryset = Project.objects.all() + + return form + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + project_id = self.request.GET.get("project_id") + if not project_id: + project_id = self.request.GET.get("project") + if project_id: + project = Project.objects.get(id=project_id) + self.form.fields["project"].initial = project + self.form.fields["project"].choices = [(project.id, project.title)] + return context + + def form_valid(self, form: ProjectStageForm) -> HttpResponse: + if form.is_valid(): + if form.instance.pk: + message = _(f"{self.form.instance} Updated") + else: + message = _("New project stage created") + form.save() + messages.success(self.request, _(message)) + return self.HttpResponse() + return super().form_valid(form) diff --git a/project/cbv/projects.py b/project/cbv/projects.py new file mode 100644 index 000000000..2a765c643 --- /dev/null +++ b/project/cbv/projects.py @@ -0,0 +1,518 @@ +""" +CBV of projects page +""" + +from typing import Any +from django.http import HttpResponse +from django.utils.translation import gettext_lazy as _ +from django.utils.decorators import method_decorator +from django.urls import reverse +from django.contrib import messages +from django.views.generic import ListView +from employee.models import Employee +from horilla_views.cbv_methods import login_required, permission_required +from horilla_views.generic.cbv.views import ( + HorillaCardView, + HorillaFormView, + HorillaListView, + HorillaNavView, + TemplateView, +) +from project.cbv.cbv_decorators import is_projectmanager_or_member_or_perms +from project.filters import ProjectFilter +from project.forms import ProjectForm +from project.methods import any_project_manager, any_project_member, is_project_manager_or_super_user, you_dont_have_permission +from project.models import Project +from django.db.models import Q +from horilla.horilla_middlewares import _thread_locals + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_project"), name="dispatch" +) +class ProjectsView(TemplateView): + """ + for projects page + """ + + template_name = "cbv/projects/projects.html" + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_project"), name="dispatch" +) +class ProjectsNavView(HorillaNavView): + """ + Nav bar + """ + + template_name = "cbv/projects/project_nav.html" + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("project-list-view") + # self.search_in = [ + # ("status", _("Managers")), + # ("members", _("Members")), + # ] + if self.request.user.has_perm("project.view_project"): + self.actions = [ + { + "action": _("Import"), + "attrs": """ + id="importProject" + data-toggle="oh-modal-toggle" + data-target="#projectImport" + style="cursor: pointer;" + """, + }, + { + "action": _("Export"), + "attrs": """ + id="exportProject" + style="cursor: pointer;" + """, + }, + { + "action": _("Archive"), + "attrs": """ + id="archiveProject" + style="cursor: pointer;" + """, + }, + { + "action": _("Un-archive"), + "attrs": """ + id="unArchiveProject" + style="cursor: pointer;" + """, + }, + { + "action": _("Delete"), + "attrs": """ + class="oh-dropdown__link--danger" + data-action ="delete" + id="deleteProject" + style="cursor: pointer; color:red !important" + """, + }, + ] + self.view_types = [ + { + "type": "list", + "icon": "list-outline", + "url": reverse("project-list-view"), + "attrs": """ + title ='List' + """, + }, + { + "type": "card", + "icon": "grid-outline", + "url": reverse("project-card-view"), + "attrs": """ + title ='Card' + """, + }, + ] + if self.request.user.has_perm("project.add_project"): + self.create_attrs = f""" + onclick = "event.stopPropagation();" + data-toggle="oh-modal-toggle" + data-target="#genericModal" + hx-target="#genericModalBody" + hx-get="{reverse('create-project')}" + """ + group_by_fields = [ + ("status", _("Status")), + ("is_active", _("Is active")), + ] + nav_title = _("Projects") + 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( + is_projectmanager_or_member_or_perms("project.view_project"), name="dispatch" +) +class ProjectsList(HorillaListView): + """ + Projects list view + """ + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_project"): + employee = self.request.user.employee_get + task_filter = queryset.filter( + Q(task__task_members=employee) + | Q(task__task_managers=employee) + ) + project_filter = queryset.filter( + Q(managers=employee) + | Q(members=employee) + ) + queryset = task_filter | project_filter + return queryset.distinct() + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("project-list-view") + if (self.request.user.is_superuser ): + self.action_method = "actions" + + model = Project + filter_class = ProjectFilter + + columns = [ + (_("Project"), "title"), + (_("Project Managers"), "get_managers"), + (_("Project Members"), "get_members"), + (_("Status"), "status_column"), + (_("Start Date"), "start_date"), + (_("End Date"), "end_date"), + (_("File"), "get_document_html"), + (_("Description"), "get_description"), + ] + + sortby_mapping = [ + ("Project", "title"), + ("Start Date", "start_date"), + ("End Date", "end_date"), + ] + + row_status_indications = [ + ( + "new--dot", + _("New"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('new'); + $('#applyFilter').click(); + " + """, + ), + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ( + "on-hold--dot", + _("On Hold"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('on_hold'); + $('#applyFilter').click(); + + " + """, + ), + ( + "cancelled--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('cancelled'); + $('#applyFilter').click(); + + " + """, + ), + ( + "expired--dot", + _("Expired"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('expired'); + $('#applyFilter').click(); + + " + """, + ), + ] + + row_status_class = "status-{status}" + + row_attrs = """ + {redirect} + """ + + +@method_decorator(login_required, name="dispatch") +# @method_decorator(permission_required("project.add_project"), name="dispatch") +class ProjectFormView(HorillaFormView): + """ + form view for create project + """ + + form_class = ProjectForm + model = Project + new_display_title = _("Create Project") + + def __init__(self, **kwargs): + super().__init__(**kwargs) + user = self.request.user + + if (not user.is_superuser and + not user.has_perm("project.add_project") + + ): + self.template_name = "decorator_404.html" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if self.form.instance.pk: + self.form_class.verbose_name = _("Update project") + return context + + def form_valid(self, form: ProjectForm) -> HttpResponse: + if form.is_valid(): + if form.instance.pk: + message = _(f"{self.form.instance} Updated") + HTTP_REFERER = self.request.META.get("HTTP_REFERER", None) + if HTTP_REFERER and "task-view/" in HTTP_REFERER: + form.save() + messages.success(self.request, message) + return self.HttpResponse("") + else: + message = _("New project created") + form.save() + messages.success(self.request, _(message)) + return self.HttpResponse() + return super().form_valid(form) + + +class DynamicProjectCreationFormView(ProjectFormView): + + is_dynamic_create_view = True + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_project"), name="dispatch" +) +class ProjectCardView(HorillaCardView): + """ + For card view + """ + + model = Project + filter_class = ProjectFilter + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_project"): + employee = self.request.user.employee_get + task_filter = queryset.filter( + Q(task__task_members=employee) + | Q(task__task_managers=employee) + ) + project_filter = queryset.filter( + Q(managers=employee) + | Q(members=employee) + ) + queryset = task_filter | project_filter + return queryset.distinct() + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("project-card-view") + if (self.request.user.has_perm("project.change_project") + or self.request.user.has_perm("project.delete_project") + or any_project_manager(self.request.user) + or any_project_member(self.request.user) + ): + self.actions = [ + { + "action": "Edit", + "accessibility": "project.cbv.accessibility.project_manager_accessibility", + "attrs": """ + hx-get='{get_update_url}' + hx-target='#genericModalBody' + data-toggle="oh-modal-toggle" + data-target="#genericModal" + class="oh-dropdown__link" + """, + }, + { + "action": "archive_status", + "accessibility": "project.cbv.accessibility.project_manager_accessibility", + "attrs": """ + href="{get_archive_url}" + onclick="return confirm('Do you want to {archive_status} this project?')" + class="oh-dropdown__link" + """, + }, + { + "action": "Delete", + "accessibility": "project.cbv.accessibility.project_manager_accessibility", + "attrs": """ + onclick=" + event.stopPropagation() + deleteItem({get_delete_url}); + " + class="oh-dropdown__link oh-dropdown__link--danger" + """, + }, + ] + + details = { + "image_src": "get_avatar", + "title": "{get_task_badge_html}", + "subtitle": "Status : {status_column}
Start date : {start_date}
End date : {end_date}", + } + card_status_class = "status-{status}" + + card_status_indications = [ + ( + "new--dot", + _("New"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('new'); + $('#applyFilter').click(); + " + """, + ), + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ( + "on-hold--dot", + _("On Hold"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('on_hold'); + $('#applyFilter').click(); + + " + """, + ), + ( + "cancelled--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('cancelled'); + $('#applyFilter').click(); + + " + """, + ), + ( + "expired--dot", + _("Expired"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('expired'); + $('#applyFilter').click(); + + " + """, + ), + ] + card_attrs = """ + {redirect} + """ + + +# def projects_tab(request, pk=None): +# """ +# This method is used to projects tab +# """ + +# projects = Project.objects.filter( +# Q(manager=pk) +# | Q(members=pk) +# | Q(task__task_members=pk) +# | Q(task__task_manager=pk) +# ) +# context = {"projects": projects} +# if pk: +# template = "cbv/projects/project_tab.html" +# employees = Employee.objects.filter(id=pk).distinct() +# emoloyee = employees.first() +# context["employee"] = emoloyee +# return render( +# request, +# template, +# context, +# ) + +class ProjectsTabView(ListView): + model = Project + template_name = "cbv/projects/project_tab.html" + context_object_name = "projects" + + def get_queryset(self): + pk = self.kwargs.get('pk') + queryset = Project.objects.filter( + Q(manager=pk) + | Q(members=pk) + | Q(task__task_members=pk) + | Q(task__task_manager=pk) + ) + return queryset.distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + pk = self.kwargs.get('pk') + if pk: + employees = Employee.objects.filter(id=pk).distinct() + employee = employees.first() + context["employee"] = employee + return context + +# Remove the command lines after horilla converted into CBV +# from employee.cbv.employee_profile import EmployeeProfileView +# EmployeeProfileView.add_tab( +# tabs=[ +# { +# "title": "Projects", +# # "view": projects_tab, +# "view": ProjectsTabView.as_view(), +# "accessibility": "employee.cbv.accessibility.workshift_accessibility", +# }, +# ] +# ) diff --git a/project/cbv/tasks.py b/project/cbv/tasks.py new file mode 100644 index 000000000..e8efdeccb --- /dev/null +++ b/project/cbv/tasks.py @@ -0,0 +1,583 @@ +""" +This page handles the cbv methods for task page +""" + +import logging +from typing import Any +from django import forms +from django.db.models import Q +from django.http import HttpResponse +from django.contrib import messages +from django.shortcuts import render +from django.utils.translation import gettext_lazy as _ +from django.urls import reverse +from django.utils.decorators import method_decorator +from base.methods import get_subordinates +from horilla_views.cbv_methods import login_required +from horilla_views.generic.cbv.views import ( + HorillaCardView, + HorillaDetailedView, + HorillaFormView, + HorillaListView, + TemplateView, + HorillaNavView, +) +from project.cbv.project_stage import StageDynamicCreateForm +from project.cbv.projects import DynamicProjectCreationFormView +from project.filters import TaskAllFilter +from project.forms import TaskAllForm +from project.methods import you_dont_have_permission +from project.models import Project, ProjectStage, Task +from project.templatetags.taskfilters import task_crud_perm + +logger = logging.getLogger(__name__) + + +@method_decorator(login_required, name="dispatch") +class TasksTemplateView(TemplateView): + """ + view page of the task page + """ + + template_name = "cbv/tasks/task_template_view.html" + + +@method_decorator(login_required, name="dispatch") +class TaskListView(HorillaListView): + """ + list view of the page + """ + + model = Task + filter_class = TaskAllFilter + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.view_id = "task-list-container" + self.search_url = reverse("tasks-list-view") + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_task"): + employee_id = self.request.user.employee_get + subordinates = get_subordinates(self.request) + subordinate_ids = [subordinate.id for subordinate in subordinates] + project = queryset.filter( + Q(project__managers=employee_id) + | Q(project__members=employee_id) + | Q(project__managers__in=subordinate_ids) + | Q(project__members__in=subordinate_ids) + ) + queryset = ( + queryset.filter( + Q(task_members=employee_id) + | Q(task_managers=employee_id) + | Q(task_members__in=subordinate_ids) + | Q(task_managers__in=subordinate_ids) + ) + | project + ) + 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"), + ] + + sortby_mapping = [ + ("Task", "title"), + ("Project", "project__title"), + ("Stage", "stage"), + ("End Date", "end_date"), + ("Status", "status"), + ] + + action_method = "actions" + + row_status_indications = [ + ( + "todo--dot", + _("To Do"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('to_do'); + $('#applyFilter').click(); + " + """, + ), + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ( + "expired--dot", + _("Expired"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('expired'); + $('#applyFilter').click(); + + " + """, + ), + ] + + row_status_class = "status-{status}" + + row_attrs = """ + hx-get='{task_detail_view}?instance_ids={ordered_ids}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + +@method_decorator(login_required, name="dispatch") +class TasksNavBar(HorillaNavView): + """ + navbar of teh page + """ + + nav_title = _("Tasks") + filter_instance = TaskAllFilter() + filter_form_context_name = "form" + filter_body_template = "cbv/tasks/task_filter.html" + search_swap_target = "#listContainer" + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + employee = self.request.user.employee_get + projects = Project.objects.all() + managers = [ + manager for project in projects for manager in project.managers.all() + ] + members = [member for project in projects for member in project.members.all()] + self.search_url = reverse("tasks-list-view") + if employee in managers + members or self.request.user.has_perm( + "project.add_task" + ): + self.create_attrs = f""" + onclick = "event.stopPropagation();" + data-toggle="oh-modal-toggle" + data-target="#genericModal" + hx-target="#genericModalBody" + hx-get="{reverse('create-task-all')}" + """ + + self.view_types = [ + { + "type": "list", + "icon": "list-outline", + "url": reverse("tasks-list-view"), + "attrs": """ + title ='List' + """, + }, + { + "type": "card", + "icon": "grid-outline", + "url": reverse("tasks-card-view"), + "attrs": """ + title ='Card' + """, + }, + ] + + if self.request.user.has_perm("project.view_task"): + self.actions = [ + { + "action": _("Archive"), + "attrs": """ + id="archiveTask", + style="cursor: pointer;" + """, + }, + { + "action": _("Un-Archive"), + "attrs": """ + id="unArchiveTask", + style="cursor: pointer;" + """, + }, + { + "action": _("Delete"), + "attrs": """ + class="oh-dropdown__link--danger" + data-action = "delete" + id="deleteTask" + style="cursor: pointer; color:red !important" + + """, + }, + ] + + group_by_fields = [ + ("project", _("Project")), + ("stage", _("Stage")), + ("status", _("Status")), + ] + + +@method_decorator(login_required, name="dispatch") +class TaskCreateForm(HorillaFormView): + """ + Form view for create and update tasks + """ + + form_class = TaskAllForm + model = Task + template_name = "cbv/tasks/task_form.html" + new_display_title = _("Create Task") + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + if self.request.user.has_perm("project.view_task"): + self.dynamic_create_fields = [ + ("project", DynamicProjectCreationFormView), + ("stage", StageDynamicCreateForm, ["project"]), + ] + + def get(self, request, *args, pk=None, **kwargs): + project_id = self.kwargs.get("project_id") + stage_id = self.kwargs.get("stage_id") + task_id = self.kwargs.get("pk") + try: + if project_id: + project = Project.objects.filter(id=project_id).first() + elif stage_id: + project = ProjectStage.objects.filter(id=stage_id).first().project + elif task_id: + task = Task.objects.filter(id=task_id).first() + project = task.project + elif not task_id: + return super().get(request, *args, pk=pk, **kwargs) + if ( + request.user.employee_get in project.managers.all() + or request.user.is_superuser + ): + self.dynamic_create_fields = [ + ("project", DynamicProjectCreationFormView), + ("stage", StageDynamicCreateForm), + ] + return super().get(request, *args, pk=pk, **kwargs) + elif task_id: + if request.user.employee_get in task.task_managers.all(): + return super().get(request, *args, pk=pk, **kwargs) + + else: + return you_dont_have_permission(request) + except Exception as e: + logger.error(e) + messages.error(request, _("Something went wrong!")) + return HttpResponse("") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + project_id = self.kwargs.get("project_id") + stage_id = self.kwargs.get("stage_id") + task_id = self.kwargs.get("pk") + + dynamic_project_id = self.request.GET.get("dynamic_project") + + if dynamic_project_id and dynamic_project_id != "dynamic_create": + stages = ProjectStage.objects.filter(project=dynamic_project_id) + attrs = self.form.fields["stage"].widget.attrs + attrs["style"] = "width:100% !important;" + self.form.fields["stage"].choices = ( + [("", _("Select Stage"))] + + [(stage.pk, stage) for stage in stages] + + [("dynamic_create", _("Dynamic Create"))] + ) + + if task_id and not dynamic_project_id: + task = self.form.instance + stages = task.project.project_stages.all() + self.form.fields["stage"].choices = ( + [("", _("Select Stage"))] + + [(stage.pk, stage) for stage in stages] + + [("dynamic_create", _("Dynamic Create"))] + ) + + if stage_id: + stage = ProjectStage.objects.filter(id=stage_id).first() + project = stage.project + self.form.fields["stage"].initial = stage + self.form.fields["stage"].choices = [(stage.id, stage.title)] + self.form.fields["project"].initial = project + self.form.fields["project"].choices = [(project.id, project.title)] + elif project_id: + project = Project.objects.get(id=project_id) + self.form.fields["project"].initial = project + self.form.fields["project"].choices = [(project.id, project.title)] + stages = ProjectStage.objects.filter(project=project) + self.form.fields["stage"].choices = [ + (stage.id, stage.title) for stage in stages + ] + elif self.form.instance.pk: + self.form_class.verbose_name = _("Update Task") + if self.request.GET.get("project_task"): + self.form.fields["project"].widget = forms.HiddenInput() + self.form.fields["stage"].widget = forms.HiddenInput() + else: + if self.request.user.is_superuser: + self.dynamic_create_fields = [ + ("project", DynamicProjectCreationFormView), + ("stage", StageDynamicCreateForm, ["project"]), + ] + + if project_id or stage_id: + if ( + self.request.user.employee_get in project.managers.all() + or self.request.user.is_superuser + ): + + self.form.fields["project"].choices.append( + ("dynamic_create", "Dynamic create") + ) + self.form.fields["stage"].choices.append( + ("dynamic_create", "Dynamic create") + ) + + return context + + def form_valid(self, form: TaskAllForm) -> HttpResponse: + stage_id = self.kwargs.get("stage_id") + if form.is_valid(): + if form.instance.pk: + message = _(f"{self.form.instance} Updated") + else: + message = _("New Task created") + form.save() + messages.success(self.request, _(message)) + if stage_id or self.request.GET.get("project_task"): + return HttpResponse("") + return self.HttpResponse("") + return super().form_valid(form) + + +class DynamicTaskCreateFormView(TaskCreateForm): + + is_dynamic_create_view = True + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if self.request.GET: + project_id = self.request.GET.get("project_id") + if project_id: + project = Project.objects.get(id=project_id) + stages = ProjectStage.objects.filter(project__id=project_id) + self.form.fields["project"].initial = project + self.form.fields["project"].choices = [(project.id, project.title)] + self.form.fields["stage"].queryset = stages + # self.form.fields["project"].widget = forms.HiddenInput() + return context + + +@method_decorator(login_required, name="dispatch") +class TaskDetailView(HorillaDetailedView): + """ + detail view of the task page + """ + + model = Task + title = _("Task Details") + + 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" + + +@method_decorator(login_required, name="dispatch") +class TaskCardView(HorillaCardView): + """ + card view of the page + """ + + model = Task + filter_class = TaskAllFilter + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.view_id = "task-card" + self.search_url = reverse("tasks-card-view") + self.actions = [ + { + "action": _("Edit"), + "accessibility": "project.cbv.accessibility.task_crud_accessibility", + "attrs": """ + data-toggle = "oh-modal-toggle" + data-target = "#genericModal" + hx-target="#genericModalBody" + hx-get ='{get_update_url}' + class="oh-dropdown__link" + style="cursor: pointer;" + """, + }, + { + "action": _("archive_status"), + "accessibility": "project.cbv.accessibility.task_crud_accessibility", + "attrs": """ + href="{get_archive_url}" + onclick="return confirm('Do you want to {archive_status} this task?')" + class="oh-dropdown__link" + """, + }, + { + "action": _("Delete"), + "accessibility": "project.cbv.accessibility.task_crud_accessibility", + "attrs": """ + onclick=" + event.stopPropagation() + deleteItem({get_delete_url}); + " + class="oh-dropdown__link oh-dropdown__link--danger" + """, + }, + ] + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_task"): + employee_id = self.request.user.employee_get + subordinates = get_subordinates(self.request) + subordinate_ids = [subordinate.id for subordinate in subordinates] + project = queryset.filter( + Q(project__managers=employee_id) + | Q(project__members=employee_id) + | Q(project__managers__in=subordinate_ids) + | Q(project__members__in=subordinate_ids) + ) + queryset = ( + queryset.filter( + Q(task_members=employee_id) + | Q(task_managers=employee_id) + | Q(task_members__in=subordinate_ids) + | Q(task_managers__in=subordinate_ids) + ) + | project + ) + return queryset.distinct() + + details = { + "image_src": "get_avatar", + "title": "{title}", + "subtitle": "Project Name : {if_project}
Stage Name : {stage}
End Date : {end_date}", + } + + card_attrs = """ + hx-get='{task_detail_view}?instance_ids={ordered_ids}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + card_status_indications = [ + ( + "todo--dot", + _("To Do"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('to_do'); + $('#applyFilter').click(); + " + """, + ), + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ( + "expired--dot", + _("Expired"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('expired'); + $('#applyFilter').click(); + + " + """, + ), + ] + + card_status_class = "status-{status}" + + +class TasksInIndividualView(TaskListView): + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + employee_id = self.request.GET.get("employee_id") + self.row_attrs = f""" + hx-get='{{task_detail_view}}?instance_ids={{ordered_ids}}&employee_id={employee_id}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + def get_queryset(self): + queryset = HorillaListView.get_queryset(self) + employee_id = self.request.GET.get("employee_id") + project_id = self.request.GET.get("project_id") + queryset = queryset.filter( + Q(task_members=employee_id) | Q(task_manager=employee_id) + ) + queryset = queryset.filter(project=project_id) + return queryset + + row_status_indications = None + bulk_select_option = None + action_method = None diff --git a/project/cbv/timesheet.py b/project/cbv/timesheet.py new file mode 100644 index 000000000..1963f1e83 --- /dev/null +++ b/project/cbv/timesheet.py @@ -0,0 +1,482 @@ +""" +CBV of time sheet page +""" + +from typing import Any +from django import forms +from django.http import HttpResponse +from django.shortcuts import render +from django.utils.translation import gettext_lazy as _ +from django.utils.decorators import method_decorator +from django.urls import resolve, reverse +from django.contrib import messages +from employee.models import Employee +from horilla_views.cbv_methods import login_required +from horilla_views.generic.cbv.views import ( + HorillaCardView, + HorillaDetailedView, + HorillaFormView, + HorillaListView, + HorillaNavView, + TemplateView, +) +from project.cbv.cbv_decorators import is_projectmanager_or_member_or_perms +from project.cbv.projects import DynamicProjectCreationFormView +from project.cbv.tasks import DynamicTaskCreateFormView +from project.filters import TimeSheetFilter +from project.forms import TimeSheetForm +from project.models import Project, Task, TimeSheet +from django.db.models import Q + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetView(TemplateView): + """ + for time sheet page + """ + + template_name = "cbv/timesheet/timesheet.html" + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetNavView(HorillaNavView): + """ + Nav bar + """ + template_name = "cbv/timesheet/timesheet_nav.html" + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("time-sheet-list") + url = f"{reverse('personal-time-sheet-view',kwargs={'emp_id': self.request.user.employee_get.id})}" + self.actions = [ + { + "action": _("Delete"), + "attrs": """ + class="oh-dropdown__link--danger" + data-action ="delete" + onclick="deleteTimeSheet();" + style="cursor: pointer; color:red !important" + """, + }, + ] + self.view_types = [ + { + "type": "list", + "icon": "list-outline", + "url": reverse("time-sheet-list"), + "attrs": """ + title ='List' + """, + }, + { + "type": "card", + "icon": "grid-outline", + "url": reverse("time-sheet-card"), + "attrs": """ + title ='Card' + """, + }, + { + "type": "graph", + "icon": "bar-chart", + "attrs": f""" + onclick="event.stopPropagation(); + window.location.href='{url}'" + title ='Graph' + """, + }, + ] + + self.create_attrs = f""" + onclick = "event.stopPropagation();" + data-toggle="oh-modal-toggle" + data-target="#genericModal" + hx-target="#genericModalBody" + 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( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetList(HorillaListView): + """ + Time sheet list view + """ + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_timesheet"): + employee = self.request.user.employee_get + queryset = queryset.filter( + Q(task_id__task_managers=employee) + | Q(project_id__managers=employee) + | Q(employee_id=employee) + ).distinct() + return queryset + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + + self.search_url = reverse("time-sheet-list") + self.action_method = "actions" + + model = TimeSheet + filter_class = TimeSheetFilter + + 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"), + ] + + row_status_indications = [ + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_Progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ] + row_attrs = """ + hx-get='{detail_view}?instance_ids={ordered_ids}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + row_status_class = "status-{status}" + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TaskTimeSheet(TimeSheetList): + + row_attrs = "" + row_status_indications = False + bulk_select_option = False + + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.view_id = "task-timesheet-container" + task_id = resolve(self.request.path_info).kwargs.get("task_id") + self.request.task_id = task_id + employee_id = self.request.GET.get("employee_id") + if employee_id: + self.action_method = "actions" + + def get_context_data(self, **kwargs: Any): + context = super().get_context_data(**kwargs) + task_id = self.kwargs.get("task_id") + task = Task.objects.get(id=task_id) + project = task.project + context["task_id"] = task_id + context["project"] = project + context["task"] = task + return context + + template_name = "cbv/timesheet/task_timesheet.html" + + def get_queryset(self): + queryset = HorillaListView.get_queryset(self) + task_id = self.kwargs.get("task_id") + task = Task.objects.filter( id = task_id).first() + queryset = TimeSheet.objects.filter(task_id=task_id) + queryset = queryset.filter(task_id=task_id) + employee_id = self.request.GET.get("employee_id") + if employee_id: + employee = Employee.objects.filter(id = employee_id ).first() + if (not employee in task.task_managers.all() + and not employee in task.project.managers.all() + and not employee.employee_user_id.is_superuser + ): + queryset = queryset.filter(employee_id=employee_id) + + return queryset + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetFormView(HorillaFormView): + """ + form view for create project + """ + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.dynamic_create_fields = [ + ("task_id", DynamicTaskCreateFormView), + ] + if self.request.user.has_perm("project.add_project"): + self.dynamic_create_fields.append( + ("project_id", DynamicProjectCreationFormView) + ) + + form_class = TimeSheetForm + model = TimeSheet + new_display_title = _("Create Time Sheet") + # template_name = "cbv/timesheet/form.html" + + def get_initial(self) -> dict: + initial = super().get_initial() + task_id = self.kwargs.get("task_id") + if task_id: + task = Task.objects.get(id=task_id) + project_id = task.project + initial["project_id"] = project_id.id + initial["task_id"] = task.id + return initial + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + task_id = self.kwargs.get("task_id") + user_employee_id = self.request.user.employee_get.id + project = None + if task_id: + task = Task.objects.get(id=task_id) + project = task.project + employee = Employee.objects.filter(id=user_employee_id) + + if self.form.instance.pk: + task_id = self.form.instance.task_id.id + project = self.form.instance.project_id + tasks = Task.objects.filter(project=project) + employee = Employee.objects.filter(id=user_employee_id) + task = Task.objects.get(id=task_id) + self.form.fields["task_id"].queryset = tasks + self.form.fields["task_id"].choices = [ + (item.id, item.title) for item in tasks + ] + self.form.fields["task_id"].choices.append( + ("dynamic_create", "Dynamic create") + ) + task_id = self.request.GET.get("task_id") + self.form_class.verbose_name = _("Update Time Sheet") + # If the timesheet create from task or project + if project: + if (self.request.user.is_superuser or + self.request.user.has_perm("project.add_project") + ): + members = ( project.managers.all() | + project.members.all() | + task.task_members.all() |task.task_managers.all() + ).distinct() + elif ( employee.first() in project.managers.all()): + members = ( + employee | project.members.all() | + task.task_members.all() | task.task_managers.all() + ).distinct() + elif( employee.first() in task.task_managers.all() ): + members = ( employee | task.task_members.all()).distinct() + else : + members = employee + if task_id: + self.form.fields["project_id"].widget = forms.HiddenInput() + self.form.fields["task_id"].widget = forms.HiddenInput() + self.form.fields["employee_id"].queryset = members + + # If the timesheet create directly + else : + employee = self.request.user.employee_get + if self.request.user.has_perm('project.add_timesheet'): + projects = Project.objects.all() + else: + projects = ( + Project.objects.filter(managers = employee) | + Project.objects.filter(members = employee) | + Project.objects.filter( + id__in=Task.objects.filter(task_managers=employee).values_list('project', flat=True) + ) | + Project.objects.filter( + id__in=Task.objects.filter(task_members=employee).values_list('project', flat=True) + ) + ).distinct() + self.form.fields['project_id'].queryset = projects + return context + + def form_valid(self, form: TimeSheetForm) -> HttpResponse: + if form.is_valid(): + if form.instance.pk: + message = _(f"{self.form.instance} Updated") + else: + message = _("New time sheet created") + form.save() + messages.success(self.request, _(message)) + return self.HttpResponse() + return super().form_valid(form) + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetCardView(HorillaCardView): + """ + For card view + """ + + model = TimeSheet + filter_class = TimeSheetFilter + + def get_queryset(self): + queryset = super().get_queryset() + if not self.request.user.has_perm("project.view_timesheet"): + employee = self.request.user.employee_get + queryset = queryset.filter( + Q(task_id__task_managers=employee) + | Q(project_id__managers=employee) + | Q(employee_id=employee) + ).distinct() + return queryset + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.search_url = reverse("time-sheet-card") + self.actions = [ + { + "action": "Edit", + "attrs": """ + hx-get='{get_update_url}' + hx-target='#genericModalBody' + data-toggle="oh-modal-toggle" + data-target="#genericModal" + class="oh-dropdown__link" + """, + }, + { + "action": "Delete", + "attrs": """ + onclick=" + event.stopPropagation() + deleteItem({get_delete_url}); + " + class="oh-dropdown__link oh-dropdown__link--danger" + """, + }, + ] + + details = { + "image_src": "employee_id__get_avatar", + "title": "{employee_id}", + "subtitle": " {date}
Project : {project_id}
{task_id} | Time Spent : {time_spent}", + } + + card_status_class = "status-{status}" + + card_status_indications = [ + ( + "in-progress--dot", + _("In progress"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('in_Progress'); + $('#applyFilter').click(); + + " + """, + ), + ( + "completed--dot", + _("Completed"), + """ + onclick=" + $('#applyFilter').closest('form').find('[name=status]').val('completed'); + $('#applyFilter').click(); + + " + """, + ), + ] + + card_attrs = """ + hx-get='{detail_view}?instance_ids={ordered_ids}' + hx-target="#genericModalBody" + data-target="#genericModal" + data-toggle="oh-modal-toggle" + """ + + +@method_decorator(login_required, name="dispatch") +@method_decorator( + is_projectmanager_or_member_or_perms("project.view_timesheet"), name="dispatch" +) +class TimeSheetDetailView(HorillaDetailedView): + """ + detail view of the page + """ + + model = TimeSheet + title = _("Details") + header = { + "title": "employee_id", + "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" diff --git a/project/decorator.py b/project/decorator.py index da809b736..06622fb82 100644 --- a/project/decorator.py +++ b/project/decorator.py @@ -1,138 +1,189 @@ -from project.methods import any_project_manager, any_project_member, any_task_manager, any_task_member -from .models import Project,Task,ProjectStage -from django.contrib import messages -from django.http import HttpResponseRedirect - - -decorator_with_arguments = lambda decorator: lambda *args, **kwargs: lambda func: decorator(func, *args, **kwargs) - -@decorator_with_arguments -def is_projectmanager_or_member_or_perms(function,perm): - def _function(request, *args, **kwargs): - """ - This method is used to check the employee is project manager or not - """ - user = request.user - if ( - user.has_perm(perm) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ): - return function(request, *args, **kwargs) - messages.info(request, "You don't have permission.") - return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) - return _function - -@decorator_with_arguments -def project_update_permission(function=None, *args, **kwargs): - def check_project_member(request,project_id=None,*args, **kwargs,): - """ - This method is used to check the employee is project member or not - """ - project = Project.objects.get(id = project_id) - if (request.user.has_perm('project.change_project') or - request.user.employee_get == project.manager or - request.user.employee_get in project.members.all() - ): - return function(request, *args,project_id=project_id, **kwargs) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - # return function(request, *args, **kwargs) - return check_project_member - - -@decorator_with_arguments -def project_delete_permission(function=None,*args,**kwargs): - def is_project_manager(request,project_id=None,*args, **kwargs,): - """ - This method is used to check the employee is project manager or not - """ - project = Project.objects.get(id = project_id) - if ( - request.user.employee_get == project.manager or - request.user.has_perm('project.delete_project') - ): - return function(request, *args,project_id=project_id, **kwargs) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - return is_project_manager - - -@decorator_with_arguments -def project_stage_update_permission(function=None, *args, **kwargs): - def check_project_member(request,stage_id=None,*args, **kwargs,): - """ - This method is used to check the employee is project stage member or not - """ - project = ProjectStage.objects.get(id = stage_id).project - if (request.user.has_perm('project.change_project') or - request.user.has_perm('project.change_task') or - request.user.employee_get == project.manager or - request.user.employee_get in project.members.all() - ): - return function(request, *args,stage_id=stage_id, **kwargs) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - # return function(request, *args, **kwargs) - return check_project_member - -@decorator_with_arguments -def project_stage_delete_permission(function=None,*args,**kwargs): - def is_project_manager(request,stage_id=None,*args, **kwargs,): - """ - This method is used to check the employee is project stage manager or not - """ - project = ProjectStage.objects.get(id = stage_id).project - if ( - request.user.employee_get == project.manager or - request.user.has_perm('project.delete_project') - ): - return function(request, *args,stage_id=stage_id, **kwargs) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - return is_project_manager - -@decorator_with_arguments -def task_update_permission(function=None,*args,**kwargs): - def is_task_member(request,task_id): - """ - This method is used to check the employee is task member or not - """ - task = Task.objects.get(id = task_id) - project = task.project - - if (request.user.has_perm('project.change_task') or - request.user.has_perm('project.change_project') or - request.user.employee_get == task.task_manager or - request.user.employee_get in task.task_members.all() or - request.user.employee_get == project.manager or - request.user.employee_get in project.members.all() - ): - return function(request, *args,task_id=task_id, **kwargs) - - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - return is_task_member - - -@decorator_with_arguments -def task_delete_permission(function=None,*args,**kwargs): - def is_task_manager(request,task_id): - """ - This method is used to check the employee is task manager or not - """ - task = Task.objects.get(id = task_id) - project = task.project - - if (request.user.has_perm('project.delete_task') or - request.user.has_perm('project.delete_project') or - request.user.employee_get == task.task_manager or - request.user.employee_get == project.manager - ): - return function(request,task_id=task_id) - - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - return is_task_manager \ No newline at end of file +from django.contrib import messages +from django.http import HttpResponse + +from project.methods import ( + any_project_manager, + any_project_member, + any_task_manager, + any_task_member, +) + +from .models import Project, ProjectStage, Task + +decorator_with_arguments = ( + lambda decorator: lambda *args, **kwargs: lambda func: decorator( + func, *args, **kwargs + ) +) + + +@decorator_with_arguments +def is_projectmanager_or_member_or_perms(function, perm): + def _function(request, *args, **kwargs): + """ + This method is used to check the employee is project manager or not + """ + user = request.user + if ( + user.has_perm(perm) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ): + return function(request, *args, **kwargs) + messages.info(request, "You don't have permission.") + return HttpResponse("") + + return _function + + +@decorator_with_arguments +def project_update_permission(function=None, *args, **kwargs): + def check_project_member( + request, + project_id=None, + *args, + **kwargs, + ): + """ + This method is used to check the employee is project member or not + """ + project = Project.objects.get(id=project_id) + employee = request.user.employee_get + if ( + request.user.has_perm("project.change_project") + or employee in project.managers.all() + or employee in project.members.all() + or any( + employee in task.task_managers.all() for task in project.task_set.all() + ) + or any( + employee in task.task_members.all() for task in project.task_set.all() + ) + ): + return function(request, *args, project_id=project_id, **kwargs) + messages.info(request, "You dont have permission.") + return HttpResponse("") + # return function(request, *args, **kwargs) + + return check_project_member + + +@decorator_with_arguments +def project_delete_permission(function=None, *args, **kwargs): + def is_project_manager( + request, + project_id=None, + *args, + **kwargs, + ): + """ + This method is used to check the employee is project manager or not + """ + project = Project.objects.get(id=project_id) + if ( + request.user.employee_get in project.managers.all() + or request.user.is_superuser + ): + return function(request, *args, project_id=project_id, **kwargs) + messages.info(request, "You dont have permission.") + return HttpResponse("") + + return is_project_manager + + +@decorator_with_arguments +def project_stage_update_permission(function=None, *args, **kwargs): + def check_project_member( + request, + stage_id=None, + *args, + **kwargs, + ): + """ + This method is used to check the employee is project stage member or not + """ + project = ProjectStage.objects.get(id=stage_id).project + if ( + request.user.has_perm("project.change_project") + or request.user.has_perm("project.change_task") + or request.user.employee_get in project.managers.all() + or request.user.employee_get in project.members.all() + ): + return function(request, *args, stage_id=stage_id, **kwargs) + messages.info(request, "You dont have permission.") + return HttpResponse("") + # return function(request, *args, **kwargs) + + return check_project_member + + +@decorator_with_arguments +def project_stage_delete_permission(function=None, *args, **kwargs): + def is_project_manager( + request, + stage_id=None, + *args, + **kwargs, + ): + """ + This method is used to check the employee is project stage manager or not + """ + project = ProjectStage.objects.get(id=stage_id).project + if ( + request.user.employee_get in project.managers.all() + or request.user.is_superuser + ): + return function(request, *args, stage_id=stage_id, **kwargs) + messages.info(request, "You dont have permission.") + return HttpResponse("") + + return is_project_manager + + +@decorator_with_arguments +def task_update_permission(function=None, *args, **kwargs): + def is_task_member(request, task_id): + """ + This method is used to check the employee is task member or not + """ + task = Task.objects.get(id=task_id) + project = task.project + + if ( + request.user.has_perm("project.change_task") + or request.user.has_perm("project.change_project") + or request.user.employee_get in task.task_managers.all() + or request.user.employee_get in task.task_members.all() + or request.user.employee_get in project.managers.all() + or request.user.employee_get in project.members.all() + ): + return function(request, *args, task_id=task_id, **kwargs) + + messages.info(request, "You dont have permission.") + return HttpResponse("") + + return is_task_member + + +@decorator_with_arguments +def task_delete_permission(function=None, *args, **kwargs): + def is_task_manager(request, task_id): + """ + This method is used to check the employee is task manager or not + """ + task = Task.objects.get(id=task_id) + project = task.project + + if ( + request.user.is_superuser + or request.user.employee_get in task.task_managers.all() + or request.user.employee_get in project.managers.all() + ): + return function(request, task_id=task_id) + + messages.info(request, "You dont have permission.") + return HttpResponse("") + + return is_task_manager diff --git a/project/filters.py b/project/filters.py index de8049214..b52e91fa0 100644 --- a/project/filters.py +++ b/project/filters.py @@ -1,27 +1,28 @@ import django_filters from django import forms from django.db.models import Q -import django_filters -from attendance.filters import FilterSet -from horilla.filters import filter_by_name -from .models import TimeSheet, Project, Task,Employee + +from horilla.filters import FilterSet, HorillaFilterSet, filter_by_name + +from .models import Employee, Project, Task, TimeSheet -class ProjectFilter(FilterSet): +class ProjectFilter(HorillaFilterSet): search = django_filters.CharFilter(method="filter_by_project") + search_field = django_filters.CharFilter(method="search_in") class Meta: model = Project - fields = ["title", - "manager", - "status", - "end_date", - "start_date", - ] - - manager = django_filters.ModelChoiceFilter( - field_name = 'manager',queryset=Employee.objects.all() - ) + fields = [ + "title", + "managers", + "members", + "status", + "end_date", + "start_date", + "is_active", + ] + start_from = django_filters.DateFilter( field_name="start_date", lookup_expr="gte", @@ -32,16 +33,18 @@ class Meta: lookup_expr="lte", widget=forms.DateInput(attrs={"type": "date"}), ) - + def filter_by_project(self, queryset, _, value): + if self.data.get("search_field"): + return queryset queryset = queryset.filter(title__icontains=value) return queryset class TaskFilter(FilterSet): search = django_filters.CharFilter(method="filter_by_task") - task_manager = django_filters.ModelChoiceFilter( - field_name = 'task_manager',queryset=Employee.objects.all() + task_managers = django_filters.ModelChoiceFilter( + field_name="task_managers", queryset=Employee.objects.all() ) end_till = django_filters.DateFilter( field_name="end_date", @@ -51,23 +54,22 @@ class TaskFilter(FilterSet): class Meta: model = Task - fields = ["title", - "stage", - "task_manager", - "end_date", - "status", - "project", - ] + fields = [ + "title", + "stage", + "task_managers", + "end_date", + "status", + "project", + ] def filter_by_task(self, queryset, _, value): queryset = queryset.filter(title__icontains=value) return queryset - -class TaskAllFilter(FilterSet): + + +class TaskAllFilter(HorillaFilterSet): search = django_filters.CharFilter(method="filter_by_task") - manager = django_filters.ModelChoiceFilter( - field_name = 'task_manager',queryset=Employee.objects.all() - ) end_till = django_filters.DateFilter( field_name="end_date", lookup_expr="lte", @@ -76,21 +78,22 @@ class TaskAllFilter(FilterSet): class Meta: model = Task - fields = ["title", - 'project', - "stage", - "task_manager", - "end_date", - "status", - ] + fields = [ + "title", + "project", + "stage", + "task_managers", + "task_members", + "end_date", + "status", + ] def filter_by_task(self, queryset, _, value): queryset = queryset.filter(title__icontains=value) return queryset - -class TimeSheetFilter(FilterSet): +class TimeSheetFilter(HorillaFilterSet): """ Filter set class for Timesheet model """ @@ -109,9 +112,11 @@ class TimeSheetFilter(FilterSet): widget=forms.DateInput(attrs={"type": "date"}), ) - project = django_filters.ModelChoiceFilter( - field_name="project_id", queryset=Project.objects.all() - ), + project = ( + django_filters.ModelChoiceFilter( + field_name="project_id", queryset=Project.objects.all() + ), + ) task = django_filters.ModelChoiceFilter( field_name="task_id", queryset=Task.objects.all() @@ -131,7 +136,7 @@ class Meta: "date", "status", ] - + def filter_by_employee(self, queryset, _, value): """ Filter queryset by first name or last name. @@ -159,4 +164,3 @@ def filter_by_employee(self, queryset, _, value): employee_id__employee_last_name__icontains=last_name ) return queryset - diff --git a/project/forms.py b/project/forms.py index 158caab5d..e2c3e3ef9 100644 --- a/project/forms.py +++ b/project/forms.py @@ -1,14 +1,23 @@ +from typing import Any + from django import forms +from django.db.models import Q +from django.template.loader import render_to_string from django.utils.translation import gettext_lazy as _ + +from base.forms import ModelForm +from horilla.horilla_middlewares import _thread_locals + from .models import * -from payroll.forms.forms import ModelForm -from django.template.loader import render_to_string class ProjectForm(ModelForm): """ Form for Project model """ + + cols = {"description": 12} + class Meta: """ Meta class to add the additional info @@ -26,6 +35,7 @@ class ProjectTimeSheetForm(ModelForm): """ Form for Project model in Time sheet form """ + def __init__(self, *args, **kwargs): super(ProjectTimeSheetForm, self).__init__(*args, **kwargs) self.fields["status"].widget.attrs.update( @@ -34,12 +44,13 @@ def __init__(self, *args, **kwargs): "class": "oh-select", } ) + def __init__(self, *args, request=None, **kwargs): super(ProjectTimeSheetForm, self).__init__(*args, **kwargs) - self.fields["manager"].widget.attrs.update({"id":"manager_id"}) - self.fields["status"].widget.attrs.update({"id":"status_id"}) - self.fields["members"].widget.attrs.update({"id":"members_id"}) - self.fields['title'].widget.attrs.update({'id':'id_project'}) + self.fields["managers"].widget.attrs.update({"id": "managers_id"}) + self.fields["status"].widget.attrs.update({"id": "status_id"}) + self.fields["members"].widget.attrs.update({"id": "members_id"}) + self.fields["title"].widget.attrs.update({"id": "id_project"}) class Meta: """ @@ -54,7 +65,6 @@ class Meta: } - class TaskForm(ModelForm): """ Form for Task model @@ -71,16 +81,36 @@ class Meta: widgets = { "end_date": forms.DateInput(attrs={"type": "date"}), - "project":forms.HiddenInput(), - "stage":forms.HiddenInput(), - "sequence":forms.HiddenInput() - } - + "project": forms.HiddenInput(), + "stage": forms.HiddenInput(), + "sequence": forms.HiddenInput(), + } + + +class QuickTaskForm(ModelForm): + class Meta: + model = Task + fields = ["title", "task_managers", "project", "stage", "end_date"] + widgets = { + "project": forms.HiddenInput(), + "stage": forms.HiddenInput(), + "end_date": forms.HiddenInput(), + } + + def __init__(self, *args, **kwargs): + super(QuickTaskForm, self).__init__(*args, **kwargs) + + self.fields["title"].widget.attrs.update( + {"class": "oh-input w-100 mb-2", "placeholder": _("Task Title")} + ) + self.fields["task_managers"].required = True + + class TaskFormCreate(ModelForm): - """ Form for Task model in create button inside task view """ + class Meta: """ Meta class to add the additional info @@ -92,19 +122,19 @@ class Meta: widgets = { "end_date": forms.DateInput(attrs={"type": "date"}), - "project":forms.HiddenInput(), - "sequence":forms.HiddenInput(), + "project": forms.HiddenInput(), + "sequence": forms.HiddenInput(), "stage": forms.SelectMultiple( attrs={ "class": "oh-select oh-select-2 select2-hidden-accessible", "onchange": "keyResultChange($(this))", } ), - } - + } + def __init__(self, *args, request=None, **kwargs): super(TaskFormCreate, self).__init__(*args, **kwargs) - self.fields["stage"].widget.attrs.update({"id":"project_stage"}) + self.fields["stage"].widget.attrs.update({"id": "project_stage"}) def as_p(self): """ @@ -113,33 +143,71 @@ def as_p(self): context = {"form": self} table_html = render_to_string("common_form.html", context) return table_html - + + class TaskAllForm(ModelForm): """ Form for Task model in task all view """ + + cols = { + "description": 12, + } + class Meta: """ Meta class to add the additional info """ + model = Task fields = "__all__" - widgets= { + widgets = { + "start_date": forms.DateInput(attrs={"type": "date"}), "end_date": forms.DateInput(attrs={"type": "date"}), - "sequence":forms.HiddenInput() + "sequence": forms.HiddenInput(), } + def __init__(self, *args, request=None, **kwargs): super(TaskAllForm, self).__init__(*args, **kwargs) - self.fields["project"].choices = list(self.fields["project"].choices) - self.fields["project"].choices.append( - ("create_new_project", "Create a new project") - ) - # Remove the select2 class from the "project" field - # self.fields["project"].widget.attrs.pop("class", None) - # self.fields["stage"].widget.attrs.pop("class", None) - self.fields["stage"].widget.attrs.update({"id":"project_stage"}) + request = getattr(_thread_locals, "request") + self.fields["stage"].widget.attrs.update({"id": "project_stage"}) + self.fields["project"].widget.attrs.update( + { + "onchange": """ + $('[name=dynamic_project]').val(this.value); + setTimeout(() => { + $('#getStageButton').click(); + }, 100); +""" + } + ) + + request = getattr(_thread_locals, "request", None) + employee = request.user.employee_get + if not self.instance.pk: + if request.user.is_superuser: + projects = Project.objects.all() + elif Project.objects.filter(managers=employee).exists(): + projects = Project.objects.filter(managers=employee) + self.fields["project"].queryset = projects + + else: + task = self.instance + if request.user.is_superuser: + projects = Project.objects.all() + elif employee in task.project.managers.all(): + projects = Project.objects.filter(managers=employee) + elif employee in task.task_managers.all(): + # Limit fields accessible to task managers + projects = Project.objects.filter(id=self.instance.project.id) + self.fields["project"].disabled = True + self.fields["stage"].disabled = True + self.fields["task_managers"].disabled = True + else: + projects = Project.objects.filter(id=self.instance.project.id) + self.fields["project"].queryset = projects class TimeSheetForm(ModelForm): @@ -147,6 +215,8 @@ class TimeSheetForm(ModelForm): Form for Time Sheet model """ + cols = {"description": 12} + class Meta: """ Meta class to add the additional info @@ -160,37 +230,63 @@ class Meta: def __init__(self, *args, request=None, **kwargs): super(TimeSheetForm, self).__init__(*args, **kwargs) - if request: - if not request.user.has_perm("project.add_timesheet"): - employee = Employee.objects.filter(employee_user_id=request.user) - employee_list = Employee.objects.filter(employee_work_info__reporting_manager_id=employee.first()) - self.fields["employee_id"].queryset = employee_list | employee - if len(employee_list) == 0: - self.fields["employee_id"].widget = forms.HiddenInput() - self.fields['project_id'].widget.attrs.update({'id':'id_project'}) - self.fields["project_id"].choices = list(self.fields["project_id"].choices) - self.fields["project_id"].choices.append( - ("create_new_project", "Create a new project") + request = getattr(_thread_locals, "request", None) + employee = request.user.employee_get + hx_trigger_value = "change" if self.instance.id else "load,change" + if not self.initial.get("project_id") == "dynamic_create": + self.fields["project_id"].widget.attrs.update( + { + "hx-target": "#id_task_id_parent_div", + "hx-trigger": hx_trigger_value, + "hx-include": "#id_task_id", + "hx-swap": "innerHTML", + "hx-get": "/project/get-tasks-of-project/", + } ) + self.fields["task_id"].widget.attrs.update( + { + "hx-target": "#id_employee_id_parent_div", + "hx-include": "#id_project_id", + "hx-trigger": hx_trigger_value, + "hx-swap": "innerHTML", + "hx-get": "/project/get-members-of-project/", + } + ) + + if not request.user.has_perm("project.add_timesheet"): + projects = Project.objects.filter( + Q(managers=employee) + | Q(members=employee) + | Q(task__task_members=employee) + | Q(task__task_managers=employee) + ).distinct() + self.fields["project_id"].queryset = projects + class TimesheetInTaskForm(ModelForm): class Meta: """ Meta class to add the additional info """ + model = TimeSheet fields = "__all__" widgets = { "date": forms.DateInput(attrs={"type": "date"}), - "project_id":forms.HiddenInput(), - "task_id":forms.HiddenInput(), + "project_id": forms.HiddenInput(), + "task_id": forms.HiddenInput(), } + class ProjectStageForm(ModelForm): """ Form for Project stage model """ + cols = { + "title": 12, + } + class Meta: """ Meta class to add the additional info @@ -200,15 +296,14 @@ class Meta: fields = "__all__" # exclude = ("project",) - widgets = { - "project":forms.HiddenInput(), - 'sequence':forms.HiddenInput() - } - + widgets = {"project": forms.HiddenInput()} + + class TaskTimeSheetForm(ModelForm): """ Form for Task model in timesheet form """ + class Meta: """ Meta class to add the additional info @@ -231,7 +326,6 @@ def __init__(self, *args, **kwargs): { "style": "width: 100%; height: 47px;", "class": "oh-select", - } ) self.fields["description"].widget.attrs.update( @@ -246,14 +340,5 @@ def __init__(self, *args, **kwargs): "class": "oh-select", } ) - - self.fields["stage"].widget.attrs.update( - { - "id":'project_stage' - } - ) - - - - + self.fields["stage"].widget.attrs.update({"id": "project_stage"}) diff --git a/project/methods.py b/project/methods.py index 26372310d..0b3bf186c 100644 --- a/project/methods.py +++ b/project/methods.py @@ -1,196 +1,239 @@ -import random -from django.core.paginator import Paginator -from django.contrib import messages -from django.http import HttpResponseRedirect -from base.methods import get_pagination -from recruitment.decorators import decorator_with_arguments -from employee.models import Employee -from project.models import TimeSheet,Project,Task - - -def strtime_seconds(time): - """ - this method is used to reconvert time in H:M formate string back to seconds and return it - args: - time : time in H:M format - """ - ftr = [3600, 60, 1] - return sum(a * b for a, b in zip(ftr, map(int, time.split(":")))) - - -def paginator_qry(qryset, page_number): - """ - This method is used to generate common paginator limit. - """ - paginator = Paginator(qryset, get_pagination()) - qryset = paginator.get_page(page_number) - return qryset - - -def random_color_generator(): - r = random.randint(0, 255) - g = random.randint(0, 255) - b = random.randint(0, 255) - if r==g or g==b or b==r: - random_color_generator() - return f"rgba({r}, {g}, {b} , 0.7)" - - -# color_palette=[] -# Function to generate distinct colors for each project -def generate_colors(num_colors): - # Define a color palette with distinct colors - color_palette = [ - "rgba(255, 99, 132, 1)", # Red - "rgba(54, 162, 235, 1)", # Blue - "rgba(255, 206, 86, 1)", # Yellow - "rgba(75, 192, 192, 1)", # Green - "rgba(153, 102, 255, 1)", # Purple - "rgba(255, 159, 64, 1)", # Orange - ] - - if num_colors > len(color_palette): - for i in range(num_colors-len(color_palette)): - color_palette.append(random_color_generator()) - - colors = [] - for i in range(num_colors): - # color=random_color_generator() - colors.append(color_palette[i % len(color_palette)]) - - return colors - -def any_project_manager(user): - employee = user.employee_get - if employee.project_manager.all().exists(): - return True - else: - return False - -def any_project_member(user): - employee = user.employee_get - if employee.project_members.all().exists(): - return True - else: - return False - -def any_task_manager(user): - employee = user.employee_get - if employee.task_set.all().exists(): - return True - else: - return False - -def any_task_member(user): - employee = user.employee_get - if employee.tasks.all().exists(): - return True - else: - return False - -@decorator_with_arguments -def is_projectmanager_or_member_or_perms(function,perm): - def _function(request, *args, **kwargs): - - """ - This method is used to check the employee is project manager or not - """ - user = request.user - if ( - user.has_perm(perm) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ): - return function(request, *args, **kwargs) - messages.info(request, "You don't have permission.") - return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) - return _function - - -# def is_project_member(request,project_id): -# """ -# This method is used to check the employee is project member or not -# """ -# print(Project.objects.get(id = project_id)) -# print(Project.objects.get(id = project_id).members.all() ) -# if (request.user.has_perm('project.change_project') or -# request.user.employee_get == Project.objects.get(id = project_id).manager or -# request.user.employee_get in Project.objects.get(id = project_id).members.all() -# ): -# return True -# return False - -# def is_project_manager(request,project_id): -# """ -# This method is used to check the employee is project manager or not -# """ -# print(Project.objects.get(id = project_id)) -# print(Project.objects.get(id = project_id).manager ) -# if ( -# request.user.employee_get == Project.objects.get(id = project_id).manager or -# request.user.has_perm('project.delete_project') - -# ): -# return True -# return False - - -# def is_project_manager(request, project_id): -# """ -# This function checks if the user is a project manager or has permission to delete a project. -# """ -# user = request.user -# try: -# project = Project.objects.get(id=project_id) -# except Project.DoesNotExist: -# return False # Project with the specified ID does not exist - -# if user.employee_get == project.manager or user.has_perm('project.delete_project'): -# return True # User is a project manager or has delete_project permission - -# return False # User does not have the required permission - - -def is_task_member(request,task_id): - """ - This method is used to check the employee is task member or not - """ - if (request.user.has_perm('project.change_task') or - request.user.employee_get == Task.objects.get(id = task_id).task_manager or - request.user.employee_get in Task.objects.get(id = task_id).task_members.all() - ): - return True - return False - -def is_task_manager(request,task_id): - """ - This method is used to check the employee is task member or not - """ - if (request.user.has_perm('project.delete_task') or - request.user.employee_get == Task.objects.get(id = task_id).task_manager - ): - return True - return False - - -def time_sheet_update_permissions(request,time_sheet_id): - if ( - request.user.has_perm("project.change_timesheet") - or request.user.employee_get == TimeSheet.objects.get(id=time_sheet_id).employee_id - or TimeSheet.objects.get(id=time_sheet_id).employee_id in Employee.objects.filter(employee_work_info__reporting_manager_id=request.user.employee_get) - ): - return True - else: - return False - -def time_sheet_delete_permissions(request,time_sheet_id): - if ( - request.user.has_perm("project.delete_timesheet") - or request.user.employee_get == TimeSheet.objects.get(id=time_sheet_id).employee_id - or TimeSheet.objects.get(id=time_sheet_id).employee_id in Employee.objects.filter(employee_work_info__reporting_manager_id=request.user.employee_get) - ): - return True - else: - return False \ No newline at end of file +import random + +from django.contrib import messages +from django.core.paginator import Paginator +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render + +from base.methods import get_pagination, get_subordinates +from employee.models import Employee +from project.models import Project, Task, TimeSheet + +decorator_with_arguments = ( + lambda decorator: lambda *args, **kwargs: lambda func: decorator( + func, *args, **kwargs + ) +) + + +def strtime_seconds(time): + """ + this method is used to reconvert time in H:M formate string back to seconds and return it + args: + time : time in H:M format + """ + ftr = [3600, 60, 1] + return sum(a * b for a, b in zip(ftr, map(int, time.split(":")))) + + +def paginator_qry(qryset, page_number): + """ + This method is used to generate common paginator limit. + """ + paginator = Paginator(qryset, get_pagination()) + qryset = paginator.get_page(page_number) + return qryset + + +def random_color_generator(): + r = random.randint(0, 255) + g = random.randint(0, 255) + b = random.randint(0, 255) + if r == g or g == b or b == r: + random_color_generator() + return f"rgba({r}, {g}, {b} , 0.7)" + + +# color_palette=[] +# Function to generate distinct colors for each project +def generate_colors(num_colors): + # Define a color palette with distinct colors + color_palette = [ + "rgba(255, 99, 132, 1)", # Red + "rgba(54, 162, 235, 1)", # Blue + "rgba(255, 206, 86, 1)", # Yellow + "rgba(75, 192, 192, 1)", # Green + "rgba(153, 102, 255, 1)", # Purple + "rgba(255, 159, 64, 1)", # Orange + ] + + if num_colors > len(color_palette): + for i in range(num_colors - len(color_palette)): + color_palette.append(random_color_generator()) + + colors = [] + for i in range(num_colors): + # color=random_color_generator() + colors.append(color_palette[i % len(color_palette)]) + + return colors + + +def any_project_manager(user): + employee = user.employee_get + if employee.project_managers.all().exists(): + return True + else: + return False + + +def any_project_member(user): + employee = user.employee_get + if employee.project_members.all().exists(): + return True + else: + return False + + +def any_task_manager(user): + employee = user.employee_get + if employee.task_set.all().exists(): + return True + else: + return False + + +def any_task_member(user): + employee = user.employee_get + if employee.tasks.all().exists(): + return True + else: + return False + + +@decorator_with_arguments +def is_projectmanager_or_member_or_perms(function, perm): + def _function(request, *args, **kwargs): + """ + This method is used to check the employee is project manager or not + """ + user = request.user + if ( + user.has_perm(perm) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ): + return function(request, *args, **kwargs) + messages.info(request, "You don't have permission.") + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) + + return _function + + +def is_task_member(request, task_id): + """ + This method is used to check the employee is task member or not + """ + if ( + request.user.has_perm("project.change_task") + or request.user.employee_get in Task.objects.get(id=task_id).task_managers.all() + or request.user.employee_get in Task.objects.get(id=task_id).task_members.all() + ): + return True + return False + + +def is_task_manager(request, task_id): + """ + This method is used to check the employee is task member or not + """ + if ( + request.user.has_perm("project.delete_task") + or request.user.employee_get in Task.objects.get(id=task_id).task_managers.all() + ): + return True + return False + + +def time_sheet_update_permissions(request, time_sheet_id): + if ( + request.user.has_perm("project.change_timesheet") + or request.user.employee_get + == TimeSheet.objects.get(id=time_sheet_id).employee_id + or TimeSheet.objects.get(id=time_sheet_id).employee_id + in Employee.objects.filter( + employee_work_info__reporting_manager_id=request.user.employee_get + ) + ): + return True + else: + return False + + +def time_sheet_delete_permissions(request, time_sheet_id): + employee = request.user.employee_get + timesheet = TimeSheet.objects.filter(id=time_sheet_id).first() + if ( + request.user.has_perm("project.delete_timesheet") + or timesheet.employee_id == employee + or employee in timesheet.task_id.task_managers.all() + or employee in timesheet.task_id.project.managers.all() + ): + return True + else: + return False + + +def get_all_project_members_and_managers(): + all_projects = Project.objects.all() + all_tasks = Task.objects.all() + + all_ids = set() + + for project in all_projects: + all_ids.update( + manager.id for manager in project.managers.all() + ) # Add manager ID + all_ids.update(member.id for member in project.members.all()) # Add member IDs + + for task in all_tasks: + all_ids.update( + task_manager.id for task_manager in task.task_managers.all() + ) # Add task manager ID + all_ids.update( + task_member.id for task_member in task.task_members.all() + ) # Add task member IDs + + # Return a single queryset for all employees + return Employee.objects.filter(id__in=all_ids) + + +def has_subordinates(request): + """ + used to check whether the project contain users subordinates or not + """ + all_members_info = get_all_project_members_and_managers() + subordinates = get_subordinates(request) + + member = {member for member in all_members_info} + + for subordinate in subordinates: + if subordinate in member: + return True + + return False + + +def is_project_manager_or_super_user(request, project): + """ + Method to check whether user is a manager of project or + user is a super user. + """ + return ( + request.user.employee_get in project.managers.all() or request.user.is_superuser + ) + + +def you_dont_have_permission(request): + """ + Method to return you dont have permission + """ + messages.info(request, "You dont have permission.") + previous_url = request.META.get("HTTP_REFERER", "/") + key = "HTTP_HX_REQUEST" + if key in request.META.keys(): + return render(request, "decorator_404.html") + script = f'' + return HttpResponse(script) diff --git a/project/models.py b/project/models.py index 44bd138b2..a148e750b 100644 --- a/project/models.py +++ b/project/models.py @@ -1,249 +1,617 @@ -""" -models.py - -This module is used to register models for project app - -""" -from django.db import models -from django.apps import apps -from employee.models import Employee -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ -from django.utils import timezone -from employee.models import Employee -from datetime import date - - - -# Create your models here. - -def validate_time_format(value): - """ - this method is used to validate the format of duration like fields. - """ - if len(value) > 5: - raise ValidationError(_("Invalid format, it should be HH:MM format")) - try: - hour, minute = value.split(":") - - if len(hour) < 2 or len(minute) < 2: - raise ValidationError(_( "Invalid format, it should be HH:MM format")) - - minute = int(minute) - if len(hour) > 2 or minute not in range(60): - raise ValidationError(_("Invalid time")) - except ValueError as error: - raise ValidationError(_("Invalid format")) from error - - - -class Project(models.Model): - PROJECT_STATUS = [ - ("new", "New"), - ("in_progress", "In Progress"), - ("completed", "Completed"), - ("on_hold", "On Hold"), - ("cancelled", "Cancelled"), - ("expired", "Expired"), - ] - title = models.CharField(max_length=200, unique=True, verbose_name="Name") - manager = models.ForeignKey( - Employee, - on_delete=models.CASCADE, - null=True, - blank=True, - related_name="project_manager", - verbose_name="Project Manager", - ) - members = models.ManyToManyField( - Employee, - blank=True, - related_name="project_members", - verbose_name="Project Members", - ) - status = models.CharField(choices=PROJECT_STATUS, max_length=250, default="new") - start_date = models.DateField() - end_date = models.DateField(null=True, blank=True) - document = models.FileField( - upload_to="project/files", blank=True, null=True, verbose_name="Project File" - ) - is_active = models.BooleanField(default=True) - description = models.TextField() - objects = models.Manager() - - - - def clean(self) -> None: - # validating end date - if self.end_date is not None: - if self.end_date < self.start_date: - raise ValidationError({"document": "End date is less than start date"}) - if self.end_date < date.today(): - self.status = "expired" - - def __str__(self): - return self.title - - - -class ProjectStage(models.Model): - """ - ProjectStage model - """ - title = models.CharField(max_length=200) - project = models.ForeignKey( - Project, - on_delete=models.CASCADE, - null=True, - blank=True, - related_name="project_stages", - ) - sequence = models.IntegerField(null=True,blank=True) - is_end_stage = models.BooleanField(default=False) - - - def __str__(self) -> str: - return f"{self.title}" - - def clean(self) -> None: - if self.is_end_stage: - project = self.project - existing_end_stage = project.project_stages.filter(is_end_stage = True).exclude(id = self.id) - - if existing_end_stage: - end_stage = project.project_stages.filter(is_end_stage = True).first() - raise ValidationError( - _(f"Already exist an end stage - {end_stage.title}.") - ) - - - class Meta: - """ - Meta class to add the additional info - """ - - unique_together = ["project", "title"] - - -class Task(models.Model): - """ - Task model - """ - TASK_STATUS = [ - ("to_do", "To Do"), - ("in_progress", "In Progress"), - ("completed", "Completed"), - ("expired", "Expired"), - ] - title = models.CharField(max_length=200) - project = models.ForeignKey(Project, on_delete=models.CASCADE, null = True, blank= True) - stage = models.ForeignKey( - ProjectStage, - on_delete=models.CASCADE, - null=True, - blank=True, - related_name="tasks", - verbose_name="Project Stage", - ) - task_manager = models.ForeignKey( - Employee, - on_delete=models.CASCADE, - null=True, - blank=True, - verbose_name="Task Manager", - ) - task_members = models.ManyToManyField( - Employee, blank=True, related_name="tasks", verbose_name="Task Members" - ) - start_date = models.DateField(null=True, blank=True) - end_date = models.DateField(null=True, blank=True) - status = models.CharField(choices=TASK_STATUS, max_length=250, default="to_do") - document = models.FileField( - upload_to="task/files", blank=True, null=True, verbose_name="Task File" - ) - is_active = models.BooleanField(default=True) - description = models.TextField() - sequence = models.IntegerField(default=0) - objects = models.Manager() - - - def clean(self) -> None: - if self.end_date is not None and self.project.end_date is not None: - if ( - self.project.end_date < self.end_date - or self.project.start_date > self.end_date - ): - raise ValidationError( - {"status": "End date must be between project start and end dates."} - ) - if self.end_date < date.today(): - self.status = "expired" - class Meta: - """ - Meta class to add the additional info - """ - unique_together = ["project", "title"] - - def __str__(self): - return f"{self.title}" - - - - - -class TimeSheet(models.Model): - """ - TimeSheet model - """ - TIME_SHEET_STATUS = [ - ("in_Progress", "In Progress"), - ("completed", "Completed"), - ] - project_id = models.ForeignKey( - Project, - on_delete=models.CASCADE, - blank=True, - null=True, - related_name="project_timesheet", - verbose_name="Project", - ) - task_id = models.ForeignKey( - Task, - on_delete=models.CASCADE, - blank=True, - null=True, - related_name="task_timesheet", - verbose_name="Task", - ) - employee_id = models.ForeignKey( - Employee, - on_delete=models.CASCADE, - verbose_name="Employee", - ) - date = models.DateField(default=timezone.now) - time_spent = models.CharField( - null=True, - default="00:00", - max_length=10, - validators=[validate_time_format], - verbose_name="Hours Spent", - ) - status = models.CharField( - choices=TIME_SHEET_STATUS, max_length=250, default="in_Progress" - ) - description = models.TextField(blank=True, null=True) - objects = models.Manager() - - class Meta: - ordering = ("-id",) - - def clean(self): - if self.project_id is None: - raise ValidationError({"project_id": "Project name is Required."}) - if self.description is None or self.description == "": - raise ValidationError( - {"description": "Please provide a description to your Time sheet"} - ) - - def __str__(self): - return f"{self.project_id} {self.date} {self.time_spent}" +""" +models.py + +This module is used to register models for project app + +""" + +import datetime +from datetime import date + +from django.apps import apps +from django.core.exceptions import ValidationError +from django.db import models +from django.templatetags.static import static +from django.urls import reverse, reverse_lazy +from django.utils import timezone +from django.utils.html import format_html +from django.utils.translation import gettext_lazy as _ + +from employee.models import Employee +from horilla.horilla_middlewares import _thread_locals +from horilla_views.cbv_methods import render_template + +# Create your models here. + + +def validate_time_format(value): + """ + this method is used to validate the format of duration like fields. + """ + if len(value) > 5: + raise ValidationError(_("Invalid format, it should be HH:MM format")) + try: + hour, minute = value.split(":") + + if len(hour) < 2 or len(minute) < 2: + raise ValidationError(_("Invalid format, it should be HH:MM format")) + + minute = int(minute) + if len(hour) > 2 or minute not in range(60): + raise ValidationError(_("Invalid time")) + except ValueError as error: + raise ValidationError(_("Invalid format")) from error + + +class Project(models.Model): + PROJECT_STATUS = [ + ("new", "New"), + ("in_progress", "In Progress"), + ("completed", "Completed"), + ("on_hold", "On Hold"), + ("cancelled", "Cancelled"), + ("expired", "Expired"), + ] + title = models.CharField(max_length=200, unique=True, verbose_name="Name") + managers = models.ManyToManyField( + Employee, + blank=True, + related_name="project_managers", + verbose_name="Project Managers", + ) + members = models.ManyToManyField( + Employee, + blank=True, + related_name="project_members", + verbose_name="Project Members", + ) + status = models.CharField(choices=PROJECT_STATUS, max_length=250, default="new") + start_date = models.DateField() + end_date = models.DateField(null=True, blank=True) + document = models.FileField( + upload_to="project/files", blank=True, null=True, verbose_name="Project File" + ) + is_active = models.BooleanField(default=True) + description = models.TextField() + objects = models.Manager() + + def get_description(self, length=50): + """ + Returns a truncated version of the description attribute. + + Parameters: + length (int): The maximum length of the returned description. + """ + return ( + self.description + if len(self.description) <= length + else self.description[:length] + "..." + ) + + def get_managers(self): + """ + managers column + """ + employees = self.managers.all() + if employees: + employee_names_string = "
".join( + [str(employee) for employee in employees] + ) + return employee_names_string + + def get_members(self): + """ + members column + """ + employees = self.members.all() + if employees: + employee_names_string = "
".join( + [str(employee) for employee in employees] + ) + return employee_names_string + + def get_avatar(self): + """ + Method will retun the api to the avatar or path to the profile image + """ + url = f"https://ui-avatars.com/api/?name={self.title}&background=random" + return url + + def get_document_html(self): + if self.document: + document_url = self.document.url + image_url = static("images/ui/project/document.png") + return format_html( + '' + '' + "  View" + "", + document_url, + image_url, + ) + + def redirect(self): + """ + This method generates an onclick URL for task viewing. + """ + request = getattr(_thread_locals, "request", None) + employee = request.user.employee_get + url = reverse_lazy("task-view", kwargs={"project_id": self.pk}) + + if ( + employee in self.managers.all() + or employee in self.members.all() + or any(employee in task.task_managers.all() for task in self.task_set.all()) + or any(employee in task.task_members.all() for task in self.task_set.all()) + or request.user.has_perm("project.view_project") + ): + return f"onclick=\"window.location.href='{url}?view=list'\"" + return "" + + def get_detail_url(self): + """ + This method to get detail url + """ + url = reverse_lazy("project-detailed-view", kwargs={"pk": self.pk}) + return url + + def get_update_url(self): + """ + This method to get update url + """ + url = reverse_lazy("update-project", kwargs={"pk": self.pk}) + return url + + def get_archive_url(self): + """ + This method to get archive url + """ + url = reverse_lazy("project-archive", kwargs={"project_id": self.pk}) + return url + + def get_task_badge_html(self): + task_count = self.task_set.count() + title = self.title + return format_html( + '
' + '
' + ' ' + " {1}" + " " + "
" + "
{0}
" + "
", + title, + task_count, + ) + + def get_delete_url(self): + """ + This method to get delete url + """ + url = reverse_lazy("delete-project", kwargs={"project_id": self.pk}) + message = "Are you sure you want to delete this project?" + return f"'{url}'" + "," + f"'{message}'" + + def actions(self): + """ + This method for get custom column for action. + """ + + return render_template( + path="cbv/projects/actions.html", + context={"instance": self}, + ) + + def archive_status(self): + """ + archive status + """ + if self.is_active: + return "Archive" + else: + return "Un-Archive" + + def clean(self) -> None: + # validating end date + if self.end_date is not None: + if self.end_date < self.start_date: + raise ValidationError({"document": "End date is less than start date"}) + if self.end_date < date.today(): + self.status = "expired" + + def save(self, *args, **kwargs): + is_new = self.pk is None + super().save(*args, **kwargs) + + if is_new: + ProjectStage.objects.create( + title="Todo", + project=self, + sequence=1, + is_end_stage=False, + ) + + def __str__(self): + return self.title + + def status_column(self): + return dict(self.PROJECT_STATUS).get(self.status) + + +class ProjectStage(models.Model): + """ + ProjectStage model + """ + + title = models.CharField(max_length=200) + project = models.ForeignKey( + Project, + on_delete=models.CASCADE, + null=True, + blank=True, + related_name="project_stages", + ) + sequence = models.IntegerField(null=True, blank=True, editable=False) + is_end_stage = models.BooleanField(default=False) + + def __str__(self) -> str: + return f"{self.title}" + + def clean(self) -> None: + if self.is_end_stage: + project = self.project + existing_end_stage = project.project_stages.filter( + is_end_stage=True + ).exclude(id=self.id) + + if existing_end_stage: + end_stage = project.project_stages.filter(is_end_stage=True).first() + raise ValidationError( + _(f"Already exist an end stage - {end_stage.title}.") + ) + + def save(self, *args, **kwargs): + if self.sequence is None: + last_stage = ( + ProjectStage.objects.filter(project=self.project) + .order_by("sequence") + .last() + ) + if last_stage: + self.sequence = last_stage.sequence + 1 + super().save(*args, **kwargs) + + def delete(self, *args, **kwargs): + project_stages_after = ProjectStage.objects.filter( + project=self.project, sequence__gt=self.sequence + ) + + # Decrement the sequence of the following stages + for stage in project_stages_after: + stage.sequence -= 1 + stage.save() + + super().delete(*args, **kwargs) + + class Meta: + """ + Meta class to add the additional info + """ + + unique_together = ["project", "title"] + + +class Task(models.Model): + """ + Task model + """ + + TASK_STATUS = [ + ("to_do", "To Do"), + ("in_progress", "In Progress"), + ("completed", "Completed"), + ("expired", "Expired"), + ] + title = models.CharField(max_length=200) + project = models.ForeignKey(Project, on_delete=models.CASCADE, null=True) + stage = models.ForeignKey( + ProjectStage, + on_delete=models.CASCADE, + null=True, + related_name="tasks", + verbose_name="Project Stage", + ) + task_managers = models.ManyToManyField( + Employee, + blank=True, + verbose_name="Task Managers", + ) + task_members = models.ManyToManyField( + Employee, blank=True, related_name="tasks", verbose_name="Task Members" + ) + status = models.CharField(choices=TASK_STATUS, max_length=250, default="to_do") + start_date = models.DateField(null=True, blank=True) + end_date = models.DateField(null=True, blank=True) + document = models.FileField( + upload_to="task/files", blank=True, null=True, verbose_name="Task File" + ) + is_active = models.BooleanField(default=True) + description = models.TextField() + sequence = models.IntegerField(default=0) + objects = models.Manager() + + def clean(self) -> None: + if self.end_date is not None and self.project.end_date is not None: + if ( + self.project.end_date < self.end_date + or self.project.start_date > self.end_date + ): + raise ValidationError( + { + "end_date": _( + "The task end date must be between the project's start and end dates." + ) + } + ) + if self.end_date < date.today(): + self.status = "expired" + + class Meta: + """ + Meta class to add the additional info + """ + + unique_together = ["project", "title"] + + def __str__(self): + return f"{self.title}" + + def if_project(self): + """ + Return project if have,otherwise return none + """ + + return self.project if self.project else "None" + + def task_detail_view(self): + """ + detail view of task + """ + + url = reverse("task-detail-view", kwargs={"pk": self.pk}) + return url + + def status_column(self): + """ + to get status + """ + return dict(self.TASK_STATUS).get(self.status) + + def get_managers(self): + """ + return task managers + """ + managers = self.task_managers.all() + if managers: + managers_name_string = "
".join([str(manager) for manager in managers]) + return managers_name_string + else: + return "" + + def get_members(self): + """ + return task members + """ + members = self.task_members.all() + if members: + members_name_string = "
".join([str(member) for member in members]) + return members_name_string + else: + return "" + + def actions(self): + """ + This method for get custom column for action. + """ + # request = getattr(_thread_locals, "request", None) + # is_task_manager = self.task_manager == request.user + # print(self.title) + # is_project_manager = self.project.manager == request.user if self.project else False + # print(self.project) + # has_permission = request.user.has_perm('project.view_task') # Replace 'your_app' with your app name + + # if is_task_manager or is_project_manager or has_permission: + # return render_template( + # "cbv/tasks/task_actions.html", + # {"instance": self} + # ) + # else: + # return "" + + return render_template( + path="cbv/tasks/task_actions.html", + context={"instance": self}, + ) + + def get_avatar(self): + """ + Method will retun the api to the avatar or path to the profile image + """ + url = f"https://ui-avatars.com/api/?name={self.title}&background=random" + return url + + def document_col(self): + """ + This method for get custom document coloumn . + """ + + return render_template( + path="cbv/tasks/task_document.html", + context={"instance": self}, + ) + + def detail_view_actions(self): + """ + This method for get detail view actions. + """ + + return render_template( + path="cbv/tasks/task_detail_actions.html", + context={"instance": self}, + ) + + def get_update_url(self): + """ + to get the update url + """ + url = reverse("update-task-all", kwargs={"pk": self.pk}) + return url + + def archive_status(self): + """ + archive status + """ + if self.is_active: + return "Archive" + else: + return "Un-Archive" + + def get_archive_url(self): + """ + to get archive url + """ + + url = reverse("task-all-archive", kwargs={"task_id": self.pk}) + return url + + def get_delete_url(self): + """ + to get delete url + """ + + url = reverse("delete-task", kwargs={"task_id": self.pk}) + url_with_params = f"{url}?task_all=true" + message = "Are you sure you want to delete this task?" + return f"'{url_with_params}'" + "," + f"'{message}'" + + +class TimeSheet(models.Model): + """ + TimeSheet model + """ + + TIME_SHEET_STATUS = [ + ("in_Progress", "In Progress"), + ("completed", "Completed"), + ] + project_id = models.ForeignKey( + Project, + on_delete=models.CASCADE, + null=True, + related_name="project_timesheet", + verbose_name="Project", + ) + task_id = models.ForeignKey( + Task, + on_delete=models.CASCADE, + null=True, + related_name="task_timesheet", + verbose_name="Task", + ) + employee_id = models.ForeignKey( + Employee, + on_delete=models.CASCADE, + verbose_name="Employee", + ) + date = models.DateField(default=timezone.now) + time_spent = models.CharField( + null=True, + default="00:00", + max_length=10, + validators=[validate_time_format], + verbose_name="Hours Spent", + ) + status = models.CharField( + choices=TIME_SHEET_STATUS, max_length=250, default="in_Progress" + ) + description = models.TextField(blank=True, null=True) + objects = models.Manager() + + class Meta: + ordering = ("-id",) + + def clean(self): + if self.project_id is None: + raise ValidationError({"project_id": "Project name is Required."}) + if self.description is None or self.description == "": + raise ValidationError( + {"description": "Please provide a description to your Time sheet"} + ) + if self.employee_id: + employee = self.employee_id + if self.task_id: + task = self.task_id + if ( + not employee in task.task_managers.all() + and not employee in task.task_members.all() + and not employee in task.project.managers.all() + and not employee in task.project.members.all() + ): + raise ValidationError(_("Employee not included in this task")) + elif self.project_id: + if ( + not employee in self.project_id.managers.all() + and not employee in self.project_id.members.all() + ): + raise ValidationError(_("Employee not included in this project")) + if self.date > datetime.datetime.today().date(): + raise ValidationError({"date": _("You cannot choose a future date.")}) + + def __str__(self): + return f"{self.employee_id} {self.project_id} {self.task_id} {self.date} {self.time_spent}" + + def status_column(self): + return dict(self.TIME_SHEET_STATUS).get(self.status) + + def actions(self): + """ + This method for get custom column for action. + """ + + return render_template( + path="cbv/timesheet/actions.html", + context={"instance": self}, + ) + + def detail_actions(self): + """ + This method for get custom column for action. + """ + + return render_template( + path="cbv/timesheet/detail_actions.html", + context={"instance": self}, + ) + + def get_update_url(self): + """ + This method to get update url + """ + url = reverse_lazy("update-time-sheet", kwargs={"pk": self.pk}) + return url + + def get_delete_url(self): + """ + This method to get delete url + """ + url = reverse_lazy("delete-time-sheet", kwargs={"time_sheet_id": self.pk}) + message = "Are you sure you want to delete this time sheet?" + return f"'{url}'" + "," + f"'{message}'" + + def detail_view(self): + """ + for detail view of page + """ + url = reverse("time-sheet-detail-view", kwargs={"pk": self.pk}) + return url diff --git a/project/sidebar.py b/project/sidebar.py index 4baa544cf..fc77a8d64 100644 --- a/project/sidebar.py +++ b/project/sidebar.py @@ -1,105 +1,117 @@ -""" -project/sidebar.py -""" - -from django.urls import reverse -from base.templatetags.basefilters import is_reportingmanager -from django.utils.translation import gettext_lazy as trans -from django.contrib.auth.context_processors import PermWrapper - - -from project.methods import any_project_manager, any_project_member, any_task_manager, any_task_member - -MENU = trans("Project") -IMG_SRC = "images/ui/project.png" -ACCESSIBILITY = "project.sidebar.menu_accessibilty" - -SUBMENUS = [ - { - "menu": trans("Dashboard"), - "redirect": reverse("project-dashboard-view"), - "accessibility": "project.sidebar.dashboard_accessibility" - }, - { - "menu": trans("Projects"), - "redirect": reverse("project-view"), - "accessibility": "project.sidebar.project_accessibility", - }, - { - "menu": trans("Tasks"), - "redirect": reverse("task-all"), - "accessibility": "project.sidebar.task_accessibility", - }, - { - "menu": trans("Timesheet"), - "redirect": reverse("view-time-sheet"), - "accessibility": "project.sidebar.timesheet_accessibility", - }, -] - -def menu_accessibilty( - request, _menu: str = "", user_perms: PermWrapper = [], *args, **kwargs -) -> bool: - user = request.user - return ( - "project" in user_perms or - is_reportingmanager(user) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ) - -def dashboard_accessibility(request, submenu, user_perms, *args, **kwargs): - user = request.user - if ( - user.has_perm("project.view_project") or - is_reportingmanager(user) or - any_project_manager(user) or - any_task_manager(user) - ): - return True - else: - return False - -def project_accessibility(request, submenu, user_perms, *args, **kwargs): - user = request.user - if ( - user.has_perm("project.view_project") or - is_reportingmanager(user) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ): - return True - else: - return False - -def task_accessibility(request, submenu, user_perms, *args, **kwargs): - user = request.user - if ( - user.has_perm("project.view_task") or - is_reportingmanager(user) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ): - return True - else: - return False - -def timesheet_accessibility(request, submenu, user_perms, *args, **kwargs): - user = request.user - if ( - user.has_perm("project.view_timesheet") or - is_reportingmanager(user) or - any_project_manager(user) or - any_project_member(user) or - any_task_manager(user) or - any_task_member(user) - ): - return True - else: - return False \ No newline at end of file +""" +project/sidebar.py +""" + +from django.contrib.auth.context_processors import PermWrapper +from django.urls import reverse +from django.utils.translation import gettext_lazy as trans + +from base.templatetags.basefilters import is_reportingmanager +from project.methods import ( + any_project_manager, + any_project_member, + any_task_manager, + any_task_member, + get_all_project_members_and_managers, + has_subordinates, +) + +MENU = trans("Project") +IMG_SRC = "images/ui/project.png" +ACCESSIBILITY = "project.sidebar.menu_accessibilty" + +SUBMENUS = [ + { + "menu": trans("Dashboard"), + "redirect": reverse("project-dashboard-view"), + "accessibility": "project.sidebar.dashboard_accessibility", + }, + { + "menu": trans("Projects"), + "redirect": reverse("project-view"), + "accessibility": "project.sidebar.project_accessibility", + }, + { + "menu": trans("Tasks"), + "redirect": reverse("task-all"), + "accessibility": "project.sidebar.task_accessibility", + }, + { + "menu": trans("Timesheet"), + "redirect": reverse("view-time-sheet"), + "accessibility": "project.sidebar.timesheet_accessibility", + }, +] + + +def menu_accessibilty( + request, _menu: str = "", user_perms: PermWrapper = [], *args, **kwargs +) -> bool: + user = request.user + return ( + "project" in user_perms + # or has_subordinates(request) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ) + + +def dashboard_accessibility(request, submenu, user_perms, *args, **kwargs): + user = request.user + if ( + user.has_perm("project.view_project") + # or has_subordinates(request) + or is_reportingmanager(user) + or any_project_manager(user) + or any_task_manager(user) + ): + return True + else: + return False + + +def project_accessibility(request, submenu, user_perms, *args, **kwargs): + user = request.user + if ( + user.has_perm("project.view_project") + # or has_subordinates(request) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ): + return True + else: + return False + + +def task_accessibility(request, submenu, user_perms, *args, **kwargs): + user = request.user + if ( + user.has_perm("project.view_task") + # or has_subordinates(request) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ): + return True + else: + return False + + +def timesheet_accessibility(request, submenu, user_perms, *args, **kwargs): + user = request.user + if ( + user.has_perm("project.view_timesheet") + # or has_subordinates(request) + or any_project_manager(user) + or any_project_member(user) + or any_task_manager(user) + or any_task_member(user) + ): + return True + else: + return False diff --git a/project/static/dashboard/projectChart.js b/project/static/dashboard/projectChart.js index fc9e5752a..77537f183 100644 --- a/project/static/dashboard/projectChart.js +++ b/project/static/dashboard/projectChart.js @@ -1,89 +1,89 @@ -$(document).ready(function(){ - function projectStatusChart(dataSet, labels) { - const data = { - labels: labels, - datasets: dataSet, - }; - // Create chart using the Chart.js library - window['projectChart'] = {} - const ctx = document.getElementById("projectStatusCanvas").getContext("2d"); - - projectChart = new Chart(ctx, { - type: 'bar', - data: data, - options: { - }, - }); - } - $.ajax({ - url: "/project/project-status-chart", - type: "GET", - success: function (response) { - // Code to handle the response - // response = {'dataSet': [{'label': 'Odoo developer 2023-03-30', 'data': [3, 0, 5, 3]}, {'label': 'React developer 2023-03-31', 'data': [0, 1, 1, 0]}, {'label': 'Content Writer 2023-04-01', 'data': [1, 0, 0, 0]}], 'labels': ['Initial', 'Test', 'Interview', 'Hired']} - dataSet = response.dataSet; - labels = response.labels; - projectStatusChart(dataSet, labels); - - }, - }); - $('#projectStatusForward').click(function (e) { - var chartType = projectChart.config.type - if (chartType === 'line') { - chartType = 'bar'; - } else if(chartType==='bar') { - chartType = 'doughnut'; - } else if(chartType==='doughnut'){ - chartType = 'pie' - }else if(chartType==='pie'){ - chartType = 'line' - } - projectChart.config.type = chartType; - projectChart.update(); - }); - - // for creating task status chart - function taskStatusChart(dataSet, labels) { - const data = { - labels: labels, - datasets: dataSet, - }; - // Create chart using the Chart.js library - window['taskChart'] = {} - const ctx = document.getElementById("taskStatusCanvas").getContext("2d"); - - taskChart = new Chart(ctx, { - type: 'bar', - data: data, - options: { - }, - }); - } - $.ajax({ - url: "/project/task-status-chart", - type: "GET", - success: function (response) { - // Code to handle the response - // response = {'dataSet': [{'label': 'Odoo developer 2023-03-30', 'data': [3, 0, 5, 3]}, {'label': 'React developer 2023-03-31', 'data': [0, 1, 1, 0]}, {'label': 'Content Writer 2023-04-01', 'data': [1, 0, 0, 0]}], 'labels': ['Initial', 'Test', 'Interview', 'Hired']} - dataSet = response.dataSet; - labels = response.labels; - taskStatusChart(dataSet, labels); - - }, - }); - $('#taskStatusForward').click(function (e) { - var chartType = taskChart.config.type - if (chartType === 'line') { - chartType = 'bar'; - } else if(chartType==='bar') { - chartType = 'doughnut'; - } else if(chartType==='doughnut'){ - chartType = 'pie' - }else if(chartType==='pie'){ - chartType = 'line' - } - taskChart.config.type = chartType; - taskChart.update(); - }); - -}); \ No newline at end of file +$(document).ready(function(){ + function projectStatusChart(dataSet, labels) { + const data = { + labels: labels, + datasets: dataSet, + }; + // Create chart using the Chart.js library + window['projectChart'] = {} + const ctx = document.getElementById("projectStatusCanvas").getContext("2d"); + + projectChart = new Chart(ctx, { + type: 'bar', + data: data, + options: { + }, + }); + } + $.ajax({ + url: "/project/project-status-chart", + type: "GET", + success: function (response) { + // Code to handle the response + // response = {'dataSet': [{'label': 'Odoo developer 2023-03-30', 'data': [3, 0, 5, 3]}, {'label': 'React developer 2023-03-31', 'data': [0, 1, 1, 0]}, {'label': 'Content Writer 2023-04-01', 'data': [1, 0, 0, 0]}], 'labels': ['Initial', 'Test', 'Interview', 'Hired']} + dataSet = response.dataSet; + labels = response.labels; + projectStatusChart(dataSet, labels); + + }, + }); + $('#projectStatusForward').click(function (e) { + var chartType = projectChart.config.type + if (chartType === 'line') { + chartType = 'bar'; + } else if(chartType==='bar') { + chartType = 'doughnut'; + } else if(chartType==='doughnut'){ + chartType = 'pie' + }else if(chartType==='pie'){ + chartType = 'line' + } + projectChart.config.type = chartType; + projectChart.update(); + }); + + // for creating task status chart + function taskStatusChart(dataSet, labels) { + const data = { + labels: labels, + datasets: dataSet, + }; + // Create chart using the Chart.js library + window['taskChart'] = {} + const ctx = document.getElementById("taskStatusCanvas").getContext("2d"); + + taskChart = new Chart(ctx, { + type: 'bar', + data: data, + options: { + }, + }); + } + $.ajax({ + url: "/project/task-status-chart", + type: "GET", + success: function (response) { + // Code to handle the response + // response = {'dataSet': [{'label': 'Odoo developer 2023-03-30', 'data': [3, 0, 5, 3]}, {'label': 'React developer 2023-03-31', 'data': [0, 1, 1, 0]}, {'label': 'Content Writer 2023-04-01', 'data': [1, 0, 0, 0]}], 'labels': ['Initial', 'Test', 'Interview', 'Hired']} + dataSet = response.dataSet; + labels = response.labels; + taskStatusChart(dataSet, labels); + + }, + }); + $('#taskStatusForward').click(function (e) { + var chartType = taskChart.config.type + if (chartType === 'line') { + chartType = 'bar'; + } else if(chartType==='bar') { + chartType = 'doughnut'; + } else if(chartType==='doughnut'){ + chartType = 'pie' + }else if(chartType==='pie'){ + chartType = 'line' + } + taskChart.config.type = chartType; + taskChart.update(); + }); + +}); diff --git a/project/static/project/import.js b/project/static/project/import.js index c55a305ea..9534d7a7e 100644 --- a/project/static/project/import.js +++ b/project/static/project/import.js @@ -1,252 +1,309 @@ -var downloadMessages = { - ar: "هل ترغب في تنزيل القالب؟", - de: "Möchten Sie die Vorlage herunterladen?", - es: "¿Quieres descargar la plantilla?", - en: "Do you want to download the template?", - fr: "Voulez-vous télécharger le modèle ?", - }; - - var importsuccess = { - ar: "نجح الاستيراد", // Arabic - de: "Import erfolgreich", // German - es: "Importado con éxito", // Spanish - en: "Imported Successfully!", // English - fr: "Importation réussie" // French - }; - - var uploadsuccess = { - ar: "تحميل كامل", // Arabic - de: "Upload abgeschlossen", // German - es: "Carga completa", // Spanish - en: "Upload Complete!", // English - fr: "Téléchargement terminé" // French - }; - - var uploadingmessage = { - ar: "جارٍ الرفع", - de: "Hochladen...", - es: "Subiendo...", - en: "Uploading...", - fr: "Téléchargement en cours...", - }; - - var validationmessage = { - ar: "يرجى تحميل ملف بامتداد .xlsx فقط.", - de: "Bitte laden Sie nur eine Datei mit der Erweiterung .xlsx hoch.", - es: "Por favor, suba un archivo con la extensión .xlsx solamente.", - en: "Please upload a file with the .xlsx extension only.", - fr: "Veuillez télécharger uniquement un fichier avec l'extension .xlsx.", - }; - - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== '') { - const cookies = document.cookie.split(';'); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === (name + '=')) { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - function getCurrentLanguageCode(callback) { - $.ajax({ - type: "GET", - url: "/employee/get-language-code/", - success: function (response) { - var languageCode = response.language_code; - callback(languageCode); // Pass the language code to the callback - }, - }); - } - - - // Get the form element - var form = document.getElementById("projectImportForm"); - - // Add an event listener to the form submission - form.addEventListener("submit", function (event) { - // Prevent the default form submission - event.preventDefault(); - - // Create a new form data object - var formData = new FormData(); - - // Append the file to the form data object - var fileInput = document.querySelector("#projectImportFile"); - formData.append("file", fileInput.files[0]); - $.ajax({ - type: "POST", - url: "/project/project-import", - dataType: "binary", - data: formData, - processData: false, - contentType: false, - headers: { - "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value - }, - xhrFields: { - responseType: "blob", - }, - success: function (response) { - const file = new Blob([response], { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); - const url = URL.createObjectURL(file); - const link = document.createElement("a"); - link.href = url; - link.download = "ImportError.xlsx"; - document.body.appendChild(link); - link.click(); - }, - error: function (xhr, textStatus, errorThrown) { - console.error("Error downloading file:", errorThrown); - }, - }); - }); - - - $("#importProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = downloadMessages[languageCode]; - // Use SweetAlert for the confirmation dialog - Swal.fire({ - - text: confirmMessage, - icon: 'question', - showCancelButton: true, - confirmButtonColor: '#008000', - cancelButtonColor: '#d33', - confirmButtonText: 'Confirm' - }).then(function(result) { - if (result.isConfirmed) { - $("#loading").show(); - var xhr = new XMLHttpRequest(); - xhr.open('GET', "/project/project-import", true); - xhr.responseType = 'arraybuffer'; - - xhr.upload.onprogress = function (e) { - if (e.lengthComputable) { - var percent = (e.loaded / e.total) * 100; - $(".progress-bar").width(percent + "%").attr("aria-valuenow", percent); - $("#progress-text").text("Uploading... " + percent.toFixed(2) + "%"); - } - }; - - xhr.onload = function (e) { - if (this.status == 200) { - const file = new Blob([this.response], { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); - const url = URL.createObjectURL(file); - const link = document.createElement("a"); - link.href = url; - link.download = "project_template.xlsx"; - document.body.appendChild(link); - link.click(); - } - }; - - xhr.onerror = function (e) { - console.error("Error downloading file:", e); - }; - - xhr.send(); - } - }); - }); - }); - - $(document).ajaxStart(function () { - $("#loading").show(); - }); - - $(document).ajaxStop(function () { - $("#loading").hide(); - }); - - function simulateProgress() { - var languageCode = null; - getCurrentLanguageCode(function(code){ - languageCode = code; - var importMessage = importsuccess[languageCode]; - var uploadMessage = uploadsuccess[languageCode]; - var uploadingMessage = uploadingmessage[languageCode]; - let progressBar = document.querySelector('.progress-bar'); - let progressText = document.getElementById('progress-text'); - - let width = 0; - let interval = setInterval(function() { - if (width >= 100) { - clearInterval(interval); - progressText.innerText = uploadMessage; - setTimeout(function() { - document.getElementById('loading').style.display = 'none'; - }, 3000); - Swal.fire({ - text: importMessage, - icon: "success", - showConfirmButton: false, - timer: 2000, - timerProgressBar: true, - }); - setTimeout(function() { - $('#projectImport').removeClass('oh-modal--show'); - location.reload(true); - }, 2000); - } else { - width++; - progressBar.style.width = width + '%'; - progressBar.setAttribute('aria-valuenow', width); - progressText.innerText = uploadingMessage + width + '%'; - } - }, 20); - } - )} - - document.getElementById('projectImportForm').addEventListener('submit', function(event) { - event.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function(code){ - languageCode = code; - var erroMessage = validationmessage[languageCode]; - - var fileInput = $('#projectImportFile').val(); - var allowedExtensions = /(\.xlsx)$/i; - - if (!allowedExtensions.exec(fileInput)) { - - var errorMessage = document.createElement('div'); - errorMessage.classList.add('error-message'); - - errorMessage.textContent = erroMessage; - - document.getElementById('error-container').appendChild(errorMessage); - - fileInput.value = ''; - - setTimeout(function() { - errorMessage.remove(); - }, 2000); - - return false; - } - else{ - - document.getElementById('loading').style.display = 'block'; - - - simulateProgress(); - } - - }); - }) - \ No newline at end of file + +var downloadMessages = { + ar: "هل ترغب في تنزيل القالب؟", + de: "Möchten Sie die Vorlage herunterladen?", + es: "¿Quieres descargar la plantilla?", + en: "Do you want to download the template?", + fr: "Voulez-vous télécharger le modèle ?", + }; + + var importsuccess = { + ar: "نجح الاستيراد", // Arabic + de: "Import erfolgreich", // German + es: "Importado con éxito", // Spanish + en: "Imported Successfully!", // English + fr: "Importation réussie" // French + }; + + var uploadsuccess = { + ar: "تحميل كامل", // Arabic + de: "Upload abgeschlossen", // German + es: "Carga completa", // Spanish + en: "Upload Complete!", // English + fr: "Téléchargement terminé" // French + }; + + var uploadingmessage = { + ar: "جارٍ الرفع", + de: "Hochladen...", + es: "Subiendo...", + en: "Uploading...", + fr: "Téléchargement en cours...", + }; + + var validationmessage = { + ar: "يرجى تحميل ملف بامتداد .xlsx فقط.", + de: "Bitte laden Sie nur eine Datei mit der Erweiterung .xlsx hoch.", + es: "Por favor, suba un archivo con la extensión .xlsx solamente.", + en: "Please upload a file with the .xlsx extension only.", + fr: "Veuillez télécharger uniquement un fichier avec l'extension .xlsx.", + }; + + function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== '') { + const cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + function getCurrentLanguageCode(callback) { + $.ajax({ + type: "GET", + url: "/employee/get-language-code/", + success: function (response) { + var languageCode = response.language_code; + callback(languageCode); // Pass the language code to the callback + }, + }); + } + + + // Get the form element + var form = document.getElementById("projectImportForm"); + + // Add an event listener to the form submission + form.addEventListener("submit", function (event) { + // Prevent the default form submission + event.preventDefault(); + + // Create a new form data object + var formData = new FormData(); + + // Append the file to the form data object + var fileInput = document.querySelector("#projectImportFile"); + formData.append("file", fileInput.files[0]); + $.ajax({ + type: "POST", + url: "/project/project-import", + dataType: "binary", + data: formData, + processData: false, + contentType: false, + headers: { + "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value + }, + xhrFields: { + responseType: "blob", + }, + success: function (response) { + const file = new Blob([response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "ImportError.xlsx"; + document.body.appendChild(link); + link.click(); + }, + error: function (xhr, textStatus, errorThrown) { + console.error("Error downloading file:", errorThrown); + }, + }); + }); + + + $("#importProject").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = downloadMessages[languageCode]; + // Use SweetAlert for the confirmation dialog + Swal.fire({ + + text: confirmMessage, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#008000', + cancelButtonColor: '#d33', + confirmButtonText: 'Confirm' + }).then(function(result) { + if (result.isConfirmed) { + $("#loading").show(); + var xhr = new XMLHttpRequest(); + xhr.open('GET', "/project/project-import", true); + xhr.responseType = 'arraybuffer'; + + xhr.upload.onprogress = function (e) { + if (e.lengthComputable) { + var percent = (e.loaded / e.total) * 100; + $(".progress-bar").width(percent + "%").attr("aria-valuenow", percent); + $("#progress-text").text("Uploading... " + percent.toFixed(2) + "%"); + } + }; + + xhr.onload = function (e) { + if (this.status == 200) { + const file = new Blob([this.response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project_template.xlsx"; + document.body.appendChild(link); + link.click(); + } + }; + + xhr.onerror = function (e) { + console.error("Error downloading file:", e); + }; + + xhr.send(); + } + }); + }); + }); + + $(document).on('click', '#importProject', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = downloadMessages[languageCode]; + // Use SweetAlert for the confirmation dialog + Swal.fire({ + text: confirmMessage, + icon: 'question', + showCancelButton: true, + confirmButtonColor: '#008000', + cancelButtonColor: '#d33', + confirmButtonText: 'Confirm' + }).then(function(result) { + if (result.isConfirmed) { + $("#loading").show(); + var xhr = new XMLHttpRequest(); + xhr.open('GET', "/project/project-import", true); + xhr.responseType = 'arraybuffer'; + + xhr.upload.onprogress = function (e) { + if (e.lengthComputable) { + var percent = (e.loaded / e.total) * 100; + $(".progress-bar").width(percent + "%").attr("aria-valuenow", percent); + $("#progress-text").text("Uploading... " + percent.toFixed(2) + "%"); + } + }; + + xhr.onload = function (e) { + if (this.status == 200) { + const file = new Blob([this.response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project_template.xlsx"; + document.body.appendChild(link); + link.click(); + // Clean up by removing the link element + document.body.removeChild(link); + URL.revokeObjectURL(url); + } + }; + + xhr.onerror = function (e) { + console.error("Error downloading file:", e); + }; + + xhr.send(); + } + }); + }); +}); + + + $(document).ajaxStart(function () { + $("#loading").show(); + }); + + $(document).ajaxStop(function () { + $("#loading").hide(); + }); + + function simulateProgress() { + var languageCode = null; + getCurrentLanguageCode(function(code){ + languageCode = code; + var importMessage = importsuccess[languageCode]; + var uploadMessage = uploadsuccess[languageCode]; + var uploadingMessage = uploadingmessage[languageCode]; + let progressBar = document.querySelector('.progress-bar'); + let progressText = document.getElementById('progress-text'); + + let width = 0; + let interval = setInterval(function() { + if (width >= 100) { + clearInterval(interval); + progressText.innerText = uploadMessage; + setTimeout(function() { + document.getElementById('loading').style.display = 'none'; + }, 3000); + Swal.fire({ + text: importMessage, + icon: "success", + showConfirmButton: false, + timer: 2000, + timerProgressBar: true, + }); + setTimeout(function() { + $('#projectImport').removeClass('oh-modal--show'); + location.reload(true); + }, 2000); + } else { + width++; + progressBar.style.width = width + '%'; + progressBar.setAttribute('aria-valuenow', width); + progressText.innerText = uploadingMessage + width + '%'; + } + }, 20); + } + )} + + document.getElementById('projectImportForm').addEventListener('submit', function(event) { + event.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function(code){ + languageCode = code; + var erroMessage = validationmessage[languageCode]; + + var fileInput = $('#projectImportFile').val(); + var allowedExtensions = /(\.xlsx)$/i; + + if (!allowedExtensions.exec(fileInput)) { + + var errorMessage = document.createElement('div'); + errorMessage.classList.add('error-message'); + + errorMessage.textContent = erroMessage; + + document.getElementById('error-container').appendChild(errorMessage); + + fileInput.value = ''; + + setTimeout(function() { + errorMessage.remove(); + }, 2000); + + return false; + } + else{ + + document.getElementById('loading').style.display = 'block'; + + + simulateProgress(); + } + + }); + }) diff --git a/project/static/project/project_action.js b/project/static/project/project_action.js index 2f27f7a0e..1f5c66a91 100644 --- a/project/static/project/project_action.js +++ b/project/static/project/project_action.js @@ -1,408 +1,631 @@ -var archiveMessages = { - // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", - // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", - en: "Do you really want to archive all the selected projects?", - // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", - }; - - var unarchiveMessages = { - // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", - // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", - en: "Do you really want to unarchive all the selected projects?", - // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", - }; - - var deleteMessages = { - // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", - // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", - en: "Do you really want to delete all the selected projects?", - // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; - - var exportMessages = { - // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", - // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", - en: "Do you really want to export all the selected projects?", - // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; - - var downloadMessages = { - ar: "هل ترغب في تنزيل القالب؟", - de: "Möchten Sie die Vorlage herunterladen?", - es: "¿Quieres descargar la plantilla?", - en: "Do you want to download the template?", - fr: "Voulez-vous télécharger le modèle ?", - }; - - var norowMessages = { - // ar: "لم يتم تحديد أي صفوف.", - // de: "Es wurden keine Zeilen ausgewählt.", - // es: "No se han seleccionado filas.", - en: "No rows have been selected.", - // fr: "Aucune ligne n'a été sélectionnée.", - }; - - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === name + "=") { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - function getCurrentLanguageCode(callback) { - $.ajax({ - type: "GET", - url: "/employee/get-language-code/", - success: function (response) { - var languageCode = response.language_code; - callback(languageCode); // Pass the language code to the callback - }, - }); - } - - - // // Get the form element - // var form = document.getElementById("projectImportForm"); - - // // Add an event listener to the form submission - // form.addEventListener("submit", function (event) { - // // Prevent the default form submission - // event.preventDefault(); - - // // Create a new form data object - // var formData = new FormData(); - - // // Append the file to the form data object - // var fileInput = document.querySelector("#projectImportFile"); - // formData.append("file", fileInput.files[0]); - // $.ajax({ - // type: "POST", - // url: "/project/project-import", - // dataType: "binary", - // data: formData, - // processData: false, - // contentType: false, - // headers: { - // "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value - // }, - // xhrFields: { - // responseType: "blob", - // }, - // success: function (response) { - // const file = new Blob([response], { - // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - // }); - // const url = URL.createObjectURL(file); - // const link = document.createElement("a"); - // link.href = url; - // link.download = "ImportError.xlsx"; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function (xhr, textStatus, errorThrown) { - // console.error("Error downloading file:", errorThrown); - // }, - // }); - // }); - - // $("#importProject").click(function (e) { - // e.preventDefault(); - // var languageCode = null; - // getCurrentLanguageCode(function (code) { - // languageCode = code; - // var confirmMessage = downloadMessages[languageCode]; - // // Use SweetAlert for the confirmation dialog - // Swal.fire({ - // text: confirmMessage, - // icon: 'question', - // showCancelButton: true, - // confirmButtonColor: '#008000', - // cancelButtonColor: '#d33', - // confirmButtonText: 'Confirm' - // }).then(function(result) { - // if (result.isConfirmed) { - // $.ajax({ - // type: "GET", - // url: "/project/project-import", - // dataType: "binary", - // xhrFields: { - // responseType: "blob", - // }, - // success: function (response) { - // const file = new Blob([response], { - // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - // }); - // const url = URL.createObjectURL(file); - // const link = document.createElement("a"); - // link.href = url; - // link.download = "project_template.xlsx"; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function (xhr, textStatus, errorThrown) { - // console.error("Error downloading file:", errorThrown); - // }, - // }); - // } - // }); - // }); - // }); - - // $('#importProject').click(function (e) { - // $.ajax({ - // type: 'POST', - // url: '/project/project-import', - // dataType: 'binary', - // xhrFields: { - // responseType: 'blob' - // }, - // success: function(response) { - // const file = new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); - // const url = URL.createObjectURL(file); - // const link = document.createElement('a'); - // link.href = url; - // link.download = 'project.xlsx'; - // document.body.appendChild(link); - // link.click(); - // }, - // error: function(xhr, textStatus, errorThrown) { - // console.error('Error downloading file:', errorThrown); - // } - // }); - // }); - - - $(".all-projects").change(function (e) { - var is_checked = $(this).is(":checked"); - if (is_checked) { - $(".all-project-row").prop("checked", true); - } else { - $(".all-project-row").prop("checked", false); - } +var archiveMessagesSelected = { + // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", + // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", + en: "Do you really want to archive all the selected projects?", + // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", + }; + + var unarchiveMessagesSelected = { + // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", + // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", + en: "Do you really want to unarchive all the selected projects?", + // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", + }; + + var deleteMessage = { + // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", + // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", + en: "Do you really want to delete all the selected projects?", + // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", + }; + + var exportMessages = { + // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", + // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", + en: "Do you really want to export all the selected projects?", + // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", + }; + + var downloadMessages = { + ar: "هل ترغب في تنزيل القالب؟", + de: "Möchten Sie die Vorlage herunterladen?", + es: "¿Quieres descargar la plantilla?", + en: "Do you want to download the template?", + fr: "Voulez-vous télécharger le modèle ?", + }; + + var norowMessagesSelected = { + // ar: "لم يتم تحديد أي صفوف.", + // de: "Es wurden keine Zeilen ausgewählt.", + // es: "No se han seleccionado filas.", + en: "No rows have been selected.", + // fr: "Aucune ligne n'a été sélectionnée.", + }; + + function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== "") { + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + function getCurrentLanguageCode(callback) { + $.ajax({ + type: "GET", + url: "/employee/get-language-code/", + success: function (response) { + var languageCode = response.language_code; + callback(languageCode); // Pass the language code to the callback + }, + }); + } + + + // // Get the form element + // var form = document.getElementById("projectImportForm"); + + // // Add an event listener to the form submission + // form.addEventListener("submit", function (event) { + // // Prevent the default form submission + // event.preventDefault(); + + // // Create a new form data object + // var formData = new FormData(); + + // // Append the file to the form data object + // var fileInput = document.querySelector("#projectImportFile"); + // formData.append("file", fileInput.files[0]); + // $.ajax({ + // type: "POST", + // url: "/project/project-import", + // dataType: "binary", + // data: formData, + // processData: false, + // contentType: false, + // headers: { + // "X-CSRFToken": getCookie('csrftoken'), // Replace with your csrf token value + // }, + // xhrFields: { + // responseType: "blob", + // }, + // success: function (response) { + // const file = new Blob([response], { + // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + // }); + // const url = URL.createObjectURL(file); + // const link = document.createElement("a"); + // link.href = url; + // link.download = "ImportError.xlsx"; + // document.body.appendChild(link); + // link.click(); + // }, + // error: function (xhr, textStatus, errorThrown) { + // console.error("Error downloading file:", errorThrown); + // }, + // }); + // }); + + // $("#importProject").click(function (e) { + // e.preventDefault(); + // var languageCode = null; + // getCurrentLanguageCode(function (code) { + // languageCode = code; + // var confirmMessage = downloadMessages[languageCode]; + // // Use SweetAlert for the confirmation dialog + // Swal.fire({ + // text: confirmMessage, + // icon: 'question', + // showCancelButton: true, + // confirmButtonColor: '#008000', + // cancelButtonColor: '#d33', + // confirmButtonText: 'Confirm' + // }).then(function(result) { + // if (result.isConfirmed) { + // $.ajax({ + // type: "GET", + // url: "/project/project-import", + // dataType: "binary", + // xhrFields: { + // responseType: "blob", + // }, + // success: function (response) { + // const file = new Blob([response], { + // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + // }); + // const url = URL.createObjectURL(file); + // const link = document.createElement("a"); + // link.href = url; + // link.download = "project_template.xlsx"; + // document.body.appendChild(link); + // link.click(); + // }, + // error: function (xhr, textStatus, errorThrown) { + // console.error("Error downloading file:", errorThrown); + // }, + // }); + // } + // }); + // }); + // }); + + // $('#importProject').click(function (e) { + // $.ajax({ + // type: 'POST', + // url: '/project/project-import', + // dataType: 'binary', + // xhrFields: { + // responseType: 'blob' + // }, + // success: function(response) { + // const file = new Blob([response], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); + // const url = URL.createObjectURL(file); + // const link = document.createElement('a'); + // link.href = url; + // link.download = 'project.xlsx'; + // document.body.appendChild(link); + // link.click(); + // }, + // error: function(xhr, textStatus, errorThrown) { + // console.error('Error downloading file:', errorThrown); + // } + // }); + // }); + + + $(".all-projects").change(function (e) { + var is_checked = $(this).is(":checked"); + if (is_checked) { + $(".all-project-row").prop("checked", true); + } else { + $(".all-project-row").prop("checked", false); + } + }); + + $("#exportProject").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = exportMessages[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + var checkedRows = $(".all-project-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-project-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-export", + dataType: "binary", + xhrFields: { + responseType: "blob", + }, + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + const file = new Blob([response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project details.xlsx"; + document.body.appendChild(link); + link.click(); + }, + }); + } + }); + } + }); + }); + + $(document).on('click', '#exportProject', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = exportMessages[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // var checkedRows = $(".all-project-row").filter(":checked"); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-export", + dataType: "binary", + xhrFields: { + responseType: "blob", + }, + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + const file = new Blob([response], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(file); + const link = document.createElement("a"); + link.href = url; + link.download = "project details.xlsx"; + document.body.appendChild(link); + link.click(); + }, + }); + } + }); + } + }); + }); + + + $("#archiveProject").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = archiveMessagesSelected[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + var checkedRows = $(".all-project-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + e.preventDefault(); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-archive?is_active=False", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + + + $(document).on('click', '#archiveProject', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = archiveMessagesSelected[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // e.preventDefault(); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-archive?is_active=False", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + + +$("#unArchiveProject").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = unarchiveMessagesSelected[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + var checkedRows = $(".all-project-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-project-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-archive?is_active=True", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + + $(document).on('click', '#unArchiveProject', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = unarchiveMessagesSelected[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // var checkedRows = $(".all-project-row").filter(":checked"); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-archive?is_active=True", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + +$("#deleteProject").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessage[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + var checkedRows = $(".all-project-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "error", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-project-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + + + $(document).on('click', '#deleteProject', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessage[languageCode]; + var textMessage = norowMessagesSelected[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "error", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // var checkedRows = $(".all-project-row").filter(":checked"); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); + + $.ajax({ + type: "POST", + url: "/project/project-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); }); - - $("#exportProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = exportMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-export", - dataType: "binary", - xhrFields: { - responseType: "blob", - }, - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - const file = new Blob([response], { - type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - }); - const url = URL.createObjectURL(file); - const link = document.createElement("a"); - link.href = url; - link.download = "project details.xlsx"; - document.body.appendChild(link); - link.click(); - }, - }); - } - }); - } - }); - }); - - $("#archiveProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = archiveMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - e.preventDefault(); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=False", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - - -$("#unArchiveProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = unarchiveMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-archive?is_active=True", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - -$("#deleteProject").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = deleteMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-project-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "error", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-project-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/project-bulk-delete", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); \ No newline at end of file diff --git a/project/static/project/project_view.js b/project/static/project/project_view.js index 5195a526a..4db7430a8 100644 --- a/project/static/project/project_view.js +++ b/project/static/project/project_view.js @@ -1,25 +1,25 @@ -$(document).ready(function(){ - $("#filter-project").keyup(function (e) { - $(".project-view-type").attr("hx-vals", `{"search":"${$(this).val()}"}`); - }); - $(".project-view-type").click(function (e) { - let view = $(this).data("view"); - var currentURL = window.location.href; - if (view != undefined){ - // Check if the query string already exists in the URL - if (/\?view=[^&]+/.test(currentURL)) { - // If the query parameter ?view exists, replace it with the new value - newURL = currentURL.replace(/\?view=[^&]+/, "?view="+view); - } - else { - // If the query parameter ?view does not exist, add it to the URL - var separator = currentURL.includes('?') ? '&' : '?'; - newURL = currentURL + separator + "view="+view; - } - - history.pushState({}, "", newURL); - $("#filter-project").attr("hx-vals", `{"view":"${view}"}`); - $('#timesheetForm').attr("hx-vals", `{"view":"${view}"}`); - } - }); -}); \ No newline at end of file +$(document).ready(function(){ + $("#filter-project").keyup(function (e) { + $(".project-view-type").attr("hx-vals", `{"search":"${$(this).val()}"}`); + }); + $(".project-view-type").click(function (e) { + let view = $(this).data("view"); + var currentURL = window.location.href; + if (view != undefined){ + // Check if the query string already exists in the URL + if (/\?view=[^&]+/.test(currentURL)) { + // If the query parameter ?view exists, replace it with the new value + newURL = currentURL.replace(/\?view=[^&]+/, "?view="+view); + } + else { + // If the query parameter ?view does not exist, add it to the URL + var separator = currentURL.includes('?') ? '&' : '?'; + newURL = currentURL + separator + "view="+view; + } + + history.pushState({}, "", newURL); + $("#filter-project").attr("hx-vals", `{"view":"${view}"}`); + $('#timesheetForm').attr("hx-vals", `{"view":"${view}"}`); + } + }); +}); diff --git a/project/static/project/task_pipeline.js b/project/static/project/task_pipeline.js index 12093c1fb..e6371d458 100644 --- a/project/static/project/task_pipeline.js +++ b/project/static/project/task_pipeline.js @@ -1,138 +1,177 @@ -$(document).ready(function(){ - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === name + "=") { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - // for the search function - $("#filter-task").keyup(function (e) { - var search = $(this).val().toLowerCase(); - var view = $(this).data('view') - if (view == 'list') { - $('.task_row').each(function () { - var task = $(this).data('task') - if (task.includes(search)) { - $(this).show(); - } else { - $(this).hide(); - } - }) - - } else { - $('.task').each(function () { - var task = $(this).data('task') - if (task.includes(search)) { - $(this).show(); - } else { - $(this).hide(); - } - }) - } - }); - - - $('.task').mousedown(function(){ - window ['previous_task_id'] = $(this).attr('data-task-id') - window ['previous_stage_id'] = $(this).parent().attr('data-stage-id') - }); - - $(".tasks-container").on("DOMNodeInserted", function (e) { - var updated_task_id = $(e.target).attr('data-task-id'); - var updated_stage_id = $(this).attr("data-stage-id"); - if (updated_task_id != null) { - var new_seq = {} - var task_container = $(this).children(".task") - task_container.each(function(i, obj) { - new_seq[$(obj).data('task-id')] = i - }); - $.ajax({ - type: "post", - url: '/project/drag-and-drop-task', - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - updated_task_id: updated_task_id, - updated_stage_id : updated_stage_id, - previous_task_id : previous_task_id, - previous_stage_id : previous_stage_id, - sequence:JSON.stringify(new_seq), - }, - success: function(response){ - if (response.change) { // Check if the 'change' attribute in the response is True - $("#ohMessages").append(` -
-
- ${response.message} -
-
`); - } - }, - }); - }; - }); - - - - $('.stage').mouseup(function(){ - window['previous_stage_id'] = $(this).attr('data-stage-id') - window['previous_sequence'] = $(this).attr('data-sequence') - setTimeout(function() { - var new_seq = {} - $('.stage').each(function(i, obj) { - new_seq[$(obj).attr('data-stage-id')] = i - }); - $.ajax({ - type: 'post', - url: '/project/drag-and-drop-stage', - data:{ - csrfmiddlewaretoken: getCookie("csrftoken"), - sequence:JSON.stringify(new_seq), - }, - success: function(response) { - if (response.change) { // Check if the 'change' attribute in the response is True - $("#ohMessages").append(` -
-
- ${response.message} -
-
`); - } - }, - }) - }, 100); - }) - - $("#filter-task").keyup(function (e) { - $(".task-view-type").attr("hx-vals", `{"search":"${$(this).val()}"}`); - }); - $(".task-view-type").click(function (e) { - let view = $(this).data("view"); - var currentURL = window.location.href; - if (view != undefined){ - // Check if the query string already exists in the URL - if (/\?view=[^&]+/.test(currentURL)) { - // If the query parameter ?view exists, replace it with the new value - newURL = currentURL.replace(/\?view=[^&]+/, "?view="+view); - } - else { - // If the query parameter ?view does not exist, add it to the URL - var separator = currentURL.includes('?') ? '&' : '?'; - newURL = currentURL + separator + "view=card"; - } - - history.pushState({}, "", newURL); - $("#filter-task").attr("hx-vals", `{"view":"${view}"}`); - $('#timesheetForm').attr("hx-vals", `{"view":"${view}"}`); - } - }); -}); \ No newline at end of file +function toggleDropdown(id) { + var element = document.getElementById('taskCreateForm' + id); + element.classList.toggle('d-none'); +} + +function updateStageClass(element) { + const parent = $(element).parent(); + const isCollapsed = parent.hasClass('oh-kanban__section'); + const stageId = parent.data('stage-id'); // Get the data-stage-id attribute value + const collapsedProjectStages = JSON.parse(localStorage.getItem('collapsedProjectStages')) || []; // Get the collapsed stages from localStorage or initialize an empty array + + + // Toggle between class states + parent.toggleClass('oh-kanban__section oh-kanban-group stage'); + parent.toggleClass('ml-2 stage ui-sortable-handle'); + + if (isCollapsed) { + setTimeout(() => { + parent.addClass('oh-kanban-card--collapsed stage'); + }, 100); + if (!collapsedProjectStages.includes(stageId)) { + collapsedProjectStages.push(stageId); + localStorage.setItem('collapsedProjectStages', JSON.stringify(collapsedProjectStages)); + } + } else { + parent.removeClass('oh-kanban-card--collapsed'); + const index = collapsedProjectStages.indexOf(stageId); + if (index > -1) { + collapsedProjectStages.splice(index, 1); // Remove the stageId from the array + localStorage.setItem('collapsedProjectStages', JSON.stringify(collapsedProjectStages)); // Update localStorage + } + } + + // Toggle task container visibility + parent.find('.task-container').toggleClass('d-none'); + parent.find('.oh-kanban__head-actions').first().toggleClass('d-none'); + +} + +function loadCollapsedProjectStages() { + // Retrieve collapsed project stages from local storage + let collapsedProjectStages = []; + const collapsedProjectStagesData = localStorage.getItem('collapsedProjectStages'); + if (collapsedProjectStagesData) { + try { + // Parse the JSON only if it's a valid string + collapsedProjectStages = JSON.parse(collapsedProjectStagesData); + } catch (error) { + console.error('Error parsing JSON from local storage:', error); + } + } + + // Iterate over collapsed project stages + $.each(collapsedProjectStages, function (index, stageId) { + const stageElement = $(`[data-stage-id='${stageId}']`); + if (stageElement.length) { + const groupHead = stageElement.find('.oh-kanban-group__head'); + if (groupHead.length) { + updateStageClass(groupHead[0]); + } + } + }); +} + +$(document).ready(function () { + let old_stage_seq = {}; // Define the old sequence of stages globally + + loadCollapsedProjectStages(); + + function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== "") { + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + $('.task-container').sortable({ + // Enables sortable functionality for task containers, + // tracking drag-and-drop events and updating task sequences via AJAX. + connectWith: ".task-container", + start: function (event, ui) { + var $draggedCard = ui.item; + var $taskOldStage = $draggedCard.closest('.oh-kanban__section'); + taskOldStageId = $taskOldStage.data('stage-id'); // Store the task old stage ID + window.$oldStageTaskCount = $taskOldStage.find('.task-count') + }, + stop: function (event, ui) { + var $draggedCard = ui.item; + var $taskNewStage = $draggedCard.closest('.oh-kanban__section'); + var $newStageTaskCount = $taskNewStage.find('.task-count'); + var taskNewStageId = $taskNewStage.data('stage-id'); // Get the task new stage ID + var taskId = $draggedCard.data('task-id'); + + // Check if the task has moved to a new stage + if (taskNewStageId !== taskOldStageId) { + var new_stage_seq = {}; + var task_container = $(this).children(".task"); + task_container.each(function (i, obj) { + new_stage_seq[$(obj).data('task-id')] = i; + }); + + // Update the task counts in old stage by -1 + var oldCount = parseInt(window.$oldStageTaskCount.html()); + window.$oldStageTaskCount.html(oldCount - 1); + + // Increment the new stage task count by 1 + var newCount = parseInt($newStageTaskCount.html()); + $newStageTaskCount.html(newCount + 1); + + // Trigger AJAX if the task has changed stages + $.ajax({ + type: "post", + url: '/project/drag-and-drop-task', + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + updated_task_id: taskId, + updated_stage_id: taskNewStageId, + previous_task_id: taskId, + previous_stage_id: taskOldStageId, + sequence: JSON.stringify(new_stage_seq), + }, + success: function (response) { + if (response.change === true) { + $("#reloadMessagesButton").click(); + } + }, + }); + } + } + }); + + $('.stage').mousedown(function (e) { + // Capture old sequence of stages on mousedown + old_stage_seq = {}; + $('.stage').each(function (i, obj) { + old_stage_seq[$(obj).attr('data-stage-id')] = i; + }); + }); + + $('.stage').mouseup(function (e) { + //For stage position rearrange event + setTimeout(function () { + var new_stage_seq = {}; + $('.stage').each(function (i, obj) { + new_stage_seq[$(obj).attr('data-stage-id')] = i; + }); + + // Compare old_stage_seq with new_stage_seq to trigger the ajax request + if (JSON.stringify(old_stage_seq) !== JSON.stringify(new_stage_seq)) { + $.ajax({ + type: 'post', + url: '/project/drag-and-drop-stage', + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + sequence: JSON.stringify(new_stage_seq), + }, + success: function (response) { + if (response.change) { + if (response.change === true) { + $("#reloadMessagesButton").click(); + } + } + }, + }); + } + }, 100); + }); +}); diff --git a/project/static/task_all/task_all_action.js b/project/static/task_all/task_all_action.js index 9f8d18335..6bd7c7e0d 100644 --- a/project/static/task_all/task_all_action.js +++ b/project/static/task_all/task_all_action.js @@ -1,220 +1,371 @@ -var archiveMessages = { - // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", - // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", - en: "Do you really want to archive all the selected tasks?", - // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", - }; - - var unarchiveMessages = { - // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", - // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", - en: "Do you really want to unarchive all the selected tasks?", - // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", - }; - - var deleteMessages = { - // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", - // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", - en: "Do you really want to delete all the selected tasks?", - // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; - - var norowMessages = { - // ar: "لم يتم تحديد أي صفوف.", - // de: "Es wurden keine Zeilen ausgewählt.", - // es: "No se han seleccionado filas.", - en: "No rows have been selected.", - // fr: "Aucune ligne n'a été sélectionnée.", - }; - - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === name + "=") { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - function getCurrentLanguageCode(callback) { - $.ajax({ - type: "GET", - url: "/employee/get-language-code/", - success: function (response) { - var languageCode = response.language_code; - callback(languageCode); // Pass the language code to the callback - }, - }); - } - $(".all-task-all").change(function (e) { - var is_checked = $(this).is(":checked"); - if (is_checked) { - $(".all-task-all-row").prop("checked", true); - } else { - $(".all-task-all-row").prop("checked", false); - } - }); - - $("#archiveTaskAll").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = archiveMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-task-all-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - e.preventDefault(); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/task-all-bulk-archive?is_active=False", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - - -$("#unArchiveTaskAll").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = unarchiveMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-task-all-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "info", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-task-all-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/task-all-bulk-archive?is_active=True", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); - -$("#deleteTaskAll").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = deleteMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-task-all-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "error", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-task-all-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/task-all-bulk-delete", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); \ No newline at end of file +var archiveMessage = { + // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", + // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", + en: "Do you really want to archive all the selected tasks?", + // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", + }; + + var unarchiveMessage = { + // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", + // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", + en: "Do you really want to unarchive all the selected tasks?", + // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", + }; + + var deleteMessage = { + // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", + // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", + en: "Do you really want to delete all the selected tasks?", + // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", + }; + + var norowMessage = { + // ar: "لم يتم تحديد أي صفوف.", + // de: "Es wurden keine Zeilen ausgewählt.", + // es: "No se han seleccionado filas.", + en: "No rows have been selected.", + // fr: "Aucune ligne n'a été sélectionnée.", + }; + + function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== "") { + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + function getCurrentLanguageCode(callback) { + $.ajax({ + type: "GET", + url: "/employee/get-language-code/", + success: function (response) { + var languageCode = response.language_code; + callback(languageCode); // Pass the language code to the callback + }, + }); + } + $(".all-task-all").change(function (e) { + var is_checked = $(this).is(":checked"); + if (is_checked) { + $(".all-task-all-row").prop("checked", true); + } else { + $(".all-task-all-row").prop("checked", false); + } + }); + + $("#archiveTaskAll").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = archiveMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + var checkedRows = $(".all-task-all-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + e.preventDefault(); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-archive?is_active=False", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + +//Bulk archive + +$(document).on('click', '#archiveTask', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = archiveMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-archive?is_active=False", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); + } else { + + } + }, + }); + } + }); + } + }); +}); + + + +$("#unArchiveTaskAll").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = unarchiveMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + var checkedRows = $(".all-task-all-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-task-all-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-archive?is_active=True", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + +//Bulk unarchive + +$(document).on('click', '#unArchiveTask', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = unarchiveMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-archive?is_active=True", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); + } else { + + } + }, + }); + } + }); + } + }); +}); + +$("#deleteTaskAll").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + var checkedRows = $(".all-task-all-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "error", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-task-all-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + +//Bulk delete + +$(document).on('click', '#deleteTask', function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessage[languageCode]; + var textMessage = norowMessage[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "info", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + + $.ajax({ + type: "POST", + url: "/project/task-all-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); + } else { + + } + }, + }); + } + }); + } + }); +}); diff --git a/project/static/time_sheet/time_sheet_action.js b/project/static/time_sheet/time_sheet_action.js index a8801f77a..b8b0ddd87 100644 --- a/project/static/time_sheet/time_sheet_action.js +++ b/project/static/time_sheet/time_sheet_action.js @@ -1,118 +1,172 @@ -var archiveMessages = { - // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", - // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", - en: "Do you really want to archive all the selected timesheet?", - // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", - }; - - var unarchiveMessages = { - // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", - // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", - en: "Do you really want to unarchive all the selected timesheet?", - // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", - }; - - var deleteMessages = { - // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", - // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", - // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", - en: "Do you really want to delete all the selected timesheet?", - // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", - }; - - var norowMessages = { - // ar: "لم يتم تحديد أي صفوف.", - // de: "Es wurden keine Zeilen ausgewählt.", - // es: "No se han seleccionado filas.", - en: "No rows have been selected.", - // fr: "Aucune ligne n'a été sélectionnée.", - }; - - function getCookie(name) { - let cookieValue = null; - if (document.cookie && document.cookie !== "") { - const cookies = document.cookie.split(";"); - for (let i = 0; i < cookies.length; i++) { - const cookie = cookies[i].trim(); - // Does this cookie string begin with the name we want? - if (cookie.substring(0, name.length + 1) === name + "=") { - cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); - break; - } - } - } - return cookieValue; - } - - function getCurrentLanguageCode(callback) { - $.ajax({ - type: "GET", - url: "/employee/get-language-code/", - success: function (response) { - var languageCode = response.language_code; - callback(languageCode); // Pass the language code to the callback - }, - }); - } - $(".all-time-sheet").change(function (e) { - var is_checked = $(this).is(":checked"); - if (is_checked) { - $(".all-time-sheet-row").prop("checked", true); - } else { - $(".all-time-sheet-row").prop("checked", false); - } - }); - - -$("#deleteTimeSheet").click(function (e) { - e.preventDefault(); - var languageCode = null; - getCurrentLanguageCode(function (code) { - languageCode = code; - var confirmMessage = deleteMessages[languageCode]; - var textMessage = norowMessages[languageCode]; - var checkedRows = $(".all-time-sheet-row").filter(":checked"); - if (checkedRows.length === 0) { - Swal.fire({ - text: textMessage, - icon: "warning", - confirmButtonText: "Close", - }); - } else { - Swal.fire({ - text: confirmMessage, - icon: "error", - showCancelButton: true, - confirmButtonColor: "#008000", - cancelButtonColor: "#d33", - confirmButtonText: "Confirm", - }).then(function (result) { - if (result.isConfirmed) { - var checkedRows = $(".all-time-sheet-row").filter(":checked"); - ids = []; - checkedRows.each(function () { - ids.push($(this).attr("id")); - }); - - $.ajax({ - type: "POST", - url: "/project/time-sheet-bulk-delete", - data: { - csrfmiddlewaretoken: getCookie("csrftoken"), - ids: JSON.stringify(ids), - }, - success: function (response, textStatus, jqXHR) { - if (jqXHR.status === 200) { - location.reload(); // Reload the current page - } else { - // console.log("Unexpected HTTP status:", jqXHR.status); - } - }, - }); - } - }); - } - }); - }); \ No newline at end of file + +var archiveMessages = { + // ar: "هل ترغب حقًا في أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter archivieren?", + // es: "¿Realmente quieres archivar a todos los empleados seleccionados?", + en: "Do you really want to archive all the selected timesheet?", + // fr: "Voulez-vous vraiment archiver tous les employés sélectionnés ?", + }; + + var unarchiveMessages = { + // ar: "هل ترغب حقًا في إلغاء أرشفة جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter aus der Archivierung zurückholen?", + // es: "¿Realmente quieres desarchivar a todos los empleados seleccionados?", + en: "Do you really want to unarchive all the selected timesheet?", + // fr: "Voulez-vous vraiment désarchiver tous les employés sélectionnés?", + }; + + var deleteMessagesBulk = { + // ar: "هل ترغب حقًا في حذف جميع الموظفين المحددين؟", + // de: "Möchten Sie wirklich alle ausgewählten Mitarbeiter löschen?", + // es: "¿Realmente quieres eliminar a todos los empleados seleccionados?", + en: "Do you really want to delete all the selected timesheet?", + // fr: "Voulez-vous vraiment supprimer tous les employés sélectionnés?", + }; + + var norowMessages = { + // ar: "لم يتم تحديد أي صفوف.", + // de: "Es wurden keine Zeilen ausgewählt.", + // es: "No se han seleccionado filas.", + en: "No rows have been selected.", + // fr: "Aucune ligne n'a été sélectionnée.", + }; + + function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== "") { + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } + + function getCurrentLanguageCode(callback) { + $.ajax({ + type: "GET", + url: "/employee/get-language-code/", + success: function (response) { + var languageCode = response.language_code; + callback(languageCode); // Pass the language code to the callback + }, + }); + } + $(".all-time-sheet").change(function (e) { + var is_checked = $(this).is(":checked"); + if (is_checked) { + $(".all-time-sheet-row").prop("checked", true); + } else { + $(".all-time-sheet-row").prop("checked", false); + } + }); + + +$("#deleteTimeSheet").click(function (e) { + e.preventDefault(); + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessagesBulk[languageCode]; + var textMessage = norowMessages[languageCode]; + var checkedRows = $(".all-time-sheet-row").filter(":checked"); + if (checkedRows.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "error", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + var checkedRows = $(".all-time-sheet-row").filter(":checked"); + ids = []; + checkedRows.each(function () { + ids.push($(this).attr("id")); + }); + + $.ajax({ + type: "POST", + url: "/project/time-sheet-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }); + + + function deleteTimeSheet(){ + var languageCode = null; + getCurrentLanguageCode(function (code) { + languageCode = code; + var confirmMessage = deleteMessagesBulk[languageCode]; + var textMessage = norowMessages[languageCode]; + ids = []; + ids.push($("#selectedInstances").attr("data-ids")); + ids = JSON.parse($("#selectedInstances").attr("data-ids")); + if (ids.length === 0) { + Swal.fire({ + text: textMessage, + icon: "warning", + confirmButtonText: "Close", + }); + } else { + Swal.fire({ + text: confirmMessage, + icon: "error", + showCancelButton: true, + confirmButtonColor: "#008000", + cancelButtonColor: "#d33", + confirmButtonText: "Confirm", + }).then(function (result) { + if (result.isConfirmed) { + // var checkedRows = $(".all-time-sheet-row").filter(":checked"); + // ids = []; + // checkedRows.each(function () { + // ids.push($(this).attr("id")); + // }); + + $.ajax({ + type: "POST", + url: "/project/time-sheet-bulk-delete", + data: { + csrfmiddlewaretoken: getCookie("csrftoken"), + ids: JSON.stringify(ids), + }, + success: function (response, textStatus, jqXHR) { + if (jqXHR.status === 200) { + location.reload(); // Reload the current page + } else { + // console.log("Unexpected HTTP status:", jqXHR.status); + } + }, + }); + } + }); + } + }); + }; diff --git a/project/templates/cbv/projects/actions.html b/project/templates/cbv/projects/actions.html new file mode 100644 index 000000000..af7a2d14c --- /dev/null +++ b/project/templates/cbv/projects/actions.html @@ -0,0 +1,70 @@ +{% load i18n %} +{% load taskfilters %} +
+ {% if perms.project.change_project or request.user|is_project_manager:instance %} + + + + {% else %} + + {% endif %} + {% if perms.project.delete_project or request.user|is_project_manager:instance %} + {% if instance.is_active %} + + + + {% else %} + + + + {% endif %} + {% endif %} + {% if perms.project.delete_project %} + + + + {% endif %} +
\ No newline at end of file diff --git a/project/templates/cbv/projects/filter.html b/project/templates/cbv/projects/filter.html new file mode 100644 index 000000000..c4e9d6fd6 --- /dev/null +++ b/project/templates/cbv/projects/filter.html @@ -0,0 +1,40 @@ +{% load i18n%} +
+
+
{% trans "Project" %}
+
+
+
+
+ + {{form.managers}} +
+
+ + {{form.status}} +
+
+ + {{form.start_from}} +
+
+
+
+ + {{form.members}} +
+
+ + {{form.is_active}} +
+
+ + {{form.end_till}} +
+
+
+
+
+
\ No newline at end of file diff --git a/project/templates/cbv/projects/project_details.html b/project/templates/cbv/projects/project_details.html new file mode 100644 index 000000000..f0848ad30 --- /dev/null +++ b/project/templates/cbv/projects/project_details.html @@ -0,0 +1,125 @@ +{% load mathfilters %} +{% load static i18n %} +{% load taskfilters %} +
+
+
+
    +
  • + {% trans "Project:" %} + {{project}} +
  • +
+ + + {% if perms.pms.change_project or request.user|is_project_manager_or_member:project %} + + {% else %} + + {% endif %} + {% if perms.project.change_project or perms.project.add_projectstage or request.user|is_project_manager_or_member:project %} +
+ {% if perms.project.add_project %} + + {% endif %} +
+ {% endif %} +
+
+ + +
+
\ No newline at end of file diff --git a/project/templates/cbv/projects/project_list.html b/project/templates/cbv/projects/project_list.html new file mode 100644 index 000000000..44b4d4cad --- /dev/null +++ b/project/templates/cbv/projects/project_list.html @@ -0,0 +1,38 @@ +{% load basefilters i18n %} +{% for project in projects %} +
+
+
+ +
+
+ {{ project.title }} +
+
+
+ + + +
+
+
+
+
+
+
+
+{% endfor %} + diff --git a/project/templates/cbv/projects/project_nav.html b/project/templates/cbv/projects/project_nav.html new file mode 100644 index 000000000..7e21f958e --- /dev/null +++ b/project/templates/cbv/projects/project_nav.html @@ -0,0 +1,9 @@ +{% load i18n %} +
+ {% include "generic/horilla_nav.html" %} +
+ + \ No newline at end of file diff --git a/project/templates/cbv/projects/project_tab.html b/project/templates/cbv/projects/project_tab.html new file mode 100644 index 000000000..a2744b93a --- /dev/null +++ b/project/templates/cbv/projects/project_tab.html @@ -0,0 +1,19 @@ +{% load i18n %} {% load static %} +
+{% if projects %} +
+

{% trans "Projects" %}

+
+
+{% include "cbv/projects/project_list.html" %} +
+{% else %} +
+
+ Page not found. 404. +

{% trans "No projects assigned to this employee." %}

+
+
+{% endif %} \ No newline at end of file diff --git a/project/templates/cbv/projects/projects.html b/project/templates/cbv/projects/projects.html new file mode 100644 index 000000000..a620ecd50 --- /dev/null +++ b/project/templates/cbv/projects/projects.html @@ -0,0 +1,149 @@ +{% extends "index.html" %} +{% load i18n %}{% load static %} + + +{% block content %} + + + + + +
+
+ + +{% include "generic/components.html" %} + + + + + +
+
+
+
+ + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/project/templates/cbv/tasks/task_actions.html b/project/templates/cbv/tasks/task_actions.html new file mode 100644 index 000000000..358fe2e71 --- /dev/null +++ b/project/templates/cbv/tasks/task_actions.html @@ -0,0 +1,51 @@ +{% load i18n %} +{% load taskfilters %} +
+ {% if request.user|task_crud_perm:instance or perms.project.view_task %} +
+ + + + {% if instance.is_active %} + + + + {% else %} + + + + {% endif %} +
+ {% csrf_token %} + +
+ + +
+ {% else %} +
+ + + + + + + + + +
+ {% endif %} +
\ No newline at end of file diff --git a/project/templates/cbv/tasks/task_detail_actions.html b/project/templates/cbv/tasks/task_detail_actions.html new file mode 100644 index 000000000..29fa8ea0a --- /dev/null +++ b/project/templates/cbv/tasks/task_detail_actions.html @@ -0,0 +1,27 @@ +{% load i18n %} +{% if request.user|task_crud_perm:instance or perms.project.view_task %} + + + + {% trans "Edit" %} + +{% endif %} + + + + {% trans "Time sheet" %} + +{% if request.user|task_crud_perm:instance or perms.project.view_task %} + + + {% trans "Delete" %} + +{% endif %} \ No newline at end of file diff --git a/project/templates/cbv/tasks/task_document.html b/project/templates/cbv/tasks/task_document.html new file mode 100644 index 000000000..77199de2b --- /dev/null +++ b/project/templates/cbv/tasks/task_document.html @@ -0,0 +1,10 @@ +{% load i18n %} +{% if instance.document %} + {% trans "Document" %} +
+ + +
+{% endif%} \ No newline at end of file diff --git a/project/templates/cbv/tasks/task_filter.html b/project/templates/cbv/tasks/task_filter.html new file mode 100644 index 000000000..1caee1152 --- /dev/null +++ b/project/templates/cbv/tasks/task_filter.html @@ -0,0 +1,41 @@ +{% load i18n %} {% load basefilters %} +
+
+
+ {% trans "Task" %} +
+
+
+
+
+ + {{form.task_manager}} +
+
+ + {{form.stage}} +
+
+
+
+ + {{form.project}} +
+
+ + {{form.status}} +
+
+
+
+ + {{form.end_till}} +
+
+ + +
+
+
+
\ No newline at end of file diff --git a/project/templates/cbv/tasks/task_form.html b/project/templates/cbv/tasks/task_form.html new file mode 100644 index 000000000..52d45716d --- /dev/null +++ b/project/templates/cbv/tasks/task_form.html @@ -0,0 +1,9 @@ +
+ + {% include 'generic/horilla_form.html' %} +
\ No newline at end of file diff --git a/project/templates/cbv/tasks/task_template_view.html b/project/templates/cbv/tasks/task_template_view.html new file mode 100644 index 000000000..58741a064 --- /dev/null +++ b/project/templates/cbv/tasks/task_template_view.html @@ -0,0 +1,125 @@ +{% extends "index.html" %} +{% load i18n %}{% load static %} + + +{% block content %} + + + + + +
+
+ + +{% include "generic/components.html" %} + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/project/templates/cbv/timesheet/actions.html b/project/templates/cbv/timesheet/actions.html new file mode 100644 index 000000000..654114c7e --- /dev/null +++ b/project/templates/cbv/timesheet/actions.html @@ -0,0 +1,58 @@ +{% load i18n %} {% load taskfilters %} +{% if request.user|time_sheet_crud_perm:instance or perms.project.change_timesheet or perms.project.delete_timesheet %} +
+ + + + {% if request.task_id %} + + + + {% else %} + + + + {% endif %} +
+{% else %} +
+ + + + + + + +
+{% endif %} diff --git a/project/templates/cbv/timesheet/detail_actions.html b/project/templates/cbv/timesheet/detail_actions.html new file mode 100644 index 000000000..06184cb7e --- /dev/null +++ b/project/templates/cbv/timesheet/detail_actions.html @@ -0,0 +1,23 @@ +{% load i18n %} +{% load basefilters %} +
+ + + {% trans "Edit" %} + + {% if perms.project.view_timesheet or request.user|is_reportingmanager %} + + + {% trans "View Timesheet Chart" %} + + {% endif %} + + + {% trans "Delete" %} + +
\ No newline at end of file diff --git a/project/templates/cbv/timesheet/employee_field.html b/project/templates/cbv/timesheet/employee_field.html new file mode 100644 index 000000000..2237c4fe0 --- /dev/null +++ b/project/templates/cbv/timesheet/employee_field.html @@ -0,0 +1,29 @@ +{% load i18n %} + +{{form.employee_id}} + \ No newline at end of file diff --git a/project/templates/cbv/timesheet/filter.html b/project/templates/cbv/timesheet/filter.html new file mode 100644 index 000000000..99bf91c24 --- /dev/null +++ b/project/templates/cbv/timesheet/filter.html @@ -0,0 +1,59 @@ +{% load i18n %}{% load basefilters%} +
+
+
{% trans "Time Sheet" %}
+
+
+
+
+ + {{form.project_id}} +
+
+ + {{form.status}} +
+
+ +
+
+ + {{form.task}} +
+
+ + {{form.date}} +
+
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %} +
+
+ + {{form.employee_id}} +
+
+ {% endif %} +
+
+
+ +
+
{% trans "Advanced" %}
+
+
+
+
+ + {{form.start_from}} +
+
+
+
+ + {{form.end_till}} +
+
+
+
+
+
\ No newline at end of file diff --git a/project/templates/cbv/timesheet/form.html b/project/templates/cbv/timesheet/form.html new file mode 100644 index 000000000..e4b885488 --- /dev/null +++ b/project/templates/cbv/timesheet/form.html @@ -0,0 +1,3 @@ +
+ {% include "generic/horilla_form.html" %} +
diff --git a/project/templates/cbv/timesheet/task_field.html b/project/templates/cbv/timesheet/task_field.html new file mode 100644 index 000000000..624a26372 --- /dev/null +++ b/project/templates/cbv/timesheet/task_field.html @@ -0,0 +1,15 @@ +{% load i18n %} + +
+ {{form.task_id}} +
+ \ No newline at end of file diff --git a/project/templates/cbv/timesheet/task_timesheet.html b/project/templates/cbv/timesheet/task_timesheet.html new file mode 100644 index 000000000..13b6c9cd9 --- /dev/null +++ b/project/templates/cbv/timesheet/task_timesheet.html @@ -0,0 +1,37 @@ +{% load i18n %} +
+ {% include "generic/components.html" %} +
+ +
{% trans "Time sheet" %}
+
+ {% comment %} for add timesheet {% endcomment %} + + + {% include "generic/horilla_list_table.html" %} +
+
\ No newline at end of file diff --git a/project/templates/cbv/timesheet/timesheet.html b/project/templates/cbv/timesheet/timesheet.html new file mode 100644 index 000000000..e78af7256 --- /dev/null +++ b/project/templates/cbv/timesheet/timesheet.html @@ -0,0 +1,51 @@ +{% extends "index.html" %} +{% load i18n %}{% load static %} + + +{% block content %} + + + + + +
+
+ + +{% include "generic/components.html" %} + + + + + +
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/project/templates/cbv/timesheet/timesheet_nav.html b/project/templates/cbv/timesheet/timesheet_nav.html new file mode 100644 index 000000000..8b0073d34 --- /dev/null +++ b/project/templates/cbv/timesheet/timesheet_nav.html @@ -0,0 +1,8 @@ +{% load i18n %} +
+ {% include "generic/horilla_nav.html" %} +
+ \ No newline at end of file diff --git a/project/templates/dashboard/project_dashboard.html b/project/templates/dashboard/project_dashboard.html index 9a9329f04..216be36b5 100644 --- a/project/templates/dashboard/project_dashboard.html +++ b/project/templates/dashboard/project_dashboard.html @@ -1,152 +1,143 @@ -{% extends 'index.html' %} -{% block content %} -{% load static i18n %} -{% load i18n %} - -
-
-
-
-
- -
-
-
- {% trans "Total Projects" %} -
- -
-
- -
-
-
- {% trans "New Projects" %} -
- -
-
- -
-
-
- {% trans "Projects in progress" %} -
- -
-
-
- -
- -
-
-
- {% trans "Project Status" %} - -
-
- -
-
-
- -
-
-
- {% trans "Task Status" %} - - -
-
- -
-
-
- -
-
- - -
- -
-
    -
  • -
  • -
  • -
  • -
-
-
-
- {% trans "Projects due in this month" %} - {% trans "View all" %} -
-
-
    - {% if unexpired_project %} - {% for project in unexpired_project %} -
  • - -
    -
    - Beth Gibbons -
    - {{project.title}} -
    -
    -
  • - {% endfor %} - {% else %} -
    -
    - Page not found. 404. -

    {% trans "No projects due in this month." %}

    -
    -
    - {% endif %} - -
-
-
-
-
-
- -
- - - - -{% endblock content %} \ No newline at end of file +{% extends 'index.html' %} +{% block content %} +{% load static i18n %} +{% load i18n %} + +
+
+
+
+
+ +
+
+
+ {% trans "Total Projects" %} +
+ +
+
+ +
+
+
+ {% trans "New Projects" %} +
+ +
+
+ +
+
+
+ {% trans "Projects in progress" %} +
+ +
+
+
+ +
+ +
+
+
+ {% trans "Project Status" %} + +
+
+ +
+
+
+ +
+
+
+ {% trans "Task Status" %} + + +
+
+ +
+
+
+ +
+
+ + +
+ +
+
    +
  • +
  • +
  • +
  • +
+
+
+
+ {% trans "Projects due in this month" %} +
+
+
+
+
+
+
+
+
+ +
+ + + + +{% endblock content %} diff --git a/project/templates/dashboard/project_details.html b/project/templates/dashboard/project_details.html index 8ed3bbc7f..b7f2ca66a 100644 --- a/project/templates/dashboard/project_details.html +++ b/project/templates/dashboard/project_details.html @@ -1,71 +1,71 @@ -{% load i18n %} {% load yes_no %} - -
- -
{{project.title}}
-
- -
-
- {% trans "Manager" %} - {{project.manager}} -
-
- {% trans "Members" %} - {% for member in project.members.all %}{{member}}, {% endfor %} -
-
-
-
- {% trans "Status" %} - {{project.get_status_display}} -
-
- {% trans "No of Tasks" %} - {{task_count}} -
-
-
-
- {% trans "Start Date" %} - {{project.start_date}} -
-
- {% trans "End date" %} - {{project.end_date}} -
-
-
-
- {% trans "Document" %} - {{project.document}} -
-
- {% trans "Description" %} - {{project.description}} -
-
-
- {% comment %}
{% endcomment %} - - - {% trans "View" %} - - {% comment %}
{% endcomment %} -
- -
\ No newline at end of file +{% load i18n %} {% load horillafilters %} + +
+ +
{{project.title}}
+
+ +
+
+ {% trans "Manager" %} + {{project.manager}} +
+
+ {% trans "Members" %} + {% for member in project.members.all %}{{member}}, {% endfor %} +
+
+
+
+ {% trans "Status" %} + {{project.get_status_display}} +
+
+ {% trans "No of Tasks" %} + {{task_count}} +
+
+
+
+ {% trans "Start Date" %} + {{project.start_date}} +
+
+ {% trans "End date" %} + {{project.end_date}} +
+
+
+
+ {% trans "Document" %} + {{project.document}} +
+
+ {% trans "Description" %} + {{project.description}} +
+
+
+ {% comment %}
{% endcomment %} + + + {% trans "View" %} + + {% comment %}
{% endcomment %} +
+ +
diff --git a/project/templates/project/new/filter_project.html b/project/templates/project/new/filter_project.html index 38c8f3b38..85e5ca2b1 100644 --- a/project/templates/project/new/filter_project.html +++ b/project/templates/project/new/filter_project.html @@ -1,44 +1,44 @@ -{% load i18n %} {% load basefilters %} -{% comment %}
{% endcomment %} -
-
-
{% trans "Project" %}
-
-
-
-
- - {{f.form.manager}} -
-
- - {{f.form.status}} -
-
-
-
- - {{f.form.start_from}} -
-
- - {{f.form.end_till}} -
-
- -
-
-
-
- -{% comment %}
{% endcomment %} \ No newline at end of file +{% load i18n %} {% load basefilters %} +{% comment %}
{% endcomment %} +
+
+
{% trans "Project" %}
+
+
+
+
+ + {{f.form.manager}} +
+
+ + {{f.form.status}} +
+
+
+
+ + {{f.form.start_from}} +
+
+ + {{f.form.end_till}} +
+
+ +
+
+
+
+ +{% comment %}
{% endcomment %} diff --git a/project/templates/project/new/forms/project_creation.html b/project/templates/project/new/forms/project_creation.html index 93195a241..a940c4e8e 100644 --- a/project/templates/project/new/forms/project_creation.html +++ b/project/templates/project/new/forms/project_creation.html @@ -1,30 +1,30 @@ -{% load i18n %} -
- -
{% trans "Project" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/project/new/forms/project_update.html b/project/templates/project/new/forms/project_update.html index 5aaf991a3..9b28c6dc2 100644 --- a/project/templates/project/new/forms/project_update.html +++ b/project/templates/project/new/forms/project_update.html @@ -1,30 +1,30 @@ -{% load i18n %} -
- -
{% trans "Project" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/project/new/navbar.html b/project/templates/project/new/navbar.html index a3e811335..ab4e14553 100644 --- a/project/templates/project/new/navbar.html +++ b/project/templates/project/new/navbar.html @@ -1,235 +1,234 @@ -{% load i18n %} - - - - - -
-
-

{% trans "Projects" %}

- - - -
-
- {% if projects %} - {% comment %} for search{% endcomment %} -
-
- - -
-
-
    - {% comment %} for list view {% endcomment %} -
  • - -
  • - {% comment %} for card view {% endcomment %} -
  • - -
  • -
-
- {% comment %} for filtering {% endcomment %} -
- - -
-
- {% comment %} for actions {% endcomment %} -
-
- - -
-
- {% endif %} - {% comment %} for create project {% endcomment %} - -
-
- +{% load i18n %} + + + + + +
+
+

{% trans "Projects" %}

+ + + +
+
+ {% if projects %} + {% comment %} for search{% endcomment %} +
+
+ + +
+
+
    + {% comment %} for list view {% endcomment %} +
  • + +
  • + {% comment %} for card view {% endcomment %} +
  • + +
  • +
+
+ {% comment %} for filtering {% endcomment %} +
+ + +
+
+ {% comment %} for actions {% endcomment %} +
+
+ + +
+
+ {% endif %} + {% comment %} for create project {% endcomment %} + +
+
diff --git a/project/templates/project/new/overall.html b/project/templates/project/new/overall.html index a50abbc4f..ea1fc2599 100644 --- a/project/templates/project/new/overall.html +++ b/project/templates/project/new/overall.html @@ -1,34 +1,33 @@ -{% extends 'index.html' %} -{% load static %} -{% block content %} - -
- {% include 'project/new/navbar.html' %} -
- -
- - {% if view_type == 'list' %} - {% include 'project/new/project_list_view.html' %} - - {% else %} - {% include 'project/new/project_kanban_view.html' %} - {% endif %} -
- - - -{% endblock content %} - +{% extends 'index.html' %} +{% load static %} +{% block content %} + +
+ {% include 'project/new/navbar.html' %} +
+ +
+ + {% if view_type == 'list' %} + {% include 'project/new/project_list_view.html' %} + + {% else %} + {% include 'project/new/project_kanban_view.html' %} + {% endif %} +
+ + + +{% endblock content %} diff --git a/project/templates/project/new/project_kanban_view.html b/project/templates/project/new/project_kanban_view.html index 0a0118bbf..5864eb278 100644 --- a/project/templates/project/new/project_kanban_view.html +++ b/project/templates/project/new/project_kanban_view.html @@ -1,201 +1,201 @@ - -{% load static %} - -{% load i18n %} -{% load basefilters %} - - -{% comment %} for showing messages {% endcomment %} -{% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
-{% endif %} -{% include "filter_tags.html" %} -{% comment %} easy filters {% endcomment %} -
- - - {% trans "New" %} - - - - {% trans "In progress" %} - - - - {% trans "Completed" %} - - - {% trans "On Hold" %} - - - - {% trans "Cancelled" %} - - - - {% trans "Expired" %} - -
- - - {% comment %} kanban view {% endcomment %} - {% if projects %} -
- {% for project in projects %} -
- - {% comment %} url link {% endcomment %} - - - {% comment %} placing image {% endcomment %} -
-
- {% if project.image %} - Username - {% else %} - Username - {% endif %} -
-
- -
- {{project.title}} - {% trans "Project manager" %} : {{project.manager}}
- {% trans "Status" %}: {{project.get_status_display}}
- {% trans "End date" %} : {{project.end_date}} -
-
-
- - {{ project.task_set.all|length}} - -
-
- - {% comment %} dropdown {% endcomment %} -
- - {% comment %} dropdown menu {% endcomment %} - - -
-
-
- {% endfor %} -
- {% comment %} pagination {% endcomment %} -
- - {% trans "Page" %} {{ projects.number }} {% trans "of" %} {{ projects.paginator.num_pages }}. - - -
-{% comment %} {% endcomment %} - -{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available projects; please create a new one." %}

-
-
-{% endif %} - - -{% comment %} js scripts {% endcomment %} - - \ No newline at end of file + +{% load static %} + +{% load i18n %}{% load horillafilters %} +{% load basefilters %} + + +{% comment %} for showing messages {% endcomment %} +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} +{% comment %} easy filters {% endcomment %} +
+ + + {% trans "New" %} + + + + {% trans "In progress" %} + + + + {% trans "Completed" %} + + + {% trans "On Hold" %} + + + + {% trans "Cancelled" %} + + + + {% trans "Expired" %} + +
+ + + {% comment %} kanban view {% endcomment %} + {% if projects %} +
+ {% for project in projects %} +
+ + {% comment %} url link {% endcomment %} + + + {% comment %} placing image {% endcomment %} +
+
+ {% if project.image %} + Username + {% else %} + Username + {% endif %} +
+
+ +
+ {{project.title}} + {% trans "Project manager" %} : {{project.manager}}
+ {% trans "Status" %}: {{project.get_status_display}}
+ {% trans "End date" %} : {{project.end_date}} +
+
+
+ + {{ project.task_set.all|length}} + +
+
+ + {% comment %} dropdown {% endcomment %} +
+ + {% comment %} dropdown menu {% endcomment %} + + +
+
+
+ {% endfor %} +
+ {% comment %} pagination {% endcomment %} +
+ + {% trans "Page" %} {{ projects.number }} {% trans "of" %} {{ projects.paginator.num_pages }}. + + +
+{% comment %} {% endcomment %} + +{% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available projects; please create a new one." %}

+
+
+{% endif %} + + +{% comment %} js scripts {% endcomment %} + + diff --git a/project/templates/project/new/project_list_view.html b/project/templates/project/new/project_list_view.html index 620872873..15be71637 100644 --- a/project/templates/project/new/project_list_view.html +++ b/project/templates/project/new/project_list_view.html @@ -1,236 +1,236 @@ - -{% load static %} -{% load i18n %} - - -{% comment %} for showing messages {% endcomment %} -{% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
-{% endif %} -{% include "filter_tags.html" %} - -
- {% if projects %} -
- {% comment %} easy filters {% endcomment %} -
- - - {% trans "New" %} - - - - {% trans "In progress" %} - - - - {% trans "Completed" %} - - - {% trans "On Hold" %} - - - - {% trans "Cancelled" %} - - - - {% trans "Expired" %} - -
- -
-
-
-
-
-
-
- -
-
- {% trans "Project" %} -
-
-
-
{% trans "Project Manager" %}
-
{% trans "Project Members" %}
-
{% trans "Status" %}
-
{% trans "Start Date" %}
-
{% trans "End Date" %}
-
{% trans "File" %}
-
{% trans "Description" %}
-
-
-
- - {% for project in projects %} -
-
-
- - {{project.title}} -
-
- -
{{project.manager}}
-
- {% for employee in project.members.all %} {{employee}}
- {% endfor %} -
-
{{project.get_status_display}}
-
{{project.start_date}}
-
{% if project.end_date %}{{project.end_date}}{% endif %}
-
{% if project.document %}document{% endif %}
-
{{project.description}}
- -
- -
-
- {% endfor %} - {% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available projects; please create a new one." %}

-
-
- - {% endif %} - -
-
-
- {% comment %}
- - {% trans "Page" %} {{ allowances.number }} {% trans "of" %} {{ - allowances.paginator.num_pages }}. - - -
{% endcomment %} -
- - -{% comment %} js scripts {% endcomment %} - - - - - \ No newline at end of file + +{% load static %} +{% load i18n %} {% load horillafilters %} + + +{% comment %} for showing messages {% endcomment %} +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} + +
+ {% if projects %} +
+ {% comment %} easy filters {% endcomment %} +
+ + + {% trans "New" %} + + + + {% trans "In progress" %} + + + + {% trans "Completed" %} + + + {% trans "On Hold" %} + + + + {% trans "Cancelled" %} + + + + {% trans "Expired" %} + +
+ +
+
+
+
+
+
+
+ +
+
+ {% trans "Project" %} +
+
+
+
{% trans "Project Manager" %}
+
{% trans "Project Members" %}
+
{% trans "Status" %}
+
{% trans "Start Date" %}
+
{% trans "End Date" %}
+
{% trans "File" %}
+
{% trans "Description" %}
+
+
+
+ + {% for project in projects %} +
+
+
+ + {{project.title}} +
+
+ +
{{project.manager}}
+
+ {% for employee in project.members.all %} {{employee}}
+ {% endfor %} +
+
{{project.get_status_display}}
+
{{project.start_date}}
+
{% if project.end_date %}{{project.end_date}}{% endif %}
+
{% if project.document %}document{% endif %}
+
{{project.description}}
+ +
+ +
+
+ {% endfor %} + {% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available projects; please create a new one." %}

+
+
+ + {% endif %} + +
+
+
+ {% comment %}
+ + {% trans "Page" %} {{ allowances.number }} {% trans "of" %} {{ + allowances.paginator.num_pages }}. + + +
{% endcomment %} +
+ + +{% comment %} js scripts {% endcomment %} + + + + + diff --git a/project/templates/project_stage/forms/create_project_stage.html b/project/templates/project_stage/forms/create_project_stage.html index 57e837957..06b3f1aab 100644 --- a/project/templates/project_stage/forms/create_project_stage.html +++ b/project/templates/project_stage/forms/create_project_stage.html @@ -1,30 +1,30 @@ -{% load i18n %} -
- -
{% trans "Project Stage" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project Stage" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/project_stage/forms/update_project_stage.html b/project/templates/project_stage/forms/update_project_stage.html index 99c5d4310..fa0c38070 100644 --- a/project/templates/project_stage/forms/update_project_stage.html +++ b/project/templates/project_stage/forms/update_project_stage.html @@ -1,30 +1,30 @@ -{% load i18n %} -
- -
{% trans "Project Stage" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }}{{form.errorList}} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project Stage" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }}{{form.errorList}} + +
+
diff --git a/project/templates/task/new/filter_task.html b/project/templates/task/new/filter_task.html index f4f6abfae..3c3bdaaa9 100644 --- a/project/templates/task/new/filter_task.html +++ b/project/templates/task/new/filter_task.html @@ -1,43 +1,44 @@ -{% load i18n %} {% load basefilters %} -
-
-
-
{% trans "Task" %}
-
-
- -
-
- - {{f.form.task_manager}} -
-
- - {{f.form.stage}} -
-
-
-
- - {{f.form.status}} -
-
- - {{f.form.end_till}} -
-
- -
-
-
-
- -
\ No newline at end of file +{% load i18n %} {% load basefilters %} +
+
+
{% trans "Task" %}
+
+
+ +
+
+ + {{f.form.task_managers}} +
+
+ + {{f.form.stage}} +
+
+
+
+ + {{f.form.task_members}} +
+
+ + {{f.form.status}} +
+
+
+
+ + {{f.form.end_till}} +
+
+ +
+
+
+
+ diff --git a/project/templates/task/new/forms/create_task.html b/project/templates/task/new/forms/create_task.html index 6aa48b83e..83df3a033 100644 --- a/project/templates/task/new/forms/create_task.html +++ b/project/templates/task/new/forms/create_task.html @@ -1,30 +1,30 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/task/new/forms/create_task_project.html b/project/templates/task/new/forms/create_task_project.html index 19396a24f..ecdd20ea3 100644 --- a/project/templates/task/new/forms/create_task_project.html +++ b/project/templates/task/new/forms/create_task_project.html @@ -1,55 +1,55 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} -
-
- - \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} +
+
+ + diff --git a/project/templates/task/new/forms/create_timesheet.html b/project/templates/task/new/forms/create_timesheet.html index 58475a47e..8bcc28ea0 100644 --- a/project/templates/task/new/forms/create_timesheet.html +++ b/project/templates/task/new/forms/create_timesheet.html @@ -1,38 +1,38 @@ -{% load i18n %} -
- -
{% trans "Timesheet" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
+{% load i18n %} +
+ +
{% trans "Timesheet" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/task/new/forms/quick_create_task_form.html b/project/templates/task/new/forms/quick_create_task_form.html new file mode 100644 index 000000000..70ef7c8f7 --- /dev/null +++ b/project/templates/task/new/forms/quick_create_task_form.html @@ -0,0 +1,26 @@ +
+ {% if form.errors %} + + {% endif %} + {{form.as_p}} +
+ Cancel + +
+
+ \ No newline at end of file diff --git a/project/templates/task/new/forms/update_task.html b/project/templates/task/new/forms/update_task.html index 0e519eeac..af45317f9 100644 --- a/project/templates/task/new/forms/update_task.html +++ b/project/templates/task/new/forms/update_task.html @@ -1,55 +1,55 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- - \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ + diff --git a/project/templates/task/new/forms/update_timesheet.html b/project/templates/task/new/forms/update_timesheet.html index 9ad068ce2..07f9b80f1 100644 --- a/project/templates/task/new/forms/update_timesheet.html +++ b/project/templates/task/new/forms/update_timesheet.html @@ -1,37 +1,37 @@ -{% load i18n %} -
- -
{% trans "Timesheet" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
\ No newline at end of file +{% load i18n %} +
+ +
{% trans "Timesheet" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
diff --git a/project/templates/task/new/overall.html b/project/templates/task/new/overall.html index a0fa03e2a..1d7886fbb 100644 --- a/project/templates/task/new/overall.html +++ b/project/templates/task/new/overall.html @@ -1,144 +1,141 @@ -{% extends 'index.html' %} - -{% block content %} -{% load i18n %} {% load yes_no %}{% load static %} - -
- {% include 'task/new/task_navbar.html' %} -
-
-
- {% if view_type == 'list' %} - {% include 'task/new/task_list_view.html' %} - {% else %} - {% include 'task/new/task_kanban_view.html' %} - {% endif %} -
- - - - - - - - - - - +{% extends 'index.html' %} + +{% block content %} +{% load i18n %} {% load horillafilters %}{% load static %} + +
+ {% include 'task/new/task_navbar.html' %} +
+{% include "generic/components.html" %} +
+ {% include "cbv/projects/project_details.html" %} + +
+ {% if view_type == 'list' %} + {% include 'task/new/task_list_view.html' %} + {% else %} + {% include 'task/new/task_kanban_view.html' %} + {% endif %} +
+
+ + + + + + + + + + + + + + + + + {% endblock content %} diff --git a/project/templates/task/new/task_accordion_view.html b/project/templates/task/new/task_accordion_view.html index a67726c6d..eee7d93c2 100644 --- a/project/templates/task/new/task_accordion_view.html +++ b/project/templates/task/new/task_accordion_view.html @@ -1,105 +1,105 @@ -{% load i18n %} {% load yes_no %} -
-
-
- {% comment %} -
-
-
-
-
- -
-
{% trans "Tasks" %}
-
-
-
{% trans "Task Assigner" %}
-
{% trans "Task Members" %}
-
{% trans "End Date" %}
-
{% trans "Stage" %}
-
{% trans "Document" %}
-
{% trans "Description" %}
-
{% trans "Actions" %}
-
-
- {% endcomment %} - -
-
{{task.title}}
-
{{task.task_assigner}}
-
- {% for employee in task.task_members.all %} {{employee}}
- {% endfor %} -
-
{{task.end_date}}
- {% comment %} -
{{task.stage}}
- {% endcomment %} -
- -
- -
{{task.document}}
-
{{task.description}}
-
-
-
- - - +{% load i18n %} {% load horillafilters %} +
+
+
+ {% comment %} +
+
+
+
+
+ +
+
{% trans "Tasks" %}
+
+
+
{% trans "Task Assigner" %}
+
{% trans "Task Members" %}
+
{% trans "End Date" %}
+
{% trans "Stage" %}
+
{% trans "Document" %}
+
{% trans "Description" %}
+
{% trans "Actions" %}
+
+
+ {% endcomment %} + +
+
{{task.title}}
+
{{task.task_assigner}}
+
+ {% for employee in task.task_members.all %} {{employee}}
+ {% endfor %} +
+
{{task.end_date}}
+ {% comment %} +
{{task.stage}}
+ {% endcomment %} +
+ +
+ +
{{task.document}}
+
{{task.description}}
+
+
+
+ + + diff --git a/project/templates/task/new/task_card_view.html b/project/templates/task/new/task_card_view.html new file mode 100644 index 000000000..2a0e38c5f --- /dev/null +++ b/project/templates/task/new/task_card_view.html @@ -0,0 +1,209 @@ + +{% load static %} +{% load i18n %} + + +{% comment %} for showing messages {% endcomment %} +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} +
+
+{% include "filter_tags.html" %} + +{% if stages %} + {% comment %} vertical tabs {% endcomment %} +
+
+
+ + {% comment %} stages view {% endcomment %} + {% for stage in stages %} +
+
+
+ {{stage.title}} +
+
+ + {{ stage.tasks.all|length}} + +
+ {% comment %} drop down menu {% endcomment %} +
+
+
+ + + +
+ + +
+
+ +
+
+
+
+
+ + {% comment %} task inside stage {% endcomment %} +
+ + {% for task in stage.tasks.all|dictsort:"sequence" %} + {% if task in tasks %} +
+
+ +
+
+ task +
+ +
+ + {{task}} + + {{task.task_manager}}
+ {{task.end_date}}
+ {{task.get_status_display}} +
+
+
+ + + {% comment %} drop down inside card {% endcomment %} + +
+ +
+
+ +
+
+
+
+ +
+ {% endif %} + {% endfor %} +
+
+ {% endfor %} + + {% comment %} + {% trans "Stage" %} + {% endcomment %} + +
+ +
+
+{% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available tasks; please create a new one." %}

+
+
+{% endif %} + + + {% comment %} js files {% endcomment %} + + + diff --git a/project/templates/task/new/task_details.html b/project/templates/task/new/task_details.html index c5c5b1d0d..7500506b6 100644 --- a/project/templates/task/new/task_details.html +++ b/project/templates/task/new/task_details.html @@ -1,134 +1,133 @@ -{% load i18n %} {% load yes_no %} - -
- -
{{task.title}}
-
- -
-
- {% trans "Title" %} - {{task.title}} -
-
- {% trans "Project" %} - {{task.project}} -
-
-
-
- {% trans "Stage" %} - {{task.stage}} -
-
- {% trans "Task manager" %} - {{task.task_manager}} -
-
-
-
- {% trans "Task members" %} - {% for member in task.task_members.all %}{{member}}, {% endfor %} -
-
- {% trans "Status" %} - {{task.get_status_display}} -
-
-
-
- {% trans "End Date" %} - {{task.end_date}} - -
-
- {% trans "Description" %} - {{task.description}} -
-
-
-
- {% trans "Document" %} - {{task.document}} - - -
-
-
- -
-
- {% comment %} {% endcomment %} - +{% load i18n %} {% load horillafilters %} + +
+ +
{{task.title}}
+
+ +
+
+ {% trans "Title" %} + {{task.title}} +
+
+ {% trans "Project" %} + {{task.project}} +
+
+
+
+ {% trans "Stage" %} + {{task.stage}} +
+
+ {% trans "Task manager" %} + {{task.task_manager}} +
+
+
+
+ {% trans "Task members" %} + {% for member in task.task_members.all %}{{member}}, {% endfor %} +
+
+ {% trans "Status" %} + {{task.get_status_display}} +
+
+
+
+ {% trans "End Date" %} + {{task.end_date}} + +
+
+ {% trans "Description" %} + {{task.description}} +
+
+
+
+ {% trans "Document" %} + {{task.document}} + + +
+
+
+ +
+
+ {% comment %} {% endcomment %} diff --git a/project/templates/task/new/task_kanban_view.html b/project/templates/task/new/task_kanban_view.html index 466665fe3..14c6c0642 100644 --- a/project/templates/task/new/task_kanban_view.html +++ b/project/templates/task/new/task_kanban_view.html @@ -1,210 +1,168 @@ - -{% load static %} -{% load i18n %} - - -{% comment %} for showing messages {% endcomment %} -{% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
+{% load i18n %} +{% load static %} + +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} + +{% include "filter_tags.html" %} + +{% if stages %} +
+
+ {% for stage in stages %} +
+
+ + {{stage.title}} +
+ + {{ stage.tasks.all|length}} + +
+
+ {% if request.user|is_project_manager:stage.project or request.user.is_superuser %} +
+ + + +
+ {% endif %} +
+
+
+
+ {% for task in stage.tasks.all|dictsort:"sequence" %} + {% if task in tasks %} +
+
+
+
+ {{task}} +
+ {{task}} +
+ {% if request.user|is_project_manager:stage.project or request.user.is_superuser or request.user|is_task_manager:task %} + + {% endif %} +
+
+ +
+ {% trans "Managers: " %} + + {% for manager in task.task_managers.all %} + {{manager}} + {% endfor %} + +
+
+ + +
+
+
+ +
+ {% endif %} + {% endfor %} +
+
+ {% endfor %} + {% if request.user|is_project_manager:stages.first.project or request.user.is_superuser %} +
+ +
+ {% endif %} +
+
+{% else %} +
+
+ Page not found. 404. +

+ {% trans "No search result found!" %}

+
+
{% endif %} -
-
-{% include "filter_tags.html" %} - -{% if stages %} - {% comment %} vertical tabs {% endcomment %} -
-
-
- - {% comment %} stages view {% endcomment %} - {% for stage in stages %} -
-
-
- {{stage.title}} -
-
- - {{ stage.tasks.all|length}} - -
- - {% comment %} drop down menu {% endcomment %} -
-
-
- - - -
- - -
-
- -
-
-
-
-
- - {% comment %} task inside stage {% endcomment %} -
- - {% for task in stage.tasks.all|dictsort:"sequence" %} - {% if task in tasks %} -
-
- -
-
- task -
- -
- - {{task}} - - {{task.task_manager}}
- {{task.end_date}}
- {{task.get_status_display}} -
-
-
- - - {% comment %} drop down inside card {% endcomment %} - -
- -
-
- -
-
-
-
- -
- {% endif %} - {% endfor %} -
-
- {% endfor %} - - - {% trans "Stage" %} - - -
- -
-
-{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available tasks; please create a new one." %}

-
-
-{% endif %} - - - {% comment %} js files {% endcomment %} - - - diff --git a/project/templates/task/new/task_list_view.html b/project/templates/task/new/task_list_view.html index a26fb644d..6c3dfc95e 100644 --- a/project/templates/task/new/task_list_view.html +++ b/project/templates/task/new/task_list_view.html @@ -1,427 +1,214 @@ -{% load static %} -{% load i18n %} {% load yes_no %} - - -{% comment %} for showing messages {% endcomment %} -
+{% load static %} +{% load i18n %} +{% load horillafilters taskfilters %} + +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} +
+ {% if request.user|is_project_manager:stages.first.project or request.user.is_superuser %} +
+ +
+ {% endif %} + {% if stages %} +
+
+ {% for stage in stages %} + +
+
+ + + {{ stage.tasks.all|length}} + + {{stage.title}} + +
+
+ +
+
+ {% if request.user|is_project_manager:stage.project or request.user.is_superuser %} + +
+ + +
+ {% endif %} +
+
+
+
+ +
+ {% if stage.tasks.all %} + +
+
+
+
+
+
+
+
+ +
+
+ {% trans "Tasks" %} +
+
+
+
{% trans "Task Managers" %}
+
{% trans "End Date" %}
+
{% trans "Status" %}
+
{% trans "Stage" %}
+
{% trans "Document" %}
+
{% trans "Description" %}
+ {% if request.user|is_project_manager:stage.project or request.user.is_superuser %} +
{% trans "Actions" %}
+ {% endif %} +
+
+ {% for task in stage.tasks.all %} + {% if task in tasks %} +
+
+
+
+ +
+ {{task.title}} +
+
+
+ {% for manager in task.task_managers.all %} + {{manager}}, + {% endfor %} +
+
{{task.end_date}}
+
{{task.get_status_display}}
+
+ +
+
+ {% if task.document %} + +   View + + {% else %} + {% trans "None" %} + {% endif %} +
+
{{task.description}}
+ {% if request.user|is_project_manager:stage.project or request.user.is_superuser %} +
+
+ + + + + + +
+
+ {% endif %} +
+ {% endif %} + {% endfor %} + +
+
+
+ + {% else %} +
+ Empty +

+ {% trans "No tasks found in this stage." %} +

+
+ {% endif %} +
+ {% endfor %} +
+
+ {% else %} +
+
+ Page not found. 404. +

{% trans "No search result found!" %}

+
+
+ {% endif %}
-{% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
-{% endif %} -{% include "filter_tags.html" %} -{% if stages %} -
-
-
- {% for stage in stages %} - -
-
- - - {{ stage.tasks.all|length}} - - {{stage.title}} - -
-
- -
-
- -
- - -
-
-
-
- -
-
-
-
-
-
-
-
- -
-
- {% trans "Tasks" %} -
-
-
-
{% trans "Task Manager" %}
-
{% trans "End Date" %}
-
{% trans "Status" %}
-
{% trans "Stage" %}
-
{% trans "Document" %}
-
{% trans "Description" %}
-
{% trans "Actions" %}
-
-
- {% for task in stage.tasks.all %} - - {% comment %} {% if task in tasks %} {% endcomment %} -
-
{{task.title}}
-
{{task.task_manager}}
-
{{task.end_date}}
-
{{task.get_status_display}}
-
- -
- -
{{task.document}}
-
{{task.description}}
-
-
- - - {% comment %} - - {% endcomment %} - - - - -
-
-
- {% comment %} {% endif %} {% endcomment %} - {% endfor %} -
-
-
-
- {% endfor %} - - -
-
-
-{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available tasks; please create a new one." %}

-
-
-{% endif %} - - - - - - - - -{% comment %} -
-
-
- {% for project_stage in project_stages %} {{project_stage.stage}}
- {% endfor %} - {% for task in tasks %} -
- {{ task.task_title }} -
-
- -
- -
-
-
-
-
-
-
-
-
-
Task Assigner
-
Task Members
-
End Date
-
Status
-
Documents
-
Description
-
- -
- -
-
-
-
-
-
-
-
-
-
- Mary Magdalene -
- {{task.task_assigner_id}} -
-
-
- {% for employee in task.task_members_id.all %} {{employee}}
- {% endfor %} -
-
{{task.end_date}}
-
{{task.status}}
-
{{task.document}}
-
{{task.description}}
-
-
-
- -
- -
- {% endfor %} - -
-
-
- - - - - - - {% endcomment %} - -{% comment %} js files {% endcomment %} diff --git a/project/templates/task/new/task_navbar.html b/project/templates/task/new/task_navbar.html index 11e5f5774..d4e18b87f 100644 --- a/project/templates/task/new/task_navbar.html +++ b/project/templates/task/new/task_navbar.html @@ -1,104 +1,72 @@ -{% load i18n %} - -
- -
-

{{project}} {% trans ":Tasks" %}

- - - -
- -
- {% comment %} for search{% endcomment %} -
- - -
- - -
-
    - {% comment %} for list view {% endcomment %} -
  • - - - -
  • - {% comment %} for card view {% endcomment %} -
  • - - -
  • -
-
- {% comment %} for filtering {% endcomment %} -
- - -
- {% comment %} for create task {% endcomment %} - -
-
- +{% load i18n taskfilters %} +
+
+ +
+

{{project}}

+ + + +
+ +
+
+ + +
+ +
+ +
+ +
+ + +
+ {% if request.user|is_project_manager:project or request.user.is_superuser %} + + {% endif %} +
+
+
diff --git a/project/templates/task/new/task_timesheet.html b/project/templates/task/new/task_timesheet.html index 06d889b28..7a6c3058c 100644 --- a/project/templates/task/new/task_timesheet.html +++ b/project/templates/task/new/task_timesheet.html @@ -1,140 +1,137 @@ -{% load i18n %} {% load yes_no %} {% load static %} - -
- -
Time Sheet
-
- {% comment %} for add timesheet {% endcomment %} -
- -
- - {% if time_sheets %} -
-
-
-
-
-
-
{% trans "Employee" %}
-
-
- {% comment %} -
{% trans "Employee" %}
- {% endcomment %} -
{% trans "Project" %}
-
{% trans "Task" %}
-
{% trans "Date" %}
-
{% trans "Time Spent" %}
-
{% trans "Status" %}
-
{% trans "Description" %}
-
{% trans "Actions" %}
-
-
- - {% for time_sheet in time_sheets %} -
-
-
-
- Username -
- {{time_sheet.employee_id.employee_first_name}} - {{time_sheet.employee_id.employee_last_name|default:""}} - -
-
-
{{time_sheet.project_id.title}}
- -
{{time_sheet.task_id}}
-
{{time_sheet.date}}
-
{{time_sheet.time_spent}}
-
{{time_sheet.get_status_display}}
-
{{time_sheet.description|truncatechars:15}}
-
-
- - - - -
-
-
- {% endfor %} -
-
- {% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available timesheets; please create a new one." %}

-
-
- {% endif %} -
- - - +{% load i18n %} {% load horillafilters %} {% load static %} + +
+ +
Time Sheet
+
+ {% comment %} for add timesheet {% endcomment %} +
+ +
+ + {% if time_sheets %} +
+
+
+
+
+
+
{% trans "Employee" %}
+
+
+ {% comment %} +
{% trans "Employee" %}
+ {% endcomment %} +
{% trans "Project" %}
+
{% trans "Task" %}
+
{% trans "Date" %}
+
{% trans "Time Spent" %}
+
{% trans "Status" %}
+
{% trans "Description" %}
+
{% trans "Actions" %}
+
+
+ + {% for time_sheet in time_sheets %} +
+
+
+
+ Username +
+ {{time_sheet.employee_id.employee_first_name}} + {{time_sheet.employee_id.employee_last_name|default:""}} + +
+
+
{{time_sheet.project_id.title}}
+ +
{{time_sheet.task_id}}
+
{{time_sheet.date}}
+
{{time_sheet.time_spent}}
+
{{time_sheet.get_status_display}}
+
{{time_sheet.description|truncatechars:15}}
+
+
+ + + + +
+
+
+ {% endfor %} +
+
+ {% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available timesheets; please create a new one." %}

+
+
+ {% endif %} +
diff --git a/project/templates/task_all/forms/create_project_stage_taskall.html b/project/templates/task_all/forms/create_project_stage_taskall.html index 652f823fa..58c20110e 100644 --- a/project/templates/task_all/forms/create_project_stage_taskall.html +++ b/project/templates/task_all/forms/create_project_stage_taskall.html @@ -1,88 +1,88 @@ -{% load i18n %} -
- -
{% trans "Project Stage" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project Stage" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ diff --git a/project/templates/task_all/forms/create_taskall.html b/project/templates/task_all/forms/create_taskall.html index 6ab8baba9..c67c6cfca 100644 --- a/project/templates/task_all/forms/create_taskall.html +++ b/project/templates/task_all/forms/create_taskall.html @@ -1,115 +1,115 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- -{% comment %} modals for showing new project and new task creation {% endcomment %} - - - - +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ +{% comment %} modals for showing new project and new task creation {% endcomment %} + + + + diff --git a/project/templates/task_all/forms/update_taskall.html b/project/templates/task_all/forms/update_taskall.html index cbde0e180..6fa29c144 100644 --- a/project/templates/task_all/forms/update_taskall.html +++ b/project/templates/task_all/forms/update_taskall.html @@ -1,110 +1,110 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
-{% comment %} modals for showing new project and new task creation {% endcomment %} - - - \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+{% comment %} modals for showing new project and new task creation {% endcomment %} + + + diff --git a/project/templates/task_all/task_all_card.html b/project/templates/task_all/task_all_card.html index 5b7b9b0c8..d86359580 100644 --- a/project/templates/task_all/task_all_card.html +++ b/project/templates/task_all/task_all_card.html @@ -1,157 +1,157 @@ -{% load i18n %} -{% load static %} -{% if messages %} -
- {% for message in messages %} -
-
- {{ message }} -
-
- {% endfor %} -
-{% endif %} -{% include "filter_tags.html" %} - -{% comment %} easy filters {% endcomment %} -
- - - {% trans "To Do" %} - - - - {% trans "In progress" %} - - - - {% trans "Completed" %} - - - - {% trans "Expired" %} - -
- -{% if tasks %} -
- {% for task in tasks %} -
- - -
-
- Username -
-
-
- {{task.title}} - {% trans "Project Name" %}: {{task.project}}
- {% trans "Stage Name" %} : {{task.stage}}
- {% trans "End Date" %} : {{task.end_date}} -
-
-
-
- - -
-
-
- {% endfor %} -
- -
- - {% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}. - - -
-{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available tasks; please create a new one." %}

-
-
-{% endif %} +{% load i18n %} +{% load static %} +{% if messages %} +
+ {% for message in messages %} +
+
+ {{ message }} +
+
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} + +{% comment %} easy filters {% endcomment %} +
+ + + {% trans "To Do" %} + + + + {% trans "In progress" %} + + + + {% trans "Completed" %} + + + + {% trans "Expired" %} + +
+ +{% if tasks %} +
+ {% for task in tasks %} +
+ + +
+
+ Username +
+
+
+ {{task.title}} + {% trans "Project Name" %}: {{task.project}}
+ {% trans "Stage Name" %} : {{task.stage}}
+ {% trans "End Date" %} : {{task.end_date}} +
+
+
+
+ + +
+
+
+ {% endfor %} +
+ +
+ + {% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}. + + +
+{% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available tasks; please create a new one." %}

+
+
+{% endif %} diff --git a/project/templates/task_all/task_all_filter.html b/project/templates/task_all/task_all_filter.html index 6afb977a7..a9dc87472 100644 --- a/project/templates/task_all/task_all_filter.html +++ b/project/templates/task_all/task_all_filter.html @@ -1,48 +1,48 @@ -{% load i18n %} {% load basefilters %} -
-
-
{% trans "Task" %}
-
-
-
-
- - {{f.form.task_manager}} -
-
- - {{f.form.stage}} -
-
-
-
- - {{f.form.project}} -
-
- - {{f.form.status}} -
-
-
-
- - {{f.form.end_till}} -
-
- - -
-
-
-
- +{% load i18n %} {% load basefilters %} +
+
+
{% trans "Task" %}
+
+
+
+
+ + {{f.form.task_manager}} +
+
+ + {{f.form.stage}} +
+
+
+
+ + {{f.form.project}} +
+
+ + {{f.form.status}} +
+
+
+
+ + {{f.form.end_till}} +
+
+ + +
+
+
+
+ diff --git a/project/templates/task_all/task_all_list.html b/project/templates/task_all/task_all_list.html index cb98c6bd0..f9a023b60 100644 --- a/project/templates/task_all/task_all_list.html +++ b/project/templates/task_all/task_all_list.html @@ -1,180 +1,180 @@ -{% load i18n %} -{% load static %} -{% if messages %} -
- {% for message in messages %} -
-
- {{ message }} -
-
- {% endfor %} -
-{% endif %} -{% include "filter_tags.html" %} -{% if tasks %} -
- {% comment %} easy filters {% endcomment %} -
- - - {% trans "To Do" %} - - - - {% trans "In progress" %} - - - - {% trans "Completed" %} - - - - {% trans "Expired" %} - -
-
-
-
-
-
-
-
- -
-
- {% trans "Task" %} -
-
-
-
{% trans "Project" %}
-
{% trans "Stage" %}
-
{% trans "Mangers" %}
-
{% trans "Members" %}
-
{% trans "End Date" %}
-
{% trans "Status" %}
-
{% trans "Description" %}
- {% comment %}
{% endcomment %} -
-
-
- {% for task in tasks %} -
-
-
-
-
- -
-
- {{task.title}} -
-
-
- - {{task.project}} - {{task.stage}} - - {{task.task_manager}} - - {% for member in task.task_members.all %} - {{member}}, - {% endfor %} - - - {{task.end_date}} - {{task.get_status_display}} - {{task.description}} - - {% comment %} - {% if perms.recruitment.view_history %} - - {% endif %} - {% endcomment %} -
-
- {% comment %} {% if perms.recruitment.change_candidate %} {% endcomment %} - - {% comment %} {% endif %} {% endcomment %} - {% comment %} {% if perms.recruitment.delete_candidate %} {% endcomment %} -
- {% csrf_token %} - -
- {% comment %} {% endif %} {% endcomment %} -
-
- - -
- -
- {% endfor %} -
-
-
- {% comment %} pagination {% endcomment %} -
- - {% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}. - - -
-{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available tasks; please create a new one." %}

-
-
-{% endif %} - - \ No newline at end of file +{% load i18n %} +{% load static %} +{% if messages %} +
+ {% for message in messages %} +
+
+ {{ message }} +
+
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} +{% if tasks %} +
+ {% comment %} easy filters {% endcomment %} +
+ + + {% trans "To Do" %} + + + + {% trans "In progress" %} + + + + {% trans "Completed" %} + + + + {% trans "Expired" %} + +
+
+
+
+
+
+
+
+ +
+
+ {% trans "Task" %} +
+
+
+
{% trans "Project" %}
+
{% trans "Stage" %}
+
{% trans "Mangers" %}
+
{% trans "Members" %}
+
{% trans "End Date" %}
+
{% trans "Status" %}
+
{% trans "Description" %}
+ {% comment %}
{% endcomment %} +
+
+
+ {% for task in tasks %} +
+
+
+
+
+ +
+
+ {{task.title}} +
+
+
+ + {{task.project}} + {{task.stage}} + + {{task.task_manager}} + + {% for member in task.task_members.all %} + {{member}}, + {% endfor %} + + + {{task.end_date}} + {{task.get_status_display}} + {{task.description}} + + {% comment %} + {% if perms.recruitment.view_history %} + + {% endif %} + {% endcomment %} +
+
+ {% comment %} {% if perms.recruitment.change_candidate %} {% endcomment %} + + {% comment %} {% endif %} {% endcomment %} + {% comment %} {% if perms.recruitment.delete_candidate %} {% endcomment %} +
+ {% csrf_token %} + +
+ {% comment %} {% endif %} {% endcomment %} +
+
+ + +
+ +
+ {% endfor %} +
+
+
+ {% comment %} pagination {% endcomment %} +
+ + {% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}. + + +
+{% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available tasks; please create a new one." %}

+
+
+{% endif %} + + diff --git a/project/templates/task_all/task_all_navbar.html b/project/templates/task_all/task_all_navbar.html index 8480c0f6c..46b6f8d16 100644 --- a/project/templates/task_all/task_all_navbar.html +++ b/project/templates/task_all/task_all_navbar.html @@ -1,168 +1,168 @@ -{% load i18n %} - -
- -
-

{% trans "Tasks" %}

- - - -
- -
- {% if tasks %} -
- {% comment %} for search{% endcomment %} - -
- - -
- - -
-
    - {% comment %} for list view {% endcomment %} -
  • - - - -
  • - {% comment %} for card view {% endcomment %} -
  • - - -
  • -
-
- {% comment %} for filtering {% endcomment %} -
- - -
-
- - {% comment %} for actions {% endcomment %} -
-
- - -
-
- - {% endif %} - {% comment %} for create {% endcomment %} - -
-
- - \ No newline at end of file +{% load i18n %} + +
+ +
+

{% trans "Tasks" %}

+ + + +
+ +
+ {% if tasks %} +
+ {% comment %} for search{% endcomment %} + +
+ + +
+ + +
+
    + {% comment %} for list view {% endcomment %} +
  • + + + +
  • + {% comment %} for card view {% endcomment %} +
  • + + +
  • +
+
+ {% comment %} for filtering {% endcomment %} +
+ + +
+
+ + {% comment %} for actions {% endcomment %} +
+
+ + +
+
+ + {% endif %} + {% comment %} for create {% endcomment %} + +
+
+ + diff --git a/project/templates/task_all/task_all_overall.html b/project/templates/task_all/task_all_overall.html index 3dd46e3c6..b78c6847f 100644 --- a/project/templates/task_all/task_all_overall.html +++ b/project/templates/task_all/task_all_overall.html @@ -1,123 +1,122 @@ -{% extends 'index.html' %} -{% load static %} -{% block content %} - -
- {% include 'task_all/task_all_navbar.html' %} - -
- -
- {% if view_type == 'list' %} - {% include 'task_all/task_all_list.html' %} - {% else %} - {% include 'task_all/task_all_card.html' %} - {% endif %} -
- - - - - - - - - -{% endblock content %} - +{% extends 'index.html' %} +{% load static %} +{% block content %} + +
+ {% include 'task_all/task_all_navbar.html' %} + +
+ +
+ {% if view_type == 'list' %} + {% include 'task_all/task_all_list.html' %} + {% else %} + {% include 'task_all/task_all_card.html' %} + {% endif %} +
+ + + + + + + + + +{% endblock content %} diff --git a/project/templates/task_all/update_task.html b/project/templates/task_all/update_task.html index f5e6d72f9..101ae7370 100644 --- a/project/templates/task_all/update_task.html +++ b/project/templates/task_all/update_task.html @@ -1,42 +1,42 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- - \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ + diff --git a/project/templates/time_sheet/chart.html b/project/templates/time_sheet/chart.html index 97e8011cb..03d555905 100644 --- a/project/templates/time_sheet/chart.html +++ b/project/templates/time_sheet/chart.html @@ -1,252 +1,252 @@ -{% extends 'index.html' %}{% block content %}{% load static %} {% load i18n %} -{% load basefilters %} {% if request.user.employee_get.id == emp_id or perms.project.view_timesheet or request.user|is_reportingmanager %} -
-
-
-
- Personal Timesheet of {{emp_name|capfirst}} -
-
-
- - - -
-
-
- -
-
-{% else %} {% include '404.html' %} {% endif %} - - - - -{% endblock %} +{% extends 'index.html' %}{% block content %}{% load static %} {% load i18n %} +{% load basefilters %} {% if request.user.employee_get.id == emp_id or perms.project.view_timesheet or request.user|is_reportingmanager %} +
+
+
+
+ Personal Timesheet of {{emp_name|capfirst}} +
+
+
+ + + +
+
+
+ +
+
+{% else %} {% include '404.html' %} {% endif %} + + + + +{% endblock %} diff --git a/project/templates/time_sheet/filters.html b/project/templates/time_sheet/filters.html index 94a0b12d2..767d0db4f 100644 --- a/project/templates/time_sheet/filters.html +++ b/project/templates/time_sheet/filters.html @@ -1,66 +1,66 @@ -{% load i18n %} {% load basefilters %} -
-
-
-
{% trans "Time Sheet" %}
-
-
-
-
- - {{f.form.project_id}} -
-
- - {{f.form.status}} -
-
- -
-
- - {{f.form.task}} -
-
- - {{f.form.date}} -
-
- {% if perms.project.view_timesheet or request.user|is_reportingmanager %} -
-
- - {{f.form.employee_id}} -
-
- {% endif %} -
-
-
- -
-
{% trans "Advanced" %}
-
-
-
-
- - {{f.form.start_from}} -
-
-
-
- - {{f.form.end_till}} -
-
-
-
-
-
- -
+{% load i18n %} {% load basefilters %} +
+
+
+
{% trans "Time Sheet" %}
+
+
+
+
+ + {{f.form.project_id}} +
+
+ + {{f.form.status}} +
+
+ +
+
+ + {{f.form.task}} +
+
+ + {{f.form.date}} +
+
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %} +
+
+ + {{f.form.employee_id}} +
+
+ {% endif %} +
+
+
+ +
+
{% trans "Advanced" %}
+
+
+
+
+ + {{f.form.start_from}} +
+
+
+
+ + {{f.form.end_till}} +
+
+
+
+
+
+ +
diff --git a/project/templates/time_sheet/form-create.html b/project/templates/time_sheet/form-create.html index e5ab2117d..ac972e6e6 100644 --- a/project/templates/time_sheet/form-create.html +++ b/project/templates/time_sheet/form-create.html @@ -1,126 +1,126 @@ -{% block content %} {% load static %} {% load i18n %} -
- -
{% trans "Time Sheet" %}
-
-
- -
- {% csrf_token %} {{form.as_p}} - -
-
- - - - -{% endblock content %} +{% block content %} {% load static %} {% load i18n %} +
+ +
{% trans "Time Sheet" %}
+
+
+ +
+ {% csrf_token %} {{form.as_p}} + +
+
+ + + + +{% endblock content %} diff --git a/project/templates/time_sheet/form-update.html b/project/templates/time_sheet/form-update.html index 03bbf530e..5c53d22a0 100644 --- a/project/templates/time_sheet/form-update.html +++ b/project/templates/time_sheet/form-update.html @@ -27,8 +27,8 @@
{% trans "Time Sheet" %}
-webkit-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;" onclick="event.stopPropagation(); $(this).parents().find('.oh-modal--show:last').toggleClass('oh-modal--show')" - id="close1" - + id="close1" + > diff --git a/project/templates/time_sheet/form_project_time_sheet.html b/project/templates/time_sheet/form_project_time_sheet.html index f5f2d53e1..db8e46ec7 100644 --- a/project/templates/time_sheet/form_project_time_sheet.html +++ b/project/templates/time_sheet/form_project_time_sheet.html @@ -1,92 +1,92 @@ -{% load i18n %} -
- -
{% trans "Project" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Project" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ diff --git a/project/templates/time_sheet/form_task_time_sheet.html b/project/templates/time_sheet/form_task_time_sheet.html index 04e0ea5a2..d9194dac3 100644 --- a/project/templates/time_sheet/form_task_time_sheet.html +++ b/project/templates/time_sheet/form_task_time_sheet.html @@ -1,110 +1,110 @@ -{% load i18n %} -
- -
{% trans "Task" %}
-
-
- -
- {% csrf_token %} {{ form.as_p }} - -
-
- \ No newline at end of file +{% load i18n %} +
+ +
{% trans "Task" %}
+
+
+ +
+ {% csrf_token %} {{ form.as_p }} + +
+
+ diff --git a/project/templates/time_sheet/time_sheet_card_view.html b/project/templates/time_sheet/time_sheet_card_view.html index c2c50d52b..9778b4c80 100644 --- a/project/templates/time_sheet/time_sheet_card_view.html +++ b/project/templates/time_sheet/time_sheet_card_view.html @@ -1,149 +1,149 @@ -{% load i18n %} -{% load static %} -{% load basefilters %} -{% if messages %} -
- {% for message in messages %} -
- {{ message }} -
- {% endfor %} -
+{% load i18n %} +{% load static %} +{% load basefilters %} +{% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} +
+{% endif %} +{% include "filter_tags.html" %} +{% comment %} easy filters {% endcomment %} +
+ + + {% trans "In progress" %} + + + + {% trans "Completed" %} + +
+{% if time_sheets %} +
+ {% for time_sheet in time_sheets %} +
+ +
+
+ Username +
+
+ +
+ {{time_sheet.employee_id}} + {{time_sheet.date}}
+ {{time_sheet.project_id}}
+ {{time_sheet.task_id}} | + {% trans "Time Spent" %} : {{time_sheet.time_spent}} +
+
+
+
+ + +
+
+
+ {% endfor %} +
+ + +
+ + {% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}. + + +
+ {% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available timesheets; please create a new one." %}

+
+
{% endif %} -{% include "filter_tags.html" %} -{% comment %} easy filters {% endcomment %} -
- - - {% trans "In progress" %} - - - - {% trans "Completed" %} - -
-{% if time_sheets %} -
- {% for time_sheet in time_sheets %} -
- -
-
- Username -
-
- -
- {{time_sheet.employee_id}} - {{time_sheet.date}}
- {{time_sheet.project_id}}
- {{time_sheet.task_id}} | - {% trans "Time Spent" %} : {{time_sheet.time_spent}} -
-
-
-
- - -
-
-
- {% endfor %} -
- - -
- - {% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}. - - -
- {% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available timesheets; please create a new one." %}

-
-
-{% endif %} \ No newline at end of file diff --git a/project/templates/time_sheet/time_sheet_list_view.html b/project/templates/time_sheet/time_sheet_list_view.html index ef0cabe8b..45ef9f122 100644 --- a/project/templates/time_sheet/time_sheet_list_view.html +++ b/project/templates/time_sheet/time_sheet_list_view.html @@ -1,188 +1,188 @@ -{% load i18n %} {% load yes_no %} {% load static %} -{% include "filter_tags.html" %} - -{% if time_sheets %} -
- {% comment %} easy filters {% endcomment %} -
- - - {% trans "In progress" %} - - - - {% trans "Completed" %} - -
- {% comment %} table of contents {% endcomment %} -
-
-
-
-
-
-
- -
-
{% trans "Employee" %}
-
-
- {% comment %} -
{% trans "Employee" %}
- {% endcomment %} -
{% trans "Project" %}
-
{% trans "Task" %}
-
{% trans "Date" %}
-
{% trans "Time Spent" %}
-
{% trans "Status" %}
-
{% trans "Description" %}
-
{% trans "Actions" %}
-
-
- - {% for time_sheet in time_sheets %} -
-
-
-
- -
-
-
- Username -
- {{time_sheet.employee_id.employee_first_name}} - {{time_sheet.employee_id.employee_last_name|default:""}} - -
-
-
-
{{time_sheet.project_id.title}}
-
{{time_sheet.task_id}}
-
{{time_sheet.date}}
-
{{time_sheet.time_spent}}
-
{{time_sheet.get_status_display}}
-
{{time_sheet.description|truncatechars:15}}
-
- -
-
- {% endfor %} -
-
-
- -
- - {% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}. - - -
-{% else %} -
-
- Page not found. 404. -

{% trans "There are currently no available timesheets; please create a new one." %}

-
-
-{% endif %} - - \ No newline at end of file +{% load i18n %} {% load horillafilters %} {% load static %} +{% include "filter_tags.html" %} + +{% if time_sheets %} +
+ {% comment %} easy filters {% endcomment %} +
+ + + {% trans "In progress" %} + + + + {% trans "Completed" %} + +
+ {% comment %} table of contents {% endcomment %} +
+
+
+
+
+
+
+ +
+
{% trans "Employee" %}
+
+
+ {% comment %} +
{% trans "Employee" %}
+ {% endcomment %} +
{% trans "Project" %}
+
{% trans "Task" %}
+
{% trans "Date" %}
+
{% trans "Time Spent" %}
+
{% trans "Status" %}
+
{% trans "Description" %}
+
{% trans "Actions" %}
+
+
+ + {% for time_sheet in time_sheets %} +
+
+
+
+ +
+
+
+ Username +
+ {{time_sheet.employee_id.employee_first_name}} + {{time_sheet.employee_id.employee_last_name|default:""}} + +
+
+
+
{{time_sheet.project_id.title}}
+
{{time_sheet.task_id}}
+
{{time_sheet.date}}
+
{{time_sheet.time_spent}}
+
{{time_sheet.get_status_display}}
+
{{time_sheet.description|truncatechars:15}}
+
+ +
+
+ {% endfor %} +
+
+
+ +
+ + {% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}. + + +
+{% else %} +
+
+ Page not found. 404. +

{% trans "There are currently no available timesheets; please create a new one." %}

+
+
+{% endif %} + + diff --git a/project/templates/time_sheet/time_sheet_navbar.html b/project/templates/time_sheet/time_sheet_navbar.html index bcc11ce5d..6a86275e6 100644 --- a/project/templates/time_sheet/time_sheet_navbar.html +++ b/project/templates/time_sheet/time_sheet_navbar.html @@ -1,144 +1,144 @@ -{% load i18n %} -{% load basefilters %} -
-
-

{% trans "Time Sheet" %}

- - - -
- {% if perms.project.view_timesheet or request.user|is_reportingmanager %} -
-
- - -
- {% endif %} -
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
- -
- - -
- {% comment %} for actions {% endcomment %} -
-
- - -
-
- - -
-
-
\ No newline at end of file +{% load i18n %} +{% load basefilters %} +
+
+

{% trans "Time Sheet" %}

+ + + +
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %} +
+
+ + +
+ {% endif %} +
+
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+ +
+ + +
+ {% comment %} for actions {% endcomment %} +
+
+ + +
+
+ + +
+
+
diff --git a/project/templates/time_sheet/time_sheet_single_view.html b/project/templates/time_sheet/time_sheet_single_view.html index c190dd4d0..57f109e37 100644 --- a/project/templates/time_sheet/time_sheet_single_view.html +++ b/project/templates/time_sheet/time_sheet_single_view.html @@ -1,94 +1,92 @@ -{% load i18n %} {% load yes_no %} {% load basefilters %} -
- -
Timesheet Details
-
- -
-
- {% trans "Employee" %} - {{time_sheet.employee_id}} -
-
- {% trans "Project" %} - {{time_sheet.project_id}} -
-
-
-
- {% trans "Task" %} - {{time_sheet.task_id}} -
-
- {% trans "Date" %} - {{time_sheet.date}} -
-
-
-
- {% trans "Time Spent" %} - {{time_sheet.time_spent}} -
-
- {% trans "Status" %} - {{time_sheet.status}} -
-
-
-
- {% trans "Description" %} - {{time_sheet.description}} -
-
-
-
- - {% trans "Edit" %} - - {% if perms.project.view_timesheet or request.user|is_reportingmanager %} - - {% trans "View Timesheet Chart" %} - - {% endif %} - - {% trans "Delete" %} - -
-
-
- - +{% load i18n %} {% load horillafilters %} {% load basefilters %} +
+ +
Timesheet Details
+
+ +
+
+ {% trans "Employee" %} + {{time_sheet.employee_id}} +
+
+ {% trans "Project" %} + {{time_sheet.project_id}} +
+
+
+
+ {% trans "Task" %} + {{time_sheet.task_id}} +
+
+ {% trans "Date" %} + {{time_sheet.date}} +
+
+
+
+ {% trans "Time Spent" %} + {{time_sheet.time_spent}} +
+
+ {% trans "Status" %} + {{time_sheet.status}} +
+
+
+
+ {% trans "Description" %} + {{time_sheet.description}} +
+
+
+
+ + {% trans "Edit" %} + + {% if perms.project.view_timesheet or request.user|is_reportingmanager %} + + {% trans "View Timesheet Chart" %} + + {% endif %} + + {% trans "Delete" %} + +
+
+
diff --git a/project/templates/time_sheet/time_sheet_view.html b/project/templates/time_sheet/time_sheet_view.html index 7ffef8cec..d9b167c72 100644 --- a/project/templates/time_sheet/time_sheet_view.html +++ b/project/templates/time_sheet/time_sheet_view.html @@ -1,65 +1,65 @@ -{% extends 'index.html' %} -{% block content %} - {% load i18n %} - {% load basefilters %} - -
- {% include 'time_sheet/time_sheet_navbar.html' %} - -
- {% if view_type == "card" %} - {% include 'time_sheet/time_sheet_card_view.html' %} - {% else %} - {% include 'time_sheet/time_sheet_list_view.html' %} - {% endif %} -
-
- - - -{% endblock content %} +{% extends 'index.html' %} +{% block content %} + {% load i18n %} + {% load basefilters %} + +
+ {% include 'time_sheet/time_sheet_navbar.html' %} + +
+ {% if view_type == "card" %} + {% include 'time_sheet/time_sheet_card_view.html' %} + {% else %} + {% include 'time_sheet/time_sheet_list_view.html' %} + {% endif %} +
+
+ + + +{% endblock content %} diff --git a/project/templatetags/taskfilters.py b/project/templatetags/taskfilters.py new file mode 100644 index 000000000..b778c49b0 --- /dev/null +++ b/project/templatetags/taskfilters.py @@ -0,0 +1,75 @@ +""" +This module is used to write custom template filters. +""" +from django.template.defaultfilters import register + +from project.models import Project + +@register.filter(name="task_crud_perm") +def task_crud_perm(user,task): + """ + This method is used to check the requested user is task manager or project manager or has permission + """ + try: + employee = user.employee_get + is_task_manager = (employee in task.task_managers.all()) + is_project_manager = (employee in task.project.managers.all()) + return is_task_manager or is_project_manager + + except Exception as _: + return False + + +@register.filter(name="time_sheet_crud_perm") +def time_sheet_crud_perm(user,timesheet): + """ + This method is used to check the requested user is task manager or project manager or has permission + """ + try: + employee = user.employee_get + is_task_manager = (employee in timesheet.task_id.task_managers.all()) + is_project_manager = (employee in timesheet.project_id.managers.all()) + is_own_timesheet = (timesheet.employee_id == employee ) + + return is_task_manager or is_project_manager or is_own_timesheet + + except Exception as _: + return False + + +@register.filter(name="is_project_manager_or_member") +def is_project_manager_or_member(user,project): + """ + This method will return true, if the user is manger or member of the project + """ + employee = user.employee_get + + return ( + Project.objects.filter( + id=project.id, managers=employee + ).exists() + or Project.objects.filter( + id=project.id, members=employee + ).exists() + ) + + +@register.filter(name="is_project_manager") +def is_project_manager(user,project): + """ + This method will return true, if the user is manager of the project + """ + employee = user.employee_get + return Project.objects.filter(id=project.id,managers=employee).exists() + +@register.filter(name="is_task_manager") +def is_task_manager(user, task): + """ + This method will return True if the user is a manager of the task. + """ + try: + employee = user.employee_get + return employee in task.task_managers.all() + except AttributeError: + # Handle cases where user or task might not have the expected structure + return False \ No newline at end of file diff --git a/project/tests.py b/project/tests.py index 7ce503c2d..de8bdc00e 100644 --- a/project/tests.py +++ b/project/tests.py @@ -1,3 +1,3 @@ -from django.test import TestCase - -# Create your tests here. +from django.test import TestCase + +# Create your tests here. diff --git a/project/urls.py b/project/urls.py index 1ff5a8bd3..22436f18d 100644 --- a/project/urls.py +++ b/project/urls.py @@ -1,86 +1,252 @@ from django.urls import path +from project.cbv import dashboard, project_stage, projects, tasks, timesheet from project.models import Project + from . import views + urlpatterns = [ # Dashboard - path('project-dashboard-view',views.dashboard_view,name='project-dashboard-view'), - path('project-status-chart',views.project_status_chart,name='project-status-chart'), - path('task-status-chart',views.task_status_chart,name='task-status-chart'), + path("project-dashboard-view", views.dashboard_view, name="project-dashboard-view"), + path( + "projects-due-in-this-month", + dashboard.ProjectsDueInMonth.as_view(), + name="projects-due-in-this-month", + ), + path( + "project-status-chart", views.project_status_chart, name="project-status-chart" + ), + path("task-status-chart", views.task_status_chart, name="task-status-chart"), + # path( + # "project-detailed-view//", + # views.project_detailed_view, + # name="project-detailed-view", + # ), path( - 'project-detailed-view//', - views.project_detailed_view, - name='project-detailed-view' + "project-detailed-view//", + dashboard.ProjectDetailView.as_view(), + name="project-detailed-view", ), - # Project - path('project-view/',views.project_view,name='project-view'), - path("create-project", views.create_project, name="create-project"), + # path("project-view/", views.project_view, name="project-view"), path( - "update-project//", - views.project_update, - name="update-project" + "project-nav-view/", projects.ProjectsNavView.as_view(), name="project-nav-view" + ), + path( + "project-list-view/", projects.ProjectsList.as_view(), name="project-list-view" + ), + path( + "project-card-view/", + projects.ProjectCardView.as_view(), + name="project-card-view", + ), + path("project-view/", projects.ProjectsView.as_view(), name="project-view"), + # path("create-project", views.create_project, name="create-project"), + path("create-project", projects.ProjectFormView.as_view(), name="create-project"), + # path( + # "update-project//", + # views.project_update, + # name="update-project" + # ), + path( + "update-project//", + projects.ProjectFormView.as_view(), + name="update-project", + ), + path( + "change-project-status//", + views.change_project_status, + name="change-project-status", + ), + path( + "delete-project//", views.project_delete, name="delete-project" ), - path("delete-project//", views.project_delete, name="delete-project"), path("project-filter", views.project_filter, name="project-filter"), path("project-import", views.project_import, name="project-import"), - path("project-bulk-export",views.project_bulk_export,name="project-bulk-export"), - path("project-bulk-archive",views.project_bulk_archive,name="project-bulk-archive"), - path("project-bulk-delete",views.project_bulk_delete,name="project-bulk-delete"), - path('project-archive//',views.project_archive,name='project-archive'), - - + path("project-bulk-export", views.project_bulk_export, name="project-bulk-export"), + path( + "project-bulk-archive", views.project_bulk_archive, name="project-bulk-archive" + ), + path("project-bulk-delete", views.project_bulk_delete, name="project-bulk-delete"), + path( + "project-archive//", + views.project_archive, + name="project-archive", + ), # Task - path('task-view//',views.task_view,name='task-view',kwargs={"model":Project}), - path('create-task//',views.create_task,name='create-task'), - path('create-task-in-project//',views.create_task_in_project,name='create-task-in-project'), - path('update-task//',views.update_task,name='update-task'), - path('delete-task//',views.delete_task,name='delete-task'), - path('task-details//',views.task_details,name='task-details'), - path('task-filter//',views.task_filter,name='task-filter'), - path('task-stage-change',views.task_stage_change,name='task-stage-change'), - path('task-timesheet//',views.task_timesheet,name='task-timesheet'), - path("create-timesheet-task//",views.create_timesheet_task,name="create-timesheet-task"), - path("update-timesheet-task//",views.update_timesheet_task,name="update-timesheet-task"), - path('drag-and-drop-task',views.drag_and_drop_task,name='drag-and-drop-task'), - + path( + "task-view//", + views.task_view, + name="task-view", + kwargs={"model": Project}, + ), + path( + "create-task//", + tasks.TaskCreateForm.as_view(), + name="create-task", + ), + path( + "quick-create-task//", + views.quick_create_task, + name="quick-create-task", + ), + # path("create-task//", views.create_task, name="create-task"), + path( + "create-task-in-project//", + views.create_task_in_project, + name="create-task-in-project", + ), + path( + "create-stage-task//", + tasks.TaskCreateForm.as_view(), + name="create-stage-task", + ), + path("update-task//", tasks.TaskCreateForm.as_view(), name="update-task"), + # path("update-task//", views.update_task, name="update-task"), + path("delete-task//", views.delete_task, name="delete-task"), + path("task-details//", views.task_details, name="task-details"), + path("task-filter//", views.task_filter, name="task-filter"), + path("task-stage-change", views.task_stage_change, name="task-stage-change"), + # path("task-timesheet//", views.task_timesheet, name="task-timesheet"), + path( + "task-timesheet//", + timesheet.TaskTimeSheet.as_view(), + name="task-timesheet", + ), + # path( + # "create-timesheet-task//", + # views.create_timesheet_task, + # name="create-timesheet-task", + # ), + path( + "update-timesheet-task//", + views.update_timesheet_task, + name="update-timesheet-task", + ), + path("drag-and-drop-task", views.drag_and_drop_task, name="drag-and-drop-task"), # Task-all - path('task-all',views.task_all,name='task-all'), - path('create-task-all',views.task_all_create,name='create-task-all'), - path('update-task-all//',views.update_task_all,name='update-task-all'), - path('task-all-filter/',views.task_all_filter,name='task-all-filter'), - path("task-all-bulk-archive",views.task_all_bulk_archive,name="task-all-bulk-archive"), - path("task-all-bulk-delete",views.task_all_bulk_delete,name="task-all-bulk-delete"), - path('task-all-archive//',views.task_all_archive,name='task-all-archive'), - - - + path("task-all/", tasks.TasksTemplateView.as_view(), name="task-all"), + path("tasks-list-view/", tasks.TaskListView.as_view(), name="tasks-list-view"), + path( + "tasks-list-individual-view/", + tasks.TasksInIndividualView.as_view(), + name="tasks-list-individual-view", + ), + path("tasks-card-view/", tasks.TaskCardView.as_view(), name="tasks-card-view"), + path("tasks-navbar/", tasks.TasksNavBar.as_view(), name="tasks-navbar"), + path("create-task-all/", tasks.TaskCreateForm.as_view(), name="create-task-all"), + path( + "update-task-all//", + tasks.TaskCreateForm.as_view(), + name="update-task-all", + ), + path( + "task-detail-view//", + tasks.TaskDetailView.as_view(), + name="task-detail-view", + ), + path( + "update-project-task-status//", + views.update_project_task_status, + name="update-project-task-status", + ), + # path("task-all", views.task_all, name="task-all"), + # path("create-task-all", views.task_all_create, name="create-task-all"), + # path( + # "update-task-all//", views.update_task_all, name="update-task-all" + # ), + path("task-all-filter/", views.task_all_filter, name="task-all-filter"), + path( + "task-all-bulk-archive", + views.task_all_bulk_archive, + name="task-all-bulk-archive", + ), + path( + "task-all-bulk-delete", views.task_all_bulk_delete, name="task-all-bulk-delete" + ), + path( + "task-all-archive//", + views.task_all_archive, + name="task-all-archive", + ), # Project stage - path('create-project-stage//',views.create_project_stage,name='create-project-stage'), - path('update-project-stage//',views.update_project_stage,name='update-project-stage'), - path('delete-project-stage//',views.delete_project_stage,name='delete-project-stage'), - path('get-stages',views.get_stages,name="get-stages"), - path('create-stage-taskall',views.create_stage_taskall,name='create-stage-taskall'), - path('drag-and-drop-stage',views.drag_and_drop_stage,name='drag-and-drop-stage'), - - - # Timesheet - path("view-time-sheet", views.time_sheet_view, name="view-time-sheet"), - path("create-time-sheet", views.time_sheet_creation, name="create-time-sheet"), path( - "update-time-sheet//", - views.time_sheet_update, - name="update-time-sheet", + "create-project-stage//", + project_stage.ProjectStageCreateForm.as_view(), + name="create-project-stage", + ), + # path( + # "create-project-stage//", + # views.create_project_stage, + # name="create-project-stage", + # ), + path( + "update-project-stage//", + project_stage.ProjectStageCreateForm.as_view(), + name="update-project-stage", + ), + # path( + # "update-project-stage//", + # views.update_project_stage, + # name="update-project-stage", + # ), + path( + "delete-project-stage//", + views.delete_project_stage, + name="delete-project-stage", + ), + path("get-stages", views.get_stages, name="get-stages"), + path( + "create-stage-taskall", views.create_stage_taskall, name="create-stage-taskall" + ), + path("drag-and-drop-stage", views.drag_and_drop_stage, name="drag-and-drop-stage"), + # Timesheet + # path("view-time-sheet", views.time_sheet_view, name="view-time-sheet"), + path("view-time-sheet/", timesheet.TimeSheetView.as_view(), name="view-time-sheet"), + path("get-members-of-project/", views.get_members, name="get-members-of-project"), + path( + "get-tasks-of-project/", + views.get_tasks_in_timesheet, + name="get-tasks-of-project", ), path( - "delete-time-sheet-ajax//", - views.time_sheet_delete_ajax, - name="delete-time-sheet-ajax", + "time-sheet-nav/", timesheet.TimeSheetNavView.as_view(), name="time-sheet-nav" ), + path("time-sheet-list/", timesheet.TimeSheetList.as_view(), name="time-sheet-list"), + path( + "time-sheet-card/", + timesheet.TimeSheetCardView.as_view(), + name="time-sheet-card", + ), + path( + "time-sheet-detail-view//", + timesheet.TimeSheetDetailView.as_view(), + name="time-sheet-detail-view", + ), + # path("create-time-sheet", views.time_sheet_creation, name="create-time-sheet"), + path( + "create-time-sheet", + timesheet.TimeSheetFormView.as_view(), + name="create-time-sheet", + ), + path( + "create-timesheet-task//", + timesheet.TimeSheetFormView.as_view(), + name="create-timesheet-task", + ), + path( + "update-time-sheet//", + timesheet.TimeSheetFormView.as_view(), + name="update-time-sheet", + ), + # path( + # "update-time-sheet//", + # views.time_sheet_update, + # name="update-time-sheet", + # ), path("filter-time-sheet", views.time_sheet_filter, name="filter-time-sheet"), path("time-sheet-initial", views.time_sheet_initial, name="time-sheet-initial"), - - path("view-time-sheet", views.time_sheet_view, name="view-time-sheet"), + # path("get-project", views.get_project, name="get-project"), path("create-time-sheet", views.time_sheet_creation, name="create-time-sheet"), path( "create-project-time-sheet", @@ -92,11 +258,6 @@ views.time_sheet_task_creation, name="create-task-time-sheet", ), - path( - "update-time-sheet//", - views.time_sheet_update, - name="update-time-sheet", - ), path( "delete-time-sheet//", views.time_sheet_delete, @@ -110,8 +271,14 @@ name="personal-time-sheet-view", ), path("personal-time-sheet/", views.personal_time_sheet, name="personal-time-sheet"), - path("view-single-time-sheet/", views.time_sheet_single_view, name="view-single-time-sheet"), - path('time-sheet-bulk-delete',views.time_sheet_bulk_delete,name="time-sheet-bulk-delete"), - - + path( + "view-single-time-sheet/", + views.time_sheet_single_view, + name="view-single-time-sheet", + ), + path( + "time-sheet-bulk-delete", + views.time_sheet_bulk_delete, + name="time-sheet-bulk-delete", + ), ] diff --git a/project/views.py b/project/views.py index e23679c03..10496b681 100644 --- a/project/views.py +++ b/project/views.py @@ -1,37 +1,54 @@ import calendar -from collections import defaultdict import datetime -from urllib.parse import parse_qs -from django.shortcuts import render, redirect -from django.http import HttpResponse,JsonResponse,HttpResponseRedirect -from django.urls import reverse +import json +import logging +from collections import defaultdict +from urllib.parse import parse_qs, urlparse + import pandas as pd -from horilla.decorators import login_required, permission_required -from django.utils.translation import gettext_lazy as _ -from django.template.loader import render_to_string +import xlsxwriter from django.contrib import messages -from .forms import * -from .models import * -from .decorator import * -from .methods import is_projectmanager_or_member_or_perms, is_task_manager,is_task_member -from .filters import TimeSheetFilter, ProjectFilter, TaskFilter,TaskAllFilter +from django.core import serializers from django.core.exceptions import ValidationError -import json from django.core.paginator import Paginator -import calendar -import datetime -from django.core import serializers -import json -import xlsxwriter -from horilla.decorators import hx_request_required +from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse +from django.shortcuts import get_object_or_404, redirect, render +from django.template.loader import render_to_string +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ -from project.methods import strtime_seconds , paginator_qry, generate_colors, time_sheet_delete_permissions, time_sheet_update_permissions from base.methods import filtersubordinates, get_key_instances +from horilla.decorators import hx_request_required, login_required, permission_required +from notifications.signals import notify +from project.cbv.projects import DynamicProjectCreationFormView +from project.cbv.tasks import DynamicTaskCreateFormView +from project.cbv.timesheet import TimeSheetFormView +from project.methods import ( + generate_colors, + paginator_qry, + strtime_seconds, + time_sheet_delete_permissions, + time_sheet_update_permissions, +) + +from .decorator import * +from .filters import ProjectFilter, TaskAllFilter, TaskFilter, TimeSheetFilter +from .forms import * +from .methods import ( + is_project_manager_or_super_user, + is_projectmanager_or_member_or_perms, + is_task_manager, + is_task_member, + you_dont_have_permission, +) +from .models import * +logger = logging.getLogger(__name__) # Create your views here. # Dash board view + @login_required def dashboard_view(request): """ @@ -39,7 +56,7 @@ def dashboard_view(request): Returns: it will redirect to dashboard. """ - + # Get the current date today = datetime.date.today() # Find the last day of the current month @@ -47,23 +64,24 @@ def dashboard_view(request): # Construct the last date of the current month last_date = datetime.date(today.year, today.month, last_day) - total_projects= Project.objects.all().count() - new_projects= Project.objects.filter(status='new').count() - projects_in_progress= Project.objects.filter(status='in_progress').count() - date_range = {'end_till':last_date} + total_projects = Project.objects.all().count() + new_projects = Project.objects.filter(status="new").count() + projects_in_progress = Project.objects.filter(status="in_progress").count() + date_range = {"end_till": last_date} projects_due_in_this_month = ProjectFilter(date_range).qs - unexpired_project =[] + unexpired_project = [] for project in projects_due_in_this_month: - if project.status != 'expired': + if project.status != "expired": unexpired_project.append(project) context = { - "total_projects":total_projects, - "new_projects":new_projects, - "projects_in_progress":projects_in_progress, - 'unexpired_project':unexpired_project + "total_projects": total_projects, + "new_projects": new_projects, + "projects_in_progress": projects_in_progress, + "unexpired_project": unexpired_project, } - return render(request,'dashboard/project_dashboard.html',context=context) + return render(request, "dashboard/project_dashboard.html", context=context) + @login_required def project_status_chart(request): @@ -81,12 +99,12 @@ def project_status_chart(request): "data": [], } ) - + for status in choices: count = Project.objects.filter(status=status[0]).count() data = [] for index, label in enumerate(initial_data): - if status[1] == initial_data[index]['label']: + if status[1] == initial_data[index]["label"]: data.append(count) else: data.append(0) @@ -96,7 +114,7 @@ def project_status_chart(request): "data": data, } ) - return JsonResponse({"dataSet":data_set, "labels": labels}) + return JsonResponse({"dataSet": data_set, "labels": labels}) @login_required @@ -118,12 +136,12 @@ def task_status_chart(request): ) # for status in choices: # count = Project.objects.filter(status=status[0]).count() - + for status in choices: count = Task.objects.filter(status=status[0]).count() data = [] for index, label in enumerate(initial_data): - if status[1] == initial_data[index]['label']: + if status[1] == initial_data[index]["label"]: data.append(count) else: data.append(0) @@ -133,20 +151,23 @@ def task_status_chart(request): "data": data, } ) - return JsonResponse({"dataSet":data_set, "labels": labels}) + return JsonResponse({"dataSet": data_set, "labels": labels}) + @login_required -def project_detailed_view(request,project_id): +def project_detailed_view(request, project_id): project = Project.objects.get(id=project_id) task_count = project.task_set.count() - context ={ - 'project':project, - "task_count":task_count, + context = { + "project": project, + "task_count": task_count, } - return render(request,'dashboard/project_details.html',context=context) + return render(request, "dashboard/project_details.html", context=context) + # Project views + @login_required @is_projectmanager_or_member_or_perms(perm="project.view_project") def project_view(request): @@ -154,20 +175,21 @@ def project_view(request): Overall view of project, the default view """ form = ProjectFilter() - view_type = 'card' - if request.GET.get('view') == 'list': - view_type = 'list' - projects = Project.objects.all() + view_type = "card" + if request.GET.get("view") == "list": + view_type = "list" + projects = Project.objects.all() if request.GET.get("search") is not None: projects = ProjectFilter(request.GET).qs - previous_data = request.environ['QUERY_STRING'] - page_number = request.GET.get('page') - context = {'view_type':view_type, - 'projects':paginator_qry(projects,page_number), - "pd": previous_data, - "f": form, - } - return render(request,'project/new/overall.html', context) + previous_data = request.environ["QUERY_STRING"] + page_number = request.GET.get("page") + context = { + "view_type": view_type, + "projects": paginator_qry(projects, page_number), + "pd": previous_data, + "f": form, + } + return render(request, "project/new/overall.html", context) @permission_required(perm="project.add_project") @@ -181,14 +203,19 @@ def create_project(request): form = ProjectForm(request.POST, request.FILES) if form.is_valid(): form.save() - messages.success(request,_('New project created')) - response = render(request, - "project/new/forms/project_creation.html", - context={"form": form}, - ) + messages.success(request, _("New project created")) + response = render( + request, + "project/new/forms/project_creation.html", + context={"form": form}, + ) - return HttpResponse(response.content.decode("utf-8") + "") - return render(request, "project/new/forms/project_creation.html", context={"form": form}) + return HttpResponse( + response.content.decode("utf-8") + "" + ) + return render( + request, "project/new/forms/project_creation.html", context={"form": form} + ) @login_required @@ -214,7 +241,9 @@ def project_update(request, project_id): project_form.save() messages.success(request, _("Project updated")) response = render( - request, "project/new/forms/project_update.html", {"form": project_form , 'project_id' : project_id} + request, + "project/new/forms/project_update.html", + {"form": project_form, "project_id": project_id}, ) return HttpResponse( response.content.decode("utf-8") + "" @@ -222,25 +251,76 @@ def project_update(request, project_id): return render( request, "project/new/forms/project_update.html", - { - "form": project_form, 'project_id' : project_id - }, + {"form": project_form, "project_id": project_id}, ) +@login_required +@project_update_permission() +def change_project_status(request, project_id): + """ + HTMX function to update the status of a project. + Args: + - project_id: ID of the Project object. + """ + status = request.POST.get("status") + try: + project = get_object_or_404(Project, id=project_id) + if status: + if project.status != status: + project.status = status + project.save() + messages.success( + request, + _(f"{project} status updated to {project.get_status_display()}."), + ) + # Notify all project managers and members + employees = (project.managers.all() | project.members.all()).distinct() + for employee in employees: + try: + notify.send( + request.user.employee_get, + recipient=employee.employee_user_id, + verb=f"The status of the project '{project}' has been changed to {project.get_status_display()}.", + verb_ar=f"تم تغيير حالة المشروع '{project}' إلى {project.get_status_display()}.", + verb_de=f"Der Status des Projekts '{project}' wurde auf {project.get_status_display()} geändert.", + verb_es=f"El estado del proyecto '{project}' ha sido cambiado a {project.get_status_display()}.", + verb_fr=f"Le statut du projet '{project}' a été changé en {project.get_status_display()}.", + redirect=reverse( + "task-view", + kwargs={"project_id": project.id}, + ), + ) + except Exception as e: + logger.error(e) + else: + messages.info( + request, + _( + f"{project} status is already set to {project.get_status_display()}." + ), + ) + else: + messages.error(request, _("Invalid status or missing data.")) + + except Http404: + messages.error(request, _("The specified project does not exist.")) + return HttpResponse("") + + @login_required @project_delete_permission() def project_delete(request, project_id): """ For deleting existing project """ - view_type = request.GET.get('view') - project_view_url = reverse('project-view') - redirected_url = f'{project_view_url}?view={view_type}' + view_type = request.GET.get("view") + project_view_url = reverse("project-view") + redirected_url = f"{project_view_url}?view={view_type}" Project.objects.get(id=project_id).delete() return redirect(redirected_url) - + @login_required def project_filter(request): @@ -248,22 +328,21 @@ def project_filter(request): For filtering projects """ projects = ProjectFilter(request.GET).qs - templete = 'project/new/project_kanban_view.html' - if request.GET.get('view') == 'list': - templete = 'project/new/project_list_view.html' - previous_data = request.environ['QUERY_STRING'] - page_number = request.GET.get('page') + templete = "project/new/project_kanban_view.html" + if request.GET.get("view") == "list": + templete = "project/new/project_list_view.html" + previous_data = request.environ["QUERY_STRING"] + page_number = request.GET.get("page") filter_obj = projects data_dict = parse_qs(previous_data) get_key_instances(Project, data_dict) context = { - 'projects':paginator_qry(projects,page_number), - "pd": previous_data, - "f": filter_obj, - "filter_dict": data_dict, - - } - return render(request, templete,context) + "projects": paginator_qry(projects, page_number), + "pd": previous_data, + "f": filter_obj, + "filter_dict": data_dict, + } + return render(request, templete, context) def convert_nan(field, dicts): @@ -276,7 +355,7 @@ def convert_nan(field, dicts): return None except ValueError: return field_value - + @login_required def project_import(request): @@ -284,7 +363,15 @@ def project_import(request): This method is used to import Project instances and creates related objects """ data_frame = pd.DataFrame( - columns=["Title", "Manager Badge id","Member Badge id","Status", "Start Date", "End Date", "Description"] + columns=[ + "Title", + "Manager Badge id", + "Member Badge id", + "Status", + "Start Date", + "End Date", + "Description", + ] ) # Export the DataFrame to an Excel file response = HttpResponse(content_type="application/ms-excel") @@ -299,95 +386,129 @@ def project_import(request): for project in project_dicts: try: # getting datas from imported file - title = project['Title'] + title = project["Title"] manager_badge_id = convert_nan("Manager Badge id", project) member_badge_id = convert_nan("Member Badge id", project) - status = project['Status'] + status = project["Status"] start_date = project["Start Date"] end_date = project["End Date"] - description = project['Description'] + description = project["Description"] # checcking all the imported values is_save = True # getting employee using badge id, for manager - if manager_badge_id : - if Employee.objects.filter(badge_id = manager_badge_id).exists(): - manager = Employee.objects.filter(badge_id = manager_badge_id).first() - else: - project ["Manager error"] = f"{manager_badge_id} - This badge not exist" - is_save = False + if manager_badge_id: + ids = manager_badge_id.split(",") + error_ids = [] + managers = [] + for id in ids: + if Employee.objects.filter(badge_id=id).exists(): + employee = Employee.objects.filter(badge_id=id).first() + managers.append(employee) + else: + error_ids.append(id) + is_save = False + if error_ids: + ids = ",".join(map(str, error_ids)) + project["Manager error"] = f"{ids} - This id not exists" + # if Employee.objects.filter(badge_id=manager_badge_id).exists(): + # manager = Employee.objects.filter( + # badge_id=manager_badge_id + # ).first() + # else: + # project["Manager error"] = ( + # f"{manager_badge_id} - This badge not exist" + # ) + # is_save = False # getting employee using badge id, for member if member_badge_id: - ids = member_badge_id.split(',') + ids = member_badge_id.split(",") error_ids = [] employees = [] for id in ids: - if Employee.objects.filter(badge_id = id).exists(): - employee = Employee.objects.filter(badge_id = id).first() + if Employee.objects.filter(badge_id=id).exists(): + employee = Employee.objects.filter(badge_id=id).first() employees.append(employee) else: error_ids.append(id) is_save = False if error_ids: - ids = ','.join(map(str, error_ids)) - project ["Member error"] = f"{ids} - This id not exists" + ids = ",".join(map(str, error_ids)) + project["Member error"] = f"{ids} - This id not exists" - if status: + if status: if status not in [stat for stat, _ in Project.PROJECT_STATUS]: - project ["Status error"] = f'{status} not available in Project status' + project["Status error"] = ( + f"{status} not available in Project status" + ) is_save = False else: - project ["Status error"] = 'Status is a required field' + project["Status error"] = "Status is a required field" is_save = False - format = '%Y-%m-%d' + format = "%Y-%m-%d" if start_date: - + # using try-except to check for truth value try: - res = bool(datetime.datetime.strptime(start_date.strftime("%Y-%m-%d"), format)) + res = bool( + datetime.datetime.strptime( + start_date.strftime("%Y-%m-%d"), format + ) + ) except Exception as e: - res = False - if res == False : - project["Start date error"] = "Date must be in 'YYYY-MM-DD' format" + res = False + if res == False: + project["Start date error"] = ( + "Date must be in 'YYYY-MM-DD' format" + ) is_save = False else: project["Start date error"] = "Start date is a required field" is_save = False - - + if end_date: # using try-except to check for truth value try: - res = bool(datetime.datetime.strptime(end_date.strftime("%Y-%m-%d"), format)) + res = bool( + datetime.datetime.strptime( + end_date.strftime("%Y-%m-%d"), format + ) + ) if end_date < start_date: - project["end date error"] = "End date must be greater than Start date" + project["end date error"] = ( + "End date must be greater than Start date" + ) is_save = False except ValueError: - res = False - if res == False : - project["end date error"] = "Date must be in 'YYYY-MM-DD' format" + res = False + if res == False: + project["end date error"] = ( + "Date must be in 'YYYY-MM-DD' format" + ) is_save = False - if is_save == True : + if is_save == True: # creating new project if Project.objects.filter(title=title).exists(): - project_obj = Project.objects.filter(title=title).first() + project_obj = Project.objects.filter(title=title).first() else: project_obj = Project(title=title) project_obj.start_date = start_date.strftime("%Y-%m-%d") project_obj.end_date = end_date.strftime("%Y-%m-%d") - project_obj.manager=manager project_obj.status = status project_obj.description = description project_obj.save() + for manager in managers: + project_obj.managers.add(manager) + project_obj.save() for member in employees: project_obj.members.add(member) project_obj.save() else: error_lists.append(project) - + except Exception as e: error_lists.append(project) if error_lists: @@ -405,7 +526,6 @@ def project_import(request): return response - @login_required # @permission_required("employee.delete_employee") # @require_http_methods(["POST"]) @@ -415,21 +535,29 @@ def project_bulk_export(request): """ ids = request.POST["ids"] ids = json.loads(ids) - data_list=[] + data_list = [] # Add headers to the worksheet - headers = ["Title", "Manager","Members", "Status", "Start Date", "End Date", "Description"] - + headers = [ + "Title", + "Managers", + "Members", + "Status", + "Start Date", + "End Date", + "Description", + ] + # Get the list of field names for your model for project_id in ids: project = Project.objects.get(id=project_id) data = { - "Title":f"{project.title}", - "Manager":f"{project.manager.employee_first_name + ' ' + project.manager.employee_last_name if project.manager else ''}", - "Members":f"{',' .join([member.employee_first_name + ' ' + member.employee_last_name for member in project.members.all()]) if project.members.exists() else ''}", - "Status":f'{project.status}', - "Start Date":f'{project.start_date.strftime("%Y-%m-%d")}', - "End Date":f'{project.end_date.strftime("%Y-%m-%d") if project.end_date else ""}', - "Description":f'{project.description}', + "Title": f"{project.title}", + "Managers": f"{',' .join([manager.employee_first_name + ' ' + manager.employee_last_name for manager in project.managers.all()]) if project.managers.exists() else ''}", + "Members": f"{',' .join([member.employee_first_name + ' ' + member.employee_last_name for member in project.members.all()]) if project.members.exists() else ''}", + "Status": f"{project.status}", + "Start Date": f'{project.start_date.strftime("%Y-%m-%d")}', + "End Date": f'{project.end_date.strftime("%Y-%m-%d") if project.end_date else ""}', + "Description": f"{project.description}", } data_list.append(data) data_frame = pd.DataFrame(data_list, columns=headers) @@ -442,13 +570,14 @@ def project_bulk_export(request): writer, sheet_name="Project details", index=False, - startrow=3 , + startrow=3, ) workbook = writer.book worksheet = writer.sheets["Project details"] max_columns = len(data) heading_format = workbook.add_format( - { "bg_color": "#ffd0cc", + { + "bg_color": "#ffd0cc", "bold": True, "font_size": 14, "align": "center", @@ -456,14 +585,16 @@ def project_bulk_export(request): "font_size": 20, } ) - header_format = workbook.add_format({ - "bg_color": "#EDF1FF", - "bold": True, - "text_wrap": True, - "font_size": 12, - "align": "center", - "border": 1 , - }) + header_format = workbook.add_format( + { + "bg_color": "#EDF1FF", + "bold": True, + "text_wrap": True, + "font_size": 12, + "align": "center", + "border": 1, + } + ) worksheet.set_row(0, 30) worksheet.merge_range( 0, @@ -483,31 +614,30 @@ def project_bulk_export(request): writer.close() - return response + @login_required -# @permission_required("employee.delete_employee") -# @require_http_methods(["POST"]) +# @project_delete_permission def project_bulk_archive(request): """ This method is used to archive bulk of Project instances """ ids = request.POST["ids"] ids = json.loads(ids) - is_active = False - if request.GET.get("is_active") == "True": - is_active = True for project_id in ids: - project = Project.objects.get(id=project_id) - project.is_active = is_active - project.save() - message = _("archived") - if is_active: - message = _("un-archived") - messages.success(request, f"{project} is {message}") + project = Project.objects.filter(id=project_id).first() + if project and is_project_manager_or_super_user(request, project): + is_active = eval(request.GET.get("is_active")) + message = "Archived" + if is_active: + message = "Un-Archived" + project.is_active = is_active + project.save() + messages.success(request, f"{project} is {message} successfully.") return JsonResponse({"message": "Success"}) + @login_required # @permission_required("employee.delete_employee") def project_bulk_delete(request): @@ -516,24 +646,25 @@ def project_bulk_delete(request): """ ids = request.POST["ids"] ids = json.loads(ids) + del_id = [] for project_id in ids: project = Project.objects.get(id=project_id) try: - project.delete() - messages.success( - request, _("%(project)s deleted.") % {"project": project} - ) + if is_project_manager_or_super_user(request, project): + project.delete() + del_id.append(project) except Exception as error: messages.error(request, error) messages.error( request, _("You cannot delete %(project)s.") % {"project": project} ) - + messages.success(request, _("{} Projects deleted".format(len(del_id)))) return JsonResponse({"message": "Success"}) + @login_required -# @permission_required("employee.delete_employee") -def project_archive(request,project_id): +@project_delete_permission() +def project_archive(request, project_id): """ This method is used to archive project instance Args: @@ -542,68 +673,119 @@ def project_archive(request,project_id): project = Project.objects.get(id=project_id) project.is_active = not project.is_active project.save() - message = _(f"{project} un-archived") + message = _(f"{project} Un-Archived successfully.") if not project.is_active: - message = _(f"{project} archived") + message = _(f"{project} Archived successfully.") messages.success(request, message) return HttpResponseRedirect(request.META.get("HTTP_REFERER")) # Task views + @login_required @project_update_permission() -def task_view(request,project_id,**kwargs): +def task_view(request, project_id, **kwargs): """ For showing tasks """ form = TaskAllFilter() - view_type = 'card' + view_type = "card" project = Project.objects.get(id=project_id) - stages = ProjectStage.objects.filter(project = project).order_by('sequence') + stages = ProjectStage.objects.filter(project=project).order_by("sequence") tasks = Task.objects.filter(project=project) - if request.GET.get('view') == 'list': - view_type = 'list' - context = {'view_type':view_type, - 'tasks':tasks, - 'stages':stages, - 'project_id':project_id, - "project":project, - "f":form, - } - return render(request,'task/new/overall.html',context) + form.form.fields["stage"].queryset = ProjectStage.objects.filter(project=project.id) + if request.GET.get("view") == "list": + view_type = "list" + context = { + "view_type": view_type, + "tasks": tasks, + "stages": stages, + "project_id": project_id, + "project": project, + "today": datetime.datetime.today().date(), + "f": form, + } + return render(request, "task/new/overall.html", context) + + +@login_required +@hx_request_required +def quick_create_task(request, stage_id): + project_stage = ProjectStage.objects.get(id=stage_id) + hx_target = request.META.get("HTTP_HX_TARGET") + if ( + request.user.employee_get in project_stage.project.managers.all() + or request.user.has_perm("project.add_task") + ): + form = QuickTaskForm( + initial={ + "stage": project_stage, + "project": project_stage.project, + "end_date": project_stage.project.end_date, + } + ) + if request.method == "POST": + form = QuickTaskForm(request.POST) + if form.is_valid(): + form.save() + messages.success(request, _("The task has been created successfully!")) + return HttpResponse( + f"" + ) + return render( + request, + "task/new/forms/quick_create_task_form.html", + context={ + "form": form, + "stage_id": stage_id, + "project_id": project_stage.project.id, + "hx_target": hx_target, + }, + ) + messages.info(request, "You dont have permission.") + return HttpResponse("") + @login_required -def create_task(request,stage_id): +def create_task(request, stage_id): """ For creating new task in project view """ - project_stage = ProjectStage.objects.get(id = stage_id) + project_stage = ProjectStage.objects.get(id=stage_id) project = project_stage.project - if ( - request.user.employee_get == project.manager or - request.user.has_perm('project.delete_project') - ): - form = TaskForm(initial={'project': project}) - if request.method == 'POST': + if request.user.employee_get in project.managers.all() or request.user.has_perm( + "project.delete_project" + ): + form = TaskForm(initial={"project": project}) + if request.method == "POST": form = TaskForm(request.POST, request.FILES) if form.is_valid(): instance = form.save(commit=False) instance.stage = project_stage instance.save() - messages.success(request,_('New task created')) - response = render(request, - "task/new/forms/create_task.html", - context={"form": form,'stage_id':stage_id}, - ) - return HttpResponse(response.content.decode("utf-8") + "") - return render(request, "task/new/forms/create_task.html", context={"form": form, 'stage_id':stage_id}) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) + messages.success(request, _("New task created")) + response = render( + request, + "task/new/forms/create_task.html", + context={"form": form, "stage_id": stage_id}, + ) + return HttpResponse( + response.content.decode("utf-8") + + "" + ) + return render( + request, + "task/new/forms/create_task.html", + context={"form": form, "stage_id": stage_id}, + ) + messages.info(request, "You dont have permission.") + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) + @login_required -def create_task_in_project(request,project_id): +def create_task_in_project(request, project_id): """ For creating new task in project view """ @@ -612,30 +794,35 @@ def create_task_in_project(request,project_id): # Serialize the queryset to JSON - serialized_data = serializers.serialize('json', stages) - if ( - request.user.employee_get == project.manager or - request.user.has_perm('project.delete_project') - ): - form = TaskFormCreate(initial={'project': project}) - if request.method == 'POST': + serialized_data = serializers.serialize("json", stages) + if request.user.employee_get in project.managers.all() or request.user.has_perm( + "project.delete_project" + ): + form = TaskFormCreate(initial={"project": project}) + if request.method == "POST": form = TaskFormCreate(request.POST, request.FILES) if form.is_valid(): form.save() - messages.success(request,_('New task created')) - response = render(request, - "task/new/forms/create_task_project.html", - context={"form": form,'project_id':project_id}, - ) - return HttpResponse(response.content.decode("utf-8") + "") - context ={"form": form, - 'project_id':project_id, - 'stages':serialized_data, + messages.success(request, _("New task created")) + response = render( + request, + "task/new/forms/create_task_project.html", + context={"form": form, "project_id": project_id}, + ) + return HttpResponse( + response.content.decode("utf-8") + + "" + ) + context = { + "form": form, + "project_id": project_id, + "stages": serialized_data, } - return render(request, "task/new/forms/create_task_project.html", context=context) - messages.info(request,'You dont have permission.') - return HttpResponseRedirect(request. META. get('HTTP_REFERER', '/')) - + return render( + request, "task/new/forms/create_task_project.html", context=context + ) + messages.info(request, "You dont have permission.") + return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) @login_required @@ -644,7 +831,7 @@ def update_task(request, task_id): """ For updating task in project view """ - + task = Task.objects.get(id=task_id) project = task.project task_form = TaskForm(instance=task) @@ -653,187 +840,236 @@ def update_task(request, task_id): if task_form.is_valid(): task_form.save() messages.success(request, _("Task updated")) - response = render(request, "task/new/forms/update_task.html", {"form": task_form, 'task_id':task_id}) + response = render( + request, + "task/new/forms/update_task.html", + {"form": task_form, "task_id": task_id}, + ) return HttpResponse( response.content.decode("utf-8") + "" - ) + ) return render( request, "task/new/forms/update_task.html", { "form": task_form, - 'task_id': task_id, + "task_id": task_id, }, ) - - + + @login_required -@task_update_permission() +@task_delete_permission() def delete_task(request, task_id): """ - For delete task + For delete task """ + view_type = request.GET.get("view") + path = urlparse(request.META["HTTP_REFERER"]).path + url_after_project = path.split("project/")[1].rstrip("/") + # Split into components + parts = url_after_project.split("/") + view_name = parts[0] + object_id = parts[1] if len(parts) > 1 else None + + if not view_name == "task-all": + task_view_url = reverse(view_name, kwargs={"project_id": object_id}) + else: + task_view_url = reverse("task-all") + redirected_url = f"{task_view_url}?view={view_type}" task = Task.objects.get(id=task_id) project_id = task.project.id task.delete() - view_type = request.GET.get('view') - # Build the URL for task_view with query parameters - task_view_url = reverse('task-view', args=[project_id]) - if request.GET.get("task_all"): - task_view_url = reverse('task-all') - - redirected_url = f'{task_view_url}?view={view_type}' - + messages.success(request, _("The task has been deleted successfully.")) + if request.META.get("HTTP_HX_REQUEST"): + return HttpResponse( + f"" + ) return redirect(redirected_url) + @login_required -def task_details(request,task_id): +def task_details(request, task_id): """ For showing all details about task """ task = Task.objects.get(id=task_id) - return render(request,'task/new/task_details.html',context={'task':task}) + return render(request, "task/new/task_details.html", context={"task": task}) + @login_required @project_update_permission() def task_filter(request, project_id): """ - For filtering task - """ - view_type = 'card' - templete = 'task/new/task_kanban_view.html' - if request.GET.get('view') == 'list': - view_type = 'list' - templete ='task/new/task_list_view.html' - tasks = TaskFilter(request.GET).qs - stages = ProjectStage.objects.filter(project = project_id).order_by('sequence') - previous_data = request.environ['QUERY_STRING'] + For filtering task + """ + templete = "task/new/task_kanban_view.html" + if request.GET.get("view") == "list": + templete = "task/new/task_list_view.html" + tasks = TaskFilter(request.GET).qs.filter(project_id=project_id) + stages = ( + ProjectStage.objects.filter(project_id=project_id).order_by("sequence") + # if len(request.GET) == 0 or len(request.GET) == 1 + # else ProjectStage.objects.filter(tasks__in=tasks) + # .distinct() + # .order_by("sequence") + ) + previous_data = request.environ["QUERY_STRING"] data_dict = parse_qs(previous_data) get_key_instances(Task, data_dict) + if data_dict.get("project"): + del data_dict["project"] + context = { + "tasks": tasks.distinct(), + "stages": stages, + "pd": request.GET.urlencode(), + "project_id": project_id, + "filter_dict": data_dict, + } + return render(request, templete, context) - context = {"tasks": tasks, - 'stages':stages, - 'view_type':view_type, - 'project_id':project_id, - "filter_dict": data_dict, - } - return render(request,templete,context) @login_required def task_stage_change(request): """ This method is used to change the current stage of a task """ - task_id = request.POST['task'] - stage_id = request.POST['stage'] + task_id = request.POST["task"] + stage_id = request.POST["stage"] stage = ProjectStage.objects.get(id=stage_id) Task.objects.filter(id=task_id).update(stage=stage) - return JsonResponse({ - 'type': 'success', - 'message': _('Task stage updated'), - }) + return JsonResponse( + { + "type": "success", + "message": _("Task stage updated"), + } + ) @login_required -def task_timesheet(request,task_id): +def task_timesheet(request, task_id): """ - For showing all timesheet related to task + For showing all timesheet related to task """ task = Task.objects.get(id=task_id) - time_sheets=task.task_timesheet.all() - # time_sheets = [] - context={'time_sheets':time_sheets, - 'task_id':task_id} - return render(request, "task/new/task_timesheet.html",context=context,) + time_sheets = task.task_timesheet.all() + context = {"time_sheets": time_sheets, "task_id": task_id} + return render( + request, + "task/new/task_timesheet.html", + context=context, + ) + @login_required -def create_timesheet_task(request,task_id): - task=Task.objects.get(id=task_id) +def create_timesheet_task(request, task_id): + task = Task.objects.get(id=task_id) project = task.project - form = TimesheetInTaskForm(initial={'project_id':project,'task_id':task}) - if request.method == 'POST': + form = TimesheetInTaskForm(initial={"project_id": project, "task_id": task}) + if request.method == "POST": form = TimesheetInTaskForm(request.POST) if form.is_valid(): form.save() messages.success(request, _("Timesheet created")) - response = render(request, "task/new/forms/create_timesheet.html", {"form": form,'task_id':task_id}) + response = render( + request, + "task/new/forms/create_timesheet.html", + {"form": form, "task_id": task_id}, + ) return HttpResponse( response.content.decode("utf-8") + "" - ) - context = {'form':form, - 'task_id':task_id, - } - return render(request,'task/new/forms/create_timesheet.html',context=context) + ) + context = { + "form": form, + "task_id": task_id, + } + return render(request, "task/new/forms/create_timesheet.html", context=context) + @login_required -def update_timesheet_task(request,timesheet_id): +def update_timesheet_task(request, timesheet_id): timesheet = TimeSheet.objects.get(id=timesheet_id) - form = TimesheetInTaskForm(instance = timesheet) - if request.method == 'POST': - form = TimesheetInTaskForm(request.POST,instance=timesheet) + form = TimesheetInTaskForm(instance=timesheet) + if request.method == "POST": + form = TimesheetInTaskForm(request.POST, instance=timesheet) if form.is_valid(): form.save() messages.success(request, _("Timesheet updated")) - response = render(request, "task/new/forms/update_timesheet.html", {"form": form,'timesheet_id':timesheet_id}) + response = render( + request, + "task/new/forms/update_timesheet.html", + {"form": form, "timesheet_id": timesheet_id}, + ) return HttpResponse( response.content.decode("utf-8") + "" - ) - context = {'form':form, - 'timesheet_id':timesheet_id, - } - return render(request,'task/new/forms/update_timesheet.html',context=context) + ) + context = { + "form": form, + "timesheet_id": timesheet_id, + } + return render(request, "task/new/forms/update_timesheet.html", context=context) + @login_required def drag_and_drop_task(request): """ - For drag and drop task into new stage + For drag and drop task into new stage """ - updated_stage_id = request.POST['updated_stage_id'] - previous_task_id = request.POST['previous_task_id'] - previous_stage_id = request.POST['previous_stage_id'] + updated_stage_id = request.POST["updated_stage_id"] + previous_task_id = request.POST["previous_task_id"] + previous_stage_id = request.POST["previous_stage_id"] change = False task = Task.objects.get(id=previous_task_id) - project=task.project - if (request.user.has_perm('project.change_task') or - request.user.has_perm('project.change_project') or - request.user.employee_get == task.task_manager or - request.user.employee_get in task.task_members.all() or - request.user.employee_get == project.manager or - request.user.employee_get in project.members.all() - ): + project = task.project + if ( + request.user.has_perm("project.change_task") + or request.user.has_perm("project.change_project") + or request.user.employee_get in task.task_managers.all() + or request.user.employee_get in task.task_members.all() + or request.user.employee_get in project.managers.all() + or request.user.employee_get in project.members.all() + ): if previous_stage_id != updated_stage_id: - task.stage=ProjectStage.objects.get(id = updated_stage_id) + task.stage = ProjectStage.objects.get(id=updated_stage_id) task.save() change = True - sequence = json.loads(request.POST['sequence']) + sequence = json.loads(request.POST["sequence"]) for key, val in sequence.items(): if Task.objects.get(id=key).sequence != val: Task.objects.filter(id=key).update(sequence=val) change = True - return JsonResponse({'type': 'success','message':_('Task stage updated'),'change':change}) + message = ( + _("Task stage has been successfully updated.") + if previous_stage_id != updated_stage_id + else _("Tasks order has been successfully updated.") + ) + messages.success(request, message) + return JsonResponse({"change": change}) change = True - return JsonResponse({'type': 'info','message':_('You dont have permission.'),'change':change}) + messages.info(request, _("You dont have permission.")) + return JsonResponse({"change": change}) # Task all views + @login_required def task_all(request): """ For showing all task """ form = TaskAllFilter() - view_type='card' - tasks= TaskAllFilter(request.GET).qs - if request.GET.get('view') == 'list': - view_type = 'list' + view_type = "card" + tasks = TaskAllFilter(request.GET).qs + if request.GET.get("view") == "list": + view_type = "list" context = { - "tasks":paginator_qry(tasks,request.GET.get('page')), - "pd":request.GET.urlencode(), - "f":form, - "view_type":view_type, + "tasks": paginator_qry(tasks, request.GET.get("page")), + "pd": request.GET.urlencode(), + "f": form, + "view_type": view_type, } - return render(request,'task_all/task_all_overall.html',context=context) + return render(request, "task_all/task_all_overall.html", context=context) @login_required @@ -842,36 +1078,64 @@ def task_all_create(request): For creating new task in task all view """ form = TaskAllForm() - if request.method == 'POST': + if request.method == "POST": form = TaskAllForm(request.POST, request.FILES) if form.is_valid(): - task = form.save(commit=False) - task.save() - messages.success(request,_('New task created')) - response = render(request, - "task_all/forms/create_taskall.html", - context={"form": form,}, - ) - return HttpResponse(response.content.decode("utf-8") + "") - return render(request, "task_all/forms/create_taskall.html", context={"form": form,}) + form.save() + messages.success(request, _("New task created")) + response = render( + request, + "task_all/forms/create_taskall.html", + context={ + "form": form, + }, + ) + return HttpResponse( + response.content.decode("utf-8") + "" + ) + return render( + request, + "task_all/forms/create_taskall.html", + context={ + "form": form, + }, + ) + @login_required -def update_task_all(request,task_id): +def update_project_task_status(request, task_id): + status = request.GET.get("status") + + task = get_object_or_404(Task, id=task_id) + task.status = status + task.save() + + messages.success(request, _("Task status has been updated successfully")) + return HttpResponse("") + + +@login_required +def update_task_all(request, task_id): task = Task.objects.get(id=task_id) form = TaskAllForm(instance=task) - if request.method == 'POST': - form = TaskAllForm(request.POST,request.FILES,instance=task) + if request.method == "POST": + form = TaskAllForm(request.POST, request.FILES, instance=task) if form.is_valid(): - form.save() - messages.success(request,_('Task updated successfully')) + task = form.save() + messages.success(request, _("Task updated successfully")) response = render( request, "task_all/forms/update_taskall.html", - context={"form":form, - 'task_id':task_id}, + context={"form": form, "task_id": task_id}, ) - return HttpResponse(response.content.decode("utf-8") + "") - return render(request, "task_all/forms/update_taskall.html", context={"form": form,'task_id':task_id}) + return HttpResponse( + response.content.decode("utf-8") + "" + ) + return render( + request, + "task_all/forms/update_taskall.html", + context={"form": form, "task_id": task_id}, + ) @login_required @@ -879,25 +1143,27 @@ def task_all_filter(request): """ For filtering tasks in task all view """ - view_type = 'card' - templete = 'task_all/task_all_card.html' - if request.GET.get('view') == 'list': - view_type = 'list' - templete ='task_all/task_all_list.html' + view_type = "card" + templete = "task_all/task_all_card.html" + if request.GET.get("view") == "list": + view_type = "list" + templete = "task_all/task_all_list.html" tasks = TaskAllFilter(request.GET).qs - page_number = request.GET.get('page') - previous_data = request.environ['QUERY_STRING'] + page_number = request.GET.get("page") + previous_data = request.environ["QUERY_STRING"] data_dict = parse_qs(previous_data) get_key_instances(Task, data_dict) # tasks = tasks.filter(project_id=project_id) - - context = {"tasks":paginator_qry(tasks,page_number), - 'view_type':view_type, - "pd":previous_data, - "filter_dict": data_dict, - } - return render(request,templete,context) + + context = { + "tasks": paginator_qry(tasks, page_number), + "view_type": view_type, + "pd": previous_data, + "filter_dict": data_dict, + } + return render(request, templete, context) + @login_required # @permission_required("employee.delete_employee") @@ -921,6 +1187,7 @@ def task_all_bulk_archive(request): messages.success(request, f"{task} is {message}") return JsonResponse({"message": "Success"}) + @login_required # @permission_required("employee.delete_employee") def task_all_bulk_delete(request): @@ -929,24 +1196,22 @@ def task_all_bulk_delete(request): """ ids = request.POST["ids"] ids = json.loads(ids) + del_ids = [] for task_id in ids: task = Task.objects.get(id=task_id) try: task.delete() - messages.success( - request, _("%(task)s deleted.") % {"task": task} - ) + del_ids.append(task) except Exception as error: messages.error(request, error) - messages.error( - request, _("You cannot delete %(task)s.") % {"task": task} - ) - + messages.error(request, _("You cannot delete %(task)s.") % {"task": task}) + messages.success(request, _("{} tasks.".format(len(del_ids)))) return JsonResponse({"message": "Success"}) + @login_required # @permission_required("employee.delete_employee") -def task_all_archive(request,task_id): +def task_all_archive(request, task_id): """ This method is used to archive project instance Args: @@ -959,96 +1224,135 @@ def task_all_archive(request,task_id): if not task.is_active: message = _(f"{task} archived") messages.success(request, message) - return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + # return HttpResponse("") + # return HttpResponse("") + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) # Project stage views @login_required @project_delete_permission() @hx_request_required -def create_project_stage(request,project_id): +def create_project_stage(request, project_id): """ For create project stage """ - project = Project.objects.get(id = project_id) - form = ProjectStageForm(initial = {'project': project}) - if request.method == 'POST': - form = ProjectStageForm(request.POST,) - if form.is_valid() : + project = Project.objects.get(id=project_id) + form = ProjectStageForm(initial={"project": project}) + if request.method == "POST": + form = ProjectStageForm( + request.POST, + ) + if form.is_valid(): instance = form.save(commit=False) instance.save() - context = {'form':form, 'project_id':project_id} + context = {"form": form, "project_id": project_id} - messages.success(request,_('New project stage created')) - response = render(request, - 'project_stage/forms/create_project_stage.html', - context, - ) - return HttpResponse(response.content.decode("utf-8")+ "") - context = {'form':form,'project_id':project_id} - return render(request,'project_stage/forms/create_project_stage.html',context) + messages.success(request, _("New project stage created")) + response = render( + request, + "project_stage/forms/create_project_stage.html", + context, + ) + return HttpResponse( + response.content.decode("utf-8") + "" + ) + context = {"form": form, "project_id": project_id} + return render(request, "project_stage/forms/create_project_stage.html", context) @login_required @project_stage_update_permission() -def update_project_stage(request,stage_id): +def update_project_stage(request, stage_id): """ For update project stage """ - stage = ProjectStage.objects.get(id = stage_id) + stage = ProjectStage.objects.get(id=stage_id) form = ProjectStageForm(instance=stage) - if request.method == 'POST': - form =ProjectStageForm(request.POST,instance=stage) + if request.method == "POST": + form = ProjectStageForm(request.POST, instance=stage) if form.is_valid(): form.save() - messages.success(request,_('Project stage updated successfully')) - response = render(request, - "project_stage/forms/update_project_stage.html", - context={'form':form,"stage_id":stage_id}) - return HttpResponse(response.content.decode("utf-8") + "") - return render(request, - "project_stage/forms/update_project_stage.html", - context={'form':form, 'stage_id':stage_id}) + messages.success(request, _("Project stage updated successfully")) + response = render( + request, + "project_stage/forms/update_project_stage.html", + context={"form": form, "stage_id": stage_id}, + ) + return HttpResponse( + response.content.decode("utf-8") + "" + ) + return render( + request, + "project_stage/forms/update_project_stage.html", + context={"form": form, "stage_id": stage_id}, + ) @login_required @project_stage_delete_permission() -def delete_project_stage(request,stage_id): +def delete_project_stage(request, stage_id): """ For delete project stage """ - view_type = request.GET.get('view') + view_type = request.GET.get("view") stage = ProjectStage.objects.get(id=stage_id) + tasks = Task.objects.filter(stage=stage) project_id = stage.project.id - task_view_url = reverse('task-view', args=[project_id]) - redirected_url = f'{task_view_url}?view={view_type}' - - stage.delete() - + if not tasks: + stage.delete() + messages.success(request, _("Stage deleted successfully")) + else: + messages.warning(request, _("Can't Delete. This stage contain some tasks")) + if request.META.get("HTTP_HX_REQUEST"): + return HttpResponse( + f"" + ) + task_view_url = reverse("task-view", args=[project_id]) + redirected_url = f"{task_view_url}?view={view_type}" + return redirect(redirected_url) + @login_required def get_stages(request): """ - This is an ajax method to return json response to take only stages related + This is an ajax method to return json response to take only stages related to the project in the task-all form fields """ - project_id = request.GET["project_id"] - stages = ProjectStage.objects.filter(project=project_id).values("title", "id") - return JsonResponse({"data": list(stages)}) + project_id = request.GET.get("project_id") + form = TaskAllForm() + form.fields["stage"].choices = [] + if project_id: + stages = ProjectStage.objects.filter(project=project_id) + form.fields["stage"].choices = ( + [("", _("Select Stage"))] + + [(stage.id, stage.title) for stage in stages] + + [("dynamic_create", _("Dynamic Create"))] + ) + # project = Project.objects.filter(id = project_id).first() + # if ( + # request.user.is_superuser or + # request.user.employee_get in project.managers.all() + # ): + # form.fields['stage'].choices.append(('dynamic_create','Dynamic create')) + return render( + request, "cbv/tasks/task_form.html", {"request": request, "form": form} + ) + @login_required def create_stage_taskall(request): """ - This is an ajax method to return json response to create stage related + This is an ajax method to return json response to create stage related to the project in the task-all form fields """ - if request.method == 'GET': + if request.method == "GET": project_id = request.GET["project_id"] project = Project.objects.get(id=project_id) - form = ProjectStageForm(initial = {'project':project}) - if request.method == 'POST': + form = ProjectStageForm(initial={"project": project}) + if request.method == "POST": form = ProjectStageForm(request.POST) if form.is_valid(): instance = form.save() @@ -1062,32 +1366,42 @@ def create_stage_taskall(request): ) - @login_required def drag_and_drop_stage(request): """ For drag and drop project stage into new sequence """ - sequence = request.POST['sequence'] + sequence = request.POST["sequence"] sequence = json.loads(sequence) - stage_id = (list(sequence.keys())[0]) + stage_id = list(sequence.keys())[0] project = ProjectStage.objects.get(id=stage_id).project change = False - if (request.user.has_perm('project.change_project') or - request.user.employee_get == project.manager or - request.user.employee_get in project.members.all() + if ( + request.user.has_perm("project.change_project") + or request.user.employee_get in project.managers.all() + or request.user.employee_get in project.members.all() ): for key, val in sequence.items(): if val != ProjectStage.objects.get(id=key).sequence: change = True ProjectStage.objects.filter(id=key).update(sequence=val) - return JsonResponse({'type': 'success','message':_('Stage sequence updated',),'change':change}) - change = True - return JsonResponse({'type': 'info','message':_('You dont have permission.'),'change':change}) + if change: + messages.success( + request, _("The project stage sequence has been successfully updated.") + ) + return JsonResponse( + { + "change": change, + } + ) + messages.warning(request, _("You don't have permission.")) + return JsonResponse({"type": change}) + # Time sheet views -@permission_required(perm='project.view_timesheet') + +# @permission_required(perm='project.view_timesheet') @login_required def time_sheet_view(request): """ @@ -1103,22 +1417,20 @@ def time_sheet_view(request): HttpResponse: The rendered HTTP response displaying the time sheets. """ form = TimeSheetFilter() - view_type= "card" - if request.GET.get("view")=="list": - view_type='list' + view_type = "card" + if request.GET.get("view") == "list": + view_type = "list" time_sheet_filter = TimeSheetFilter(request.GET).qs - time_sheet_filter = filtersubordinates(request,time_sheet_filter,"project.view_timesheet" ) - time_sheet_filter=list(time_sheet_filter) + time_sheet_filter = filtersubordinates( + request, time_sheet_filter, "project.view_timesheet" + ) + time_sheet_filter = list(time_sheet_filter) for item in TimeSheet.objects.filter(employee_id=request.user.employee_get): if item not in time_sheet_filter: time_sheet_filter.append(item) - + time_sheets = paginator_qry(time_sheet_filter, request.GET.get("page")) - context = { - "time_sheets": time_sheets, - "f": form, - "view_type":view_type - } + context = {"time_sheets": time_sheets, "f": form, "view_type": view_type} return render( request, "time_sheet/time_sheet_view.html", @@ -1126,6 +1438,117 @@ def time_sheet_view(request): ) +def time_sheet_initial(request): + """ + This is an ajax method to return json response to take only tasks related + to the project in the timesheet form fields + """ + project_id = request.GET["project_id"] + tasks = Task.objects.filter(project=project_id).values("title", "id") + return JsonResponse({"data": list(tasks)}) + + +# def get_members(request): +# project_id = request.GET.get("project_id") +# project = Project.objects.get(id=project_id) +# user_employee = request.user.employee_get +# members = project.members.all().values_list("employee_first_name", "id") +# members = list(members) + +# # Include the user if not already a member +# # if user_employee.id not in [member[1] for member in members]: +# # members.append((user_employee.first_name, user_employee.id)) + +# return JsonResponse({'data': list(members)}) + + +def get_members(request): + project_id = request.GET.get("project_id") + task_id = request.GET.get("task_id") + form = TimeSheetForm() + if project_id and task_id: + if task_id != "dynamic_create" and project_id != "dynamic_create": + project = Project.objects.filter(id=project_id).first() + task = Task.objects.filter(id=task_id).first() + employee = Employee.objects.filter(id=request.user.employee_get.id) + if employee.first() in project.managers.all(): + members = ( + employee + | project.members.all() + | task.task_managers.all() + | task.task_members.all() + ).distinct() + elif employee.first() in task.task_managers.all(): + members = (employee | task.task_members.all()).distinct() + else: + members = employee + form.fields["employee_id"].queryset = members + else: + form.fields["employee_id"].queryset = Employee.objects.none() + + employee_field_html = render_to_string( + "cbv/timesheet/employee_field.html", + { + "form": form, + "field_name": "employee_id", + "field": form.fields["employee_id"], + "task_id": task_id, + "project_id": project_id, + }, + ) + return HttpResponse(employee_field_html) + + +def get_tasks_in_timesheet(request): + project_id = request.GET.get("project_id") + form = TimeSheetForm() + if project_id and project_id != "dynamic_create": + project = Project.objects.get(id=project_id) + employee = request.user.employee_get + all_tasks = Task.objects.filter(project=project) + # ie the employee is a project manager return all tasks + if ( + employee in project.managers.all() + or employee in project.members.all() + or request.user.has_perm("project.add_timesheet") + ): + tasks = all_tasks + # if the employee is a task manager and task member + elif ( + Task.objects.filter(project=project_id, task_managers=employee).exists() + and Task.objects.filter(project=project_id, task_members=employee).exists() + ): + tasks = ( + Task.objects.filter(project=project_id, task_managers=employee) + | Task.objects.filter(project=project_id, task_members=employee) + ).distinct() + # if the employee is manager of a task under the project + elif Task.objects.filter(project=project_id, task_managers=employee).exists(): + tasks = Task.objects.filter(project=project_id, task_managers=employee) + # if the employee ids a member of task under the project + elif Task.objects.filter(project=project_id, task_members=employee).exists(): + tasks = Task.objects.filter(project=project_id, task_members=employee) + form.fields["task_id"].queryset = tasks + form.fields["task_id"].choices = list(form.fields["task_id"].choices) + if employee in project.managers.all() or request.user.is_superuser: + form.fields["task_id"].choices.append(("dynamic_create", "Dynamic create")) + task_id = request.GET.get("task_id") + if task_id: + form.fields["task_id"].initial = task_id + else: + form.fields["task_id"].queryset = Task.objects.none() + + task_field_html = render_to_string( + "cbv/timesheet/task_field.html", + { + "form": form, + "field_name": "task_id", + "field": form.fields["task_id"], + }, + ) + return HttpResponse(task_field_html) + + @login_required def time_sheet_creation(request): """ @@ -1147,14 +1570,17 @@ def time_sheet_creation(request): if request.method == "POST": form = TimeSheetForm(request.POST, request.FILES, request=request) if form.is_valid(): - form.save() + form.save() messages.success(request, _("Time sheet created")) response = render( request, "time_sheet/form-create.html", context={"form": form} ) - return HttpResponse(response.content.decode("utf-8") + "") + return HttpResponse( + response.content.decode("utf-8") + "" + ) return render(request, "time_sheet/form-create.html", context={"form": form}) + @login_required def time_sheet_project_creation(request): """ @@ -1200,11 +1626,11 @@ def time_sheet_task_creation(request): """ if request.method == "GET": project_id = request.GET["project_id"] - project = Project.objects.get(id = project_id) + project = Project.objects.get(id=project_id) stages = ProjectStage.objects.filter(project__id=project_id) task_form = TaskTimeSheetForm(initial={"project": project}) task_form.fields["stage"].queryset = stages - + if request.method == "POST": task_form = TaskTimeSheetForm(request.POST, request.FILES) if task_form.is_valid(): @@ -1215,9 +1641,7 @@ def time_sheet_task_creation(request): return render( request, "time_sheet/form_task_time_sheet.html", - context={"form": task_form, - 'project_id':project_id - }, + context={"form": task_form, "project_id": project_id}, ) @@ -1235,7 +1659,7 @@ def time_sheet_update(request, time_sheet_id): Otherwise, renders the time sheet update form. """ - if time_sheet_update_permissions(request,time_sheet_id): + if time_sheet_update_permissions(request, time_sheet_id): time_sheet = TimeSheet.objects.get(id=time_sheet_id) update_form = TimeSheetForm(instance=time_sheet, request=request) update_form.fields["task_id"].queryset = time_sheet.project_id.task_set.all() @@ -1243,7 +1667,6 @@ def time_sheet_update(request, time_sheet_id): if request.method == "POST": update_form = TimeSheetForm(request.POST, instance=time_sheet) - if update_form.is_valid(): update_form.save() messages.success(request, _("Time sheet updated")) @@ -1252,7 +1675,8 @@ def time_sheet_update(request, time_sheet_id): request, "./time_sheet/form-create.html", context={"form": form} ) return HttpResponse( - response.content.decode("utf-8") + "" + response.content.decode("utf-8") + + "" ) return render( request, @@ -1262,7 +1686,7 @@ def time_sheet_update(request, time_sheet_id): }, ) else: - return render (request,"error.html") + return render(request, "error.html") @login_required @@ -1277,31 +1701,18 @@ def time_sheet_delete(request, time_sheet_id): Returns: HttpResponseRedirect: A redirect response to the time sheet view page. """ - if time_sheet_delete_permissions(request,time_sheet_id): + if time_sheet_delete_permissions(request, time_sheet_id): TimeSheet.objects.get(id=time_sheet_id).delete() - view_type = "list" - if request.GET.get("view")=="card": - view_type="card" - return redirect('/project/view-time-sheet'+"?view="+view_type) + messages.success(request, _("The time sheet has been deleted successfully")) + view_type = "card" + if request.GET.get("view") == "list": + view_type = "list" + task_id = request.GET.get("task_id") + if task_id: + return redirect(f"/project/task-timesheet/{task_id}/") + return redirect("/project/view-time-sheet" + "?view=" + view_type) else: - return render (request,"error.html") - -@login_required -def time_sheet_delete_ajax(request, time_sheet_id): - """ - View function to handle the deletion of a time sheet. - - Parameters: - request (HttpRequest): The HTTP request object. - time_sheet_id (int): The ID of the time sheet to be deleted. - - Returns: - JsonResponse: A success message after deleting timeshhet or a info message if the user don't have the permission to delete . - """ - if time_sheet_delete_permissions(request,time_sheet_id): - TimeSheet.objects.get(id=time_sheet_id).delete() - return JsonResponse({'type': 'success','message':_('Timesheet deleted successfully.')}) - return JsonResponse({'type': 'info','message':_("You don't have permission."),}) + return you_dont_have_permission(request) def time_sheet_filter(request): @@ -1317,23 +1728,25 @@ def time_sheet_filter(request): """ emp_id = request.user.employee_get.id filtered_time_sheet = TimeSheetFilter(request.GET).qs - - time_sheet_filter = filtersubordinates(request,filtered_time_sheet,"project.view_timesheet" ) - if filtered_time_sheet.filter(employee_id__id =emp_id).exists(): - time_sheet_filter=list(time_sheet_filter) + + time_sheet_filter = filtersubordinates( + request, filtered_time_sheet, "project.view_timesheet" + ) + if filtered_time_sheet.filter(employee_id__id=emp_id).exists(): + time_sheet_filter = list(time_sheet_filter) for item in TimeSheet.objects.filter(employee_id=request.user.employee_get): if item not in time_sheet_filter: time_sheet_filter.append(item) time_sheets = paginator_qry(time_sheet_filter, request.GET.get("page")) - previous_data = request.environ['QUERY_STRING'] + previous_data = request.environ["QUERY_STRING"] data_dict = parse_qs(previous_data) get_key_instances(TimeSheet, data_dict) view_type = request.GET.get("view") template = "time_sheet/time_sheet_list_view.html" if view_type == "card": - template="time_sheet/time_sheet_card_view.html" - elif view_type== "chart": - return redirect("personal-time-sheet-view"+"?view="+emp_id) + template = "time_sheet/time_sheet_card_view.html" + elif view_type == "chart": + return redirect("personal-time-sheet-view" + "?view=" + emp_id) return render( request, template, @@ -1347,7 +1760,7 @@ def time_sheet_filter(request): @login_required def time_sheet_initial(request): """ - This is an ajax method to return json response to take only tasks related + This is an ajax method to return json response to take only tasks related to the project in the timesheet form fields """ project_id = request.GET["project_id"] @@ -1357,11 +1770,11 @@ def time_sheet_initial(request): def personal_time_sheet(request): """ - This is an ajax method to return json response for generating bar charts to employees. + This is an ajax method to return json response for generating bar charts to employees. """ emp_id = request.GET["emp_id"] selected = request.GET["selected"] - month_number =request.GET["month"] + month_number = request.GET["month"] year = request.GET["year"] week_number = request.GET["week"] @@ -1369,10 +1782,10 @@ def personal_time_sheet(request): dataset = [] projects = Project.objects.filter(project_timesheet__employee_id=emp_id).distinct() - + time_sheets = TimeSheet.objects.filter(employee_id=emp_id).order_by("date") - time_sheets=time_sheets.filter(date__week=week_number) + time_sheets = time_sheets.filter(date__week=week_number) # check for labels to be genarated weeky or monthly if selected == "week": @@ -1383,19 +1796,19 @@ def personal_time_sheet(request): for i in range(7): day = start_date + datetime.timedelta(days=i) date_list.append(day) - day=day.strftime("%d-%m-%Y %A") + day = day.strftime("%d-%m-%Y %A") labels.append(day) elif selected == "month": - days_in_month = calendar.monthrange(int(year), int(month_number)+1)[1] - start_date = datetime.datetime(int(year), int(month_number)+1, 1).date() + days_in_month = calendar.monthrange(int(year), int(month_number) + 1)[1] + start_date = datetime.datetime(int(year), int(month_number) + 1, 1).date() labels = [] date_list = [] for i in range(days_in_month): day = start_date + datetime.timedelta(days=i) date_list.append(day) - day=day.strftime("%d-%m-%Y") - labels.append(day) + day = day.strftime("%d-%m-%Y") + labels.append(day) colors = generate_colors(len(projects)) for project, color in zip(projects, colors): @@ -1410,7 +1823,7 @@ def personal_time_sheet(request): # Calculate total hours for each project on each date total_hours_by_project_and_date = defaultdict(lambda: defaultdict(float)) - #addding values to the response + # addding values to the response for label in date_list: time_sheets = TimeSheet.objects.filter(employee_id=emp_id, date=label) for time in time_sheets: @@ -1418,7 +1831,9 @@ def personal_time_sheet(request): total_hours_by_project_and_date[time.project_id.title][label] += time_spent for data in dataset: project_title = data["label"] - data["data"] = [total_hours_by_project_and_date[project_title][label] for label in date_list] + data["data"] = [ + total_hours_by_project_and_date[project_title][label] for label in date_list + ] response = { "dataSet": dataset, @@ -1427,7 +1842,7 @@ def personal_time_sheet(request): return JsonResponse(response) -def personal_time_sheet_view(request,emp_id): +def personal_time_sheet_view(request, emp_id): """ Function for viewing the barcharts for timesheet of a specific employee. @@ -1440,19 +1855,25 @@ def personal_time_sheet_view(request,emp_id): """ try: Employee.objects.get(id=emp_id) - except : - return render (request,"error.html") - emp_last_name = Employee.objects.get(id=emp_id).employee_last_name if Employee.objects.get(id=emp_id).employee_last_name!=None else "" - employee_name = f"{Employee.objects.get(id=emp_id).employee_first_name} {emp_last_name}" + except: + return render(request, "error.html") + emp_last_name = ( + Employee.objects.get(id=emp_id).employee_last_name + if Employee.objects.get(id=emp_id).employee_last_name != None + else "" + ) + employee_name = ( + f"{Employee.objects.get(id=emp_id).employee_first_name} {emp_last_name}" + ) context = { - "emp_id" :emp_id, - "emp_name":employee_name, + "emp_id": emp_id, + "emp_name": employee_name, } - return render(request,"time_sheet/chart.html",context=context) + return render(request, "time_sheet/chart.html", context=context) -def time_sheet_single_view(request,time_sheet_id): +def time_sheet_single_view(request, time_sheet_id): """ Renders a single timesheet view page. @@ -1468,6 +1889,7 @@ def time_sheet_single_view(request,time_sheet_id): context = {"time_sheet": timesheet} return render(request, "time_sheet/time_sheet_single_view.html", context) + def time_sheet_bulk_delete(request): """ This method is used to delete set of Task instances @@ -1484,6 +1906,7 @@ def time_sheet_bulk_delete(request): except Exception as error: messages.error(request, error) messages.error( - request, _("You cannot delete %(timesheet)s.") % {"timesheet": timesheet} + request, + _("You cannot delete %(timesheet)s.") % {"timesheet": timesheet}, ) return JsonResponse({"message": "Success"})