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 "Manager" %}
+ {{form.managers}}
+
+
+ {% trans "Status" %}
+ {{form.status}}
+
+
+ {% trans "Start from" %}
+ {{form.start_from}}
+
+
+
+
+ {% trans "Members" %}
+ {{form.members}}
+
+
+ {% trans "Active" %}
+ {{form.is_active}}
+
+
+ {% trans "End till" %}
+ {{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 %}
+
+
+
\ 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 %}
+
+
+{% include "cbv/projects/project_list.html" %}
+
+{% else %}
+
+
+
+
{% 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 %}
+
+
+
+
+ {% 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 "Manager" %}
+ {{form.task_manager}}
+
+
+ {% trans "Stage" %}
+ {{form.stage}}
+
+
+
+
+ {% trans "Project" %}
+ {{form.project}}
+
+
+ {% trans "Status" %}
+ {{form.status}}
+
+
+
+
+ {% trans "End Date" %}
+ {{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 %}
+
\ 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 %}
+{% trans form.employee_id.label %}
+{{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 "Project" %}
+ {{form.project_id}}
+
+
+ {% trans "Status" %}
+ {{form.status}}
+
+
+
+
+
+ {% trans "Task" %}
+ {{form.task}}
+
+
+ {% trans "Date" %}
+ {{form.date}}
+
+
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %}
+
+
+ {% trans "Employee" %}
+ {{form.employee_id}}
+
+
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+ {% trans "Start Date From" %}
+ {{form.start_from}}
+
+
+
+
+ {% trans "Till End Date" %}
+ {{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 %}
+
+ {% trans form.task_id.label %}
+
+
+ {{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" %}
+
+
\ 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 %}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {% if unexpired_project %}
- {% for project in unexpired_project %}
-
-
-
-
-
-
-
{{project.title}}
-
-
-
- {% endfor %}
- {% else %}
-
-
-
-
{% 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 %}
+
+
+
+
+
+
+
+
+
+
+
+{% 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 %}
-
-
\ No newline at end of file
+{% load i18n %} {% load horillafilters %}
+
+
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 %}
\ No newline at end of file
+{% load i18n %} {% load basefilters %}
+{% 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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 actions {% endcomment %}
-
-
-
- {% trans "Actions" %}
-
-
-
-
- {% endif %}
- {% comment %} for create project {% endcomment %}
-
-
-
-
+{% load i18n %}
+
+
+
+
+
+
+
+
{% trans "Projects" %}
+
+
+
+
+
+ {% if projects %}
+ {% comment %} for search{% endcomment %}
+
+ {% comment %} for actions {% endcomment %}
+
+
+
+ {% trans "Actions" %}
+
+
+
+
+ {% 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 %}
-
- {% endfor %}
-
- {% comment %} pagination {% endcomment %}
-
-{% comment %}
{% endcomment %}
-
-{% else %}
-
-
-
-
{% 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 %}
+
+ {% endfor %}
+
+ {% comment %} pagination {% endcomment %}
+
+{% comment %} {% endcomment %}
+
+{% else %}
+
+
+
+
{% 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 %}
-
-
{{project.description}}
-
-
-
- {% endfor %}
- {% else %}
-
-
-
-
{% trans "There are currently no available projects; please create a new one." %}
-
-
-
- {% endif %}
-
-
-
-
- {% comment %} {% 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 %}
+
+
{{project.description}}
+
+
+
+ {% endfor %}
+ {% else %}
+
+
+
+
{% trans "There are currently no available projects; please create a new one." %}
+
+
+
+ {% endif %}
+
+
+
+
+ {% comment %} {% 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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 %}
-
\ No newline at end of file
+{% load i18n %} {% load basefilters %}
+
+
+
+
+
+
+
+
+ {% trans "Managers" %}
+ {{f.form.task_managers}}
+
+
+ {% trans "Stage" %}
+ {{f.form.stage}}
+
+
+
+
+ {% trans "Members" %}
+ {{f.form.task_members}}
+
+
+ {% trans "Status" %}
+ {{f.form.status}}
+
+
+
+
+ {% trans "End till" %}
+ {{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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 %}
-
-
-
\ No newline at end of file
+{% load i18n %}
+
+
+
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 %}
-
+{% load i18n %}
+
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 @@
+
+
\ 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 %}
-
-
-
\ No newline at end of file
+{% load i18n %}
+
+
+
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 %}
-
\ No newline at end of file
+{% load i18n %}
+
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 %}
-
-
- {% for stage in stages %} {% if stage.id == task.stage.id %}
- {{stage}}
- {% else %}
- {{stage}}
- {% endif %} {% endfor %}
-
-
-
-
{{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 %}
+
+
+ {% for stage in stages %} {% if stage.id == task.stage.id %}
+ {{stage}}
+ {% else %}
+ {{stage}}
+ {% endif %} {% endfor %}
+
+
+
+
{{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 %}
+
+ {% endif %}
+ {% endfor %}
+
+
+ {% endfor %}
+
+ {% comment %}
+ {% trans "Stage" %}
+ {% endcomment %}
+
+
+
+
+
+{% else %}
+
+
+
+
{% 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 %}
-
-
- {% comment %} {% endcomment %}
-
+{% load i18n %} {% load horillafilters %}
+
+
+ {% 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}}
+
+ {% 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 %}
+
+
+
+ {% trans "Status" %}
+
+ {% for option in task.TASK_STATUS %}
+ {{option.1}}
+ {% endfor %}
+
+
+
+
+
+
+ {% endif %}
+ {% endfor %}
+
+
+ {% endfor %}
+ {% if request.user|is_project_manager:stages.first.project or request.user.is_superuser %}
+
+
+
+ {% trans "Add Stage" %}
+
+
+ {% endif %}
+
+
+{% else %}
+
+
+
+
+ {% 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 %}
-
- {% endif %}
- {% endfor %}
-
-
- {% endfor %}
-
-
- {% trans "Stage" %}
-
-
-
-
-
-
-{% else %}
-
-
-
-
{% 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 %}
+
+
+
+ {% trans "Add Stage" %}
+
+
+ {% endif %}
+ {% if stages %}
+
+ {% else %}
+
+
+
+
{% trans "No search result found!" %}
+
+
+ {% endif %}
-{% if messages %}
-
- {% for message in messages %}
-
- {{ message }}
-
- {% endfor %}
-
-{% endif %}
-{% include "filter_tags.html" %}
-{% if stages %}
-
-{% else %}
-
-
-
-
{% trans "There are currently no available tasks; please create a new one." %}
-
-
-{% endif %}
-
-
-
-
-
-
-
-
-{% comment %}
-
-
-
-
-
-
-
-
- {% 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 %}
-
-
- {% trans "Filter" %}
-
-
-
- {% comment %} for create task {% endcomment %}
-
-
-
-
+{% load i18n taskfilters %}
+
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 %}
-
-
-
-
-
+{% load i18n %} {% load horillafilters %} {% load static %}
+
+
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 %}
-
-
\ No newline at end of file
+{% load i18n %}
+
+
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 %}
-
-
-{% comment %} modals for showing new project and new task creation {% endcomment %}
-
-
-
-
+{% load i18n %}
+
+
+{% 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 %}
-
-{% comment %} modals for showing new project and new task creation {% endcomment %}
-
-
-
\ No newline at end of file
+{% load i18n %}
+
+{% 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 %}
-
- {% 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 %}
-
- {% endfor %}
-
-
-
-{% else %}
-
-
-
-
{% trans "There are currently no available tasks; please create a new one." %}
-
-
-{% endif %}
+{% load i18n %}
+{% load static %}
+{% if messages %}
+
+ {% for message in messages %}
+
+ {% 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 %}
+
+ {% endfor %}
+
+
+
+{% else %}
+
+
+
+
{% 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 "Manager" %}
- {{f.form.task_manager}}
-
-
- {% trans "Stage" %}
- {{f.form.stage}}
-
-
-
-
- {% trans "Project" %}
- {{f.form.project}}
-
-
- {% trans "Status" %}
- {{f.form.status}}
-
-
-
-
- {% trans "End Date" %}
- {{f.form.end_till}}
-
-
-
-
-
-
-
-
-
+{% load i18n %} {% load basefilters %}
+
+
+
+
+
+
+
+ {% trans "Manager" %}
+ {{f.form.task_manager}}
+
+
+ {% trans "Stage" %}
+ {{f.form.stage}}
+
+
+
+
+ {% trans "Project" %}
+ {{f.form.project}}
+
+
+ {% trans "Status" %}
+ {{f.form.status}}
+
+
+
+
+ {% trans "End Date" %}
+ {{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 %}
-
- {% 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 %}
-
- {% endfor %}
-
-
-
- {% comment %} pagination {% endcomment %}
-
-{% else %}
-
-
-
-
{% 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 %}
+
+ {% 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 %}
+
+ {% endfor %}
+
+
+
+ {% comment %} pagination {% endcomment %}
+
+{% else %}
+
+
+
+
{% 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 actions {% endcomment %}
-
-
-
- {% trans "Actions" %}
-
-
-
-
-
- {% endif %}
- {% comment %} for create {% endcomment %}
-
-
-
-
-
\ No newline at end of file
+{% load i18n %}
+
+
+
+
+
{% trans "Tasks" %}
+
+
+
+
+
+
+ {% if tasks %}
+
+
+ {% comment %} for actions {% endcomment %}
+
+
+
+ {% trans "Actions" %}
+
+
+
+
+
+ {% 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 %}
-
-
-
\ No newline at end of file
+{% load i18n %}
+
+
+
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 %}
-
-{% 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 %}
+
+{% 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 %}
-
+{% load i18n %} {% load basefilters %}
+
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 %}
-
-
-
-
-
-{% endblock content %}
+{% block content %} {% load static %} {% load i18n %}
+
+
+
+
+
+{% 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 %}
-
-
\ No newline at end of file
+{% load i18n %}
+
+
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 %}
-
-
\ No newline at end of file
+{% load i18n %}
+
+
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 %}
+
+ {% endfor %}
+
+
+
+
+ {% else %}
+
+
+
+
{% 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 %}
-
- {% endfor %}
-
-
-
-
- {% else %}
-
-
-
-
{% 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 %}
-
-
-
-
-
-
-
-
-
-
-
{{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 %}
-
-
-
-
{% 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 %}
+
+
+
+
+
+
+
+
+
+
+
{{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 %}
+
+
+
+
{% 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 %}
-
-
-
-
-
- {% trans "Filter" %}
-
-
-
- {% comment %} for actions {% endcomment %}
-
-
-
- {% trans "Actions" %}
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+{% load i18n %}
+{% load basefilters %}
+
+
+
{% trans "Time Sheet" %}
+
+
+
+
+ {% if perms.project.view_timesheet or request.user|is_reportingmanager %}
+
+
+
+
+
+ {% endif %}
+
+
+
+
+
+ {% trans "Filter" %}
+
+
+
+ {% comment %} for actions {% endcomment %}
+
+
+
+ {% trans "Actions" %}
+
+
+
+
+
+
+
+
+
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 %}
-
-
-
+{% load i18n %} {% load horillafilters %} {% load basefilters %}
+
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"})