[UPDT] PROJECT: Missing files updated

This commit is contained in:
Horilla
2025-02-14 10:01:48 +05:30
parent 07e9c2331e
commit d453c1d192
95 changed files with 12125 additions and 7542 deletions

View File

@@ -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)

View File

@@ -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
"""

0
project/cbv/__init__.py Normal file
View File

View File

@@ -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)

View File

@@ -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

107
project/cbv/dashboard.py Normal file
View File

@@ -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

View File

@@ -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("<script>window.location.reload()</script>")
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"<span hx-get='/project/task-filter/{project_id}' hx-trigger='load' hx-target='#viewContainer'></span>"
)
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)

518
project/cbv/projects.py Normal file
View File

@@ -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("<script>window.location.reload()</script>")
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} <br> Start date : {start_date} <br>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",
# },
# ]
# )

583
project/cbv/tasks.py Normal file
View File

@@ -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("<script>window.location.reload()</script>")
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("<script>location.reload();</script>")
return self.HttpResponse("<script>$('#taskFilterButton').click();</script>")
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} <br> Stage Name : {stage}<br> 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

482
project/cbv/timesheet.py Normal file
View File

@@ -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": " <b>{date}</b> <br>Project : <b>{project_id}</b> <br><b>{task_id}</b> | Time Spent : <b>{time_spent}</b>",
}
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"

View File

@@ -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
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("<script>window.location.reload()</script>")
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("<script>window.location.reload()</script>")
# 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("<script>window.location.reload()</script>")
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("<script>window.location.reload()</script>")
# 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("<script>window.location.reload()</script>")
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("<script>window.location.reload()</script>")
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("<script>window.location.reload()</script>")
return is_task_manager

View File

@@ -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 ProjectFilter(FilterSet):
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 TimeSheetFilter(FilterSet):
"date",
"status",
]
def filter_by_employee(self, queryset, _, value):
"""
Filter queryset by first name or last name.
@@ -159,4 +164,3 @@ class TimeSheetFilter(FilterSet):
employee_id__employee_last_name__icontains=last_name
)
return queryset

View File

@@ -1,14 +1,23 @@
from typing import Any
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import *
from payroll.forms.forms import ModelForm
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 *
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 @@ class ProjectTimeSheetForm(ModelForm):
"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 ProjectTimeSheetForm(ModelForm):
}
class TaskForm(ModelForm):
"""
Form for Task model
@@ -71,16 +81,36 @@ class TaskForm(ModelForm):
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 TaskFormCreate(ModelForm):
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 @@ class TaskFormCreate(ModelForm):
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 TimeSheetForm(ModelForm):
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 ProjectStageForm(ModelForm):
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 @@ class TaskTimeSheetForm(ModelForm):
{
"style": "width: 100%; height: 47px;",
"class": "oh-select",
}
)
self.fields["description"].widget.attrs.update(
@@ -246,14 +340,5 @@ class TaskTimeSheetForm(ModelForm):
"class": "oh-select",
}
)
self.fields["stage"].widget.attrs.update(
{
"id":'project_stage'
}
)
self.fields["stage"].widget.attrs.update({"id": "project_stage"})

View File

@@ -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
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'<script>window.location.href = "{previous_url}"</script>'
return HttpResponse(script)

View File

@@ -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 = "<br>".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 = "<br>".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(
'<a href="{0}" style="text-decoration: none" rel="noopener noreferrer" class="oh-btn oh-btn--light" target="_blank" onclick="event.stopPropagation();">'
'<span class="oh-file-icon oh-file-icon--pdf"></span>'
"&nbsp View"
"</a>",
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(
'<div style="display: flex; align-items: center;">'
' <div class="oh-tabs__input-badge-container">'
' <span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1" title="{1} Tasks">'
" {1}"
" </span>"
" </div>"
" <div>{0}</div>"
"</div>",
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 = "<br>".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 = "<br>".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

View File

@@ -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
"""
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

View File

@@ -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();
});
});
$(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();
});
});

View File

@@ -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();
}
});
})
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();
}
});
})

File diff suppressed because it is too large Load Diff

View File

@@ -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}"}`);
}
});
});
$(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}"}`);
}
});
});

View File

@@ -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(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`);
}
},
});
};
});
$('.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(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`);
}
},
})
}, 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}"}`);
}
});
});
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);
});
});

View File

@@ -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);
}
},
});
}
});
}
});
});
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 {
}
},
});
}
});
}
});
});

View File

@@ -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);
}
},
});
}
});
}
});
});
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);
}
},
});
}
});
}
});
};

View File

@@ -0,0 +1,70 @@
{% load i18n %}
{% load taskfilters %}
<div class="oh-btn-group">
{% if perms.project.change_project or request.user|is_project_manager:instance %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#genericModal"
hx-get="{% url 'update-project' instance.id %}"
hx-target="#genericModalBody"
onclick="event.stopPropagation();"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
{% else %}
<button
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
disabled
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</button>
{% endif %}
{% if perms.project.delete_project or request.user|is_project_manager:instance %}
{% if instance.is_active %}
<a
class="oh-btn oh-btn--light-bkg w-100"
href="{{instance.get_archive_url}}"
onclick="return confirm('Do you want to {{instance.archive_status}} this project?')"
title="{% trans 'Archive' %}"
>
<ion-icon name="archive"></ion-icon>
</a>
{% else %}
<a
class="oh-btn oh-btn--light-bkg w-100"
href="{{instance.get_archive_url}}"
onclick="return confirm('Do you want to {{instance.archive_status}} this project?')"
title="{% trans 'Un-Archive' %}"
>
<ion-icon name="archive"></ion-icon>
</a>
{% endif %}
{% endif %}
{% if perms.project.delete_project %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-project' instance.id %}?view=list"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
{% endif %}
</div>

View File

@@ -0,0 +1,40 @@
{% load i18n%}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');"
>{% trans "Project" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{form.managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{form.status}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Start from" %}</label>
{{form.start_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Members" %}</label>
{{form.members}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Active" %}</label>
{{form.is_active}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
{{form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,125 @@
{% load mathfilters %}
{% load static i18n %}
{% load taskfilters %}
<div class="oh-card">
<div class="oh-helpdesk__header">
<div class="d-flex justify-content-between align-items-center">
<ul class="helpdesk__card-items">
<li class="helpdesk__card-item">
<span class="helpdesk__card-label">{% trans "Project:" %}</span>
<span class="helpdesk__card-value b" name="" id="">{{project}}</span>
</li>
</ul>
<span class="helpdesk__card-value me-3" style="display:flex">
{% if perms.pms.change_project or request.user|is_project_manager_or_member:project %}
<select id="status" class="oh-select oh-select--sm ms-3 mr-1 " name="status" title="Status"
hx-post="{%url 'change-project-status' project.id %}"
hx-trigger="change" hx-target="#reloadMessages">
<option value="{{project.status}}" selected>
{% trans project.get_status_display %}
</option>
{%for status in project.PROJECT_STATUS %}
{% if project.status != status.0 %}
<option value="{{status.0}}">{% trans status.1 %}</option>
{% endif%}
{% endfor %}
</select>
{% else %}
<label id="status" class="oh-select oh-select--sm ms-3" name="status" title="Status">
{% trans project.get_status_display %}
</label>
{% endif %}
{% if perms.project.change_project or perms.project.add_projectstage or request.user|is_project_manager_or_member:project %}
<div class="oh-btn-group">
{% if perms.project.add_project %}
<a
onclick="event.preventDefault();event.stopPropagation()"
data-toggle="oh-modal-toggle"
data-target="#objectCreateModal"
hx-get="{% url 'update-project' project.id %}"
hx-target="#objectCreateModalTarget"
class="oh-btn oh-btn--light-bkg w-100"
title="{% trans 'Edit' %}"
><ion-icon name="create-outline"></ion-icon
></a>
{% endif %}
</div>
{% endif %}
</span>
</div>
<ul class="helpdesk__card-items">
<li class="helpdesk__card-item">
<span class="helpdesk__card-label">{% trans "Managers:" %}</span>
<span class="helpdesk__card-value" name="" id="">
<div class="d-flex justify-content-between custom-scroll">
<div class="avatars" id="avatarsContainer">
{% for manager in project.managers.all %}
<a
href="{% url 'employee-view-individual' manager.id %}"
class="avatars__item"
title="{{manager}}"
><img class="avatar" src="{{manager.get_avatar}}" alt=""
/></a>
{% endfor %}
</div>
</div>
</span>
</li>
<li class="helpdesk__card-item">
<span class="helpdesk__card-label">{% trans "Members:" %}</span>
<span class="helpdesk__card-value" name="" id="">
<div class="d-flex justify-content-between custom-scroll">
<div class="avatars" id="avatarsContainer">
{% for menber in project.members.all %}
<a
href="{% url 'employee-view-individual' menber.id %}"
class="avatars__item"
title="{{menber}}"
><img class="avatar" src="{{menber.get_avatar}}" alt=""
/></a>
{% endfor %}
</div>
</div>
</span>
</li>
<li class="helpdesk__card-item">
<span class="helpdesk__card-label">{% trans "Start date:" %}</span>
<span class="helpdesk__card-value" name="" id=""
>{{project.start_date}}
</span
>
</li>
<li class="helpdesk__card-item">
<span class="helpdesk__card-label">{% trans "End date:" %}</span>
<span class="helpdesk__card-value" name="" id=""
>{{project.end_date}}
</span
>
<span title = 'due {% if project.end_date == today %} today {% else %}in {{project.end_date|sub:today}}{% endif %}'>
<ion-icon
class="text-{% if project.end_date < today %}danger {% elif project.end_date == today %}warning {% else %}success{% endif %}"
name="time-sharp"
>
</ion-icon>
</span>
</li>
<li class="helpdesk__card-item">
<div class="d-flex">
<div>
<span class="helpdesk__card-label">{% trans "Description:" %}</span>
</div>
<details>
<summary>
</summary>
<p>
{{project.description}}
</p>
</details>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,38 @@
{% load basefilters i18n %}
{% for project in projects %}
<div class="oh-user_permission-list_item perm-accordion exclude-accordion-style" onclick="$(this).next().toggle();$(this).toggleClass('perm-accordion-active');$(this).next().find('.oh-general__tab-target').removeClass('d-none');">
<div class="oh-user_permission-list_profile">
<div class="oh-navbar__user-photo oh-user_permission--profile">
<img src="{{ project.get_avatar }}" class="oh-navbar__user-image" loading="lazy" />
</div>
<div class="oh-feedback-card__name-container ms-1" hx-target="#genericModalBody" data-target="#genericModal" data-toggle="oh-modal-toggle" hx-get="{% url 'project-detailed-view' project.id %}">
<span class="oh-card__title oh-card__title--sm fw-bold me-1">{{ project.title }}</span>
</div>
</div>
<div class="d-flex d-flex-reverse">
<button
onclick = "event.stopPropagation();"
class="oh-btn "
title="{% trans 'View Project' %}"
hx-target="#genericModalBody"
data-target="#genericModal"
data-toggle="oh-modal-toggle"
hx-get="{% url 'project-detailed-view' project.id %}"
><ion-icon
name="eye-outline"
role="img"
></ion-icon
></button>
<button class="oh-accordion-meta__btn oh-user_permssion-dropdownbtn"><ion-icon class="ms-2 oh-accordion-meta__btn-icon md hydrated" name="caret-down-outline" role="img" aria-label="caret down outline"></ion-icon></button>
</div>
</div>
<div class="panel view-employees" id="panel{{ employee.id }}" data-user-id="{{ employee.id }}">
<div class="oh-general__tab-target oh-profile-section">
<div hx-get="{% url 'tasks-list-individual-view' %}?employee_id={{ employee.id }}&project_id={{ project.id }}" hx-trigger="load">
</div>
</div>
</div>
{% endfor %}

View File

@@ -0,0 +1,9 @@
{% load i18n %}
<div id="stage-container">
{% include "generic/horilla_nav.html" %}
</div>
<script>
$('#applyFilter').closest('form').find('[name=is_active]').val('true');
$('#applyFilter').click();
</script>

View File

@@ -0,0 +1,19 @@
{% load i18n %} {% load static %}
<div id="messages" class="oh-alert-container"></div>
{% if projects %}
<div
class="oh-inner-sidebar-content__header d-flex justify-content-between align-items-center"
>
<h2 class="oh-inner-sidebar-content__title">{% trans "Projects" %}</h2>
</div>
<div id="projectContainer">
{% include "cbv/projects/project_list.html" %}
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 100px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "No projects assigned to this employee." %}</h3>
</div>
</div>
{% endif %}

View File

@@ -0,0 +1,149 @@
{% extends "index.html" %}
{% load i18n %}{% load static %}
{% block content %}
<style>
.expired--dot{
background-color:gray;
}
.cancelled--dot{
background-color:red;
}
.on-hold--dot{
background-color:orange;
}
.completed--dot{
background-color:yellowgreen;
}
.in-progress--dot{
background-color: rgb(103, 171, 238);
}
.new--dot{
background-color: cyan;
}
.status-completed{
border-left: 3.4px solid yellowgreen !important;
border-radius: 4px 0 0 4px;
}
.status-cancelled {
border-left: 3.4px solid red !important;
border-radius: 4px 0 0 4px;
}
.status-expired {
border-left: 3.4px solid gray !important;
border-radius: 4px 0 0 4px;
}
.status-on_hold {
border-left: 3.4px solid orange !important;
border-radius: 4px 0 0 4px;
}
.status-in_progress {
border-left: 3.4px solid rgb(103, 171, 238); !important;
border-radius: 4px 0 0 4px;
}
.status-new {
border-left: 3.4px solid cyan !important;
border-radius: 4px 0 0 4px;
}
</style>
<meta name="csrf-token" content="{{ csrf_token }}">
<div hx-get="{% url 'project-nav-view' %}?{{request.GET.urlencode}}" hx-trigger="load">
</div>
{% include "generic/components.html" %}
<div
class="oh-checkpoint-badge mb-2"
id="selectedInstances"
data-ids="[]"
data-clicked=""
style="display: none"
></div>
<div
class="oh-wrapper"
id="listContainer">
<div class="animated-background">
</div>
</div>
<div
class="oh-modal"
id="projectImport"
role="dialog"
aria-labelledby="projectImport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="projectImportLavel">
{% trans "Import Project" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-modal__dialog-body p-0 pt-2" id="projectImportModalBody">
<form
id="projectImportForm"
enctype="multipart/form-data"
>
<div id="loading" style="display: none;">
<div id="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<p id="progress-text">{% trans "Uploading..." %}</p>
</div>
<div id="error-container" style='color:red;'></div>
<div class="modal-body">
{% csrf_token %}
<div class="oh-dropdown__import-form">
<label class="oh-dropdown__import-label" for="uploadFile">
<ion-icon name="cloud-upload" class="oh-dropdown__import-form-icon md hydrated" role="img" aria-label="cloud upload"></ion-icon>
<span class="oh-dropdown__import-form-title">{% trans "Upload a File" %}</span>
<span class="oh-dropdown__import-form-text">{% trans "Drag and drop files here" %}</span>
</label>
<input type="file" name="file" id="projectImportFile" />
</div>
</div>
<div class="modal-footer d-flex flex-row-reverse">
<input
type="submit"
class="oh-btn oh-btn--secondary"
value='{% trans "Upload" %}'
/>
</div>
</form>
</div>
</div>
</div>
</div>
<script src="{% static 'cbv/deleteFunc.js' %}"></script>
<script src="{% static '/project/import.js' %}"></script>
<script src="{% static '/project/project_action.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,51 @@
{% load i18n %}
{% load taskfilters %}
<div onclick="event.stopPropagation();">
{% if request.user|task_crud_perm:instance or perms.project.view_task %}
<div class="oh-btn-group">
<a hx-get="{% url 'update-task-all' instance.id %}?instance_ids={{instance.ordered_ids}}"
hx-target='#genericModalBody' data-toggle='oh-modal-toggle' data-target="#genericModal" hx-swap='innerHTML'
class="oh-btn oh-btn--light-bkg w-100" title="Edit">
<ion-icon name="create-outline"></ion-icon>
</a>
{% if instance.is_active %}
<a href="{% url 'task-all-archive' instance.id %}"
onclick="return confirm('{% trans 'Do you want to archive this task?' %}')"
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg small-button" title="{% trans 'Archive' %}">
<ion-icon name="archive"></ion-icon>
</a>
{% else %}
<a href="{% url 'task-all-archive' instance.id %}"
onclick="return confirm('{% trans 'Do you want to un archive this task?' %}')"
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg small-button" title="{% trans 'Un-Archive' %}">
<ion-icon name="archive"></ion-icon>
</a>
{% endif %}
<form action="{% url 'delete-task' instance.id %}?task_all=true" method="post"
onsubmit="return confirm('{% trans "Do you want Delete this Task ?" %}')" style="display: contents">
{% csrf_token %}
<button class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100" title="{% trans 'Delete' %}">
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="trash outline"></ion-icon>
</button>
</form>
</div>
{% else %}
<div class="oh-btn-group">
<a href="#" class="oh-btn oh-btn--light-bkg w-100 oh-btn--disabled" title="Edit" aria-disabled="true">
<ion-icon name="create-outline"></ion-icon>
</a>
<button type="button" class="oh-btn oh-btn--danger-outline w-100 oh-btn--disabled" title="{% trans 'Delete' %}"
aria-disabled="true" disabled>
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="trash outline"></ion-icon>
</button>
<a href="#" class="oh-btn oh-btn--danger-outline oh-btn--light-bkg small-button oh-btn--disabled"
title="{% trans 'Archive' %}" aria-disabled="true">
<ion-icon name="archive"></ion-icon>
</a>
</div>
{% endif %}
</div>

View File

@@ -0,0 +1,27 @@
{% load i18n %}
{% if request.user|task_crud_perm:instance or perms.project.view_task %}
<a hx-get="{% url 'update-task' instance.id %}?project_task=true" hx-target='#genericModalBody'
data-toggle="oh-modal-toggle" data-target="#genericModal" data-key=".oh-kanban__section"
class="oh-btn oh-btn-group oh-btn--info" style="width: 50%;">
<ion-icon name="create-outline" role="img" class="md hydrated" aria-label="create outline">
</ion-icon>
{% trans "Edit" %}
</a>
{% endif %}
<a hx-get="{% url 'task-timesheet' instance.id %}?employee_id={{request.user.employee_get.id}}"
hx-target='#TaskTimesheetTarget' data-target="#TaskTimesheetModal" data-toggle="oh-modal-toggle"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info {% if not request.user|task_crud_perm:instance and not perms.project.view_task %} w-100 {% endif %}"
style="width: 56%; background-color:green">
<ion-icon name="bar-chart" role="img" class="md hydrated" aria-label="create outline">
</ion-icon>
{% trans "Time sheet" %}
</a>
{% if request.user|task_crud_perm:instance or perms.project.view_task %}
<a href="{% url 'delete-task' instance.id %}?view={{request.GET.view }}" class="oh-btn oh-btn-group oh-btn--danger"
style="width: 50%;"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)">
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="create outline"></ion-icon>
{% trans "Delete" %}
</a>
{% endif %}

View File

@@ -0,0 +1,10 @@
{% load i18n %}
{% if instance.document %}
<span class="oh-timeoff-modal__stat-title">{% trans "Document" %}</span>
<div class="d-flex ">
<a href="{{instance.document.url}}" rel="noopener noreferrer" target="_blank"><span
class="oh-file-icon oh-file-icon--pdf" onmouseover="enlargeattachment('{{instance.document.url}}')"
style="width:40px;height:40px"></span>
</a>
</div>
{% endif%}

View File

@@ -0,0 +1,41 @@
{% load i18n %} {% load basefilters %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');">
{% trans "Task" %}
</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{form.task_manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{form.project}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
{{form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<div>
<form hidden hx-get="{{request.path}}" hx-target="#id_stage_parent_div" hx-swap="outerHTML"
hx-select="#id_stage_parent_div, .dynamic_stage_scripts">
<input type="text" name="dynamic_project">
<button id="getStageButton" type="submit">
</button>
</form>
{% include 'generic/horilla_form.html' %}
</div>

View File

@@ -0,0 +1,125 @@
{% extends "index.html" %}
{% load i18n %}{% load static %}
{% block content %}
<style>
.expired--dot {
background-color: red;
}
.todo--dot {
background-color: yellowgreen;
}
.completed--dot {
background-color: rgb(103, 171, 238);
}
.in-progress--dot {
background-color: orange;
}
.status-completed {
border-left: 3.4px solid rgb(103, 171, 238) !important;
border-radius: 4px 0 0 4px;
}
.status-to_do {
border-left: 3.4px solid yellowgreen !important;
border-radius: 4px 0 0 4px;
}
.status-expired {
border-left: 3.4px solid red !important;
border-radius: 4px 0 0 4px;
}
.status-in_progress {
border-left: 3.4px solid orange; !important;
border-radius: 4px 0 0 4px;
}
</style>
<meta name="csrf-token" content="{{ csrf_token }}">
<div hx-get="{% url 'tasks-navbar' %}" hx-trigger="load">
</div>
{% include "generic/components.html" %}
<div class="oh-checkpoint-badge mb-2" id="selectedInstances" data-ids="[]" data-clicked="" style="display: none"></div>
<div class="oh-wrapper" id="listContainer">
<div class="animated-background">
</div>
</div>
<div class="oh-modal" id="TaskTimesheetModal" role="dialog" aria-labelledby="TaskTimesheetModal" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width:1350px" id="TaskTimesheetTarget"></div>
</div>
<div class="oh-modal" id="addTimesheetModal" role="dialog" aria-labelledby="addTimesheetModal" aria-hidden="true"
style="z-index: 1022;">
<div class="oh-modal__dialog" id="addTimesheetModalBody">
</div>
</div>
<script src="{% static '/task_all/task_all_action.js' %}"></script>
<script src="{% static 'cbv/deleteFunc.js' %}"></script>
<script>
function enlargeattachment(src, $element) {
$("#enlargeattachmentContainer").empty()
var enlargeattachmentContainer = $("#enlargeattachmentContainer")
enlargeattachmentContainer.empty()
style = 'width:100%; height:90%; box-shadow: 0 10px 10px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.2); background:white'
var enlargedImage = $('<iframe>').attr({ src: src, style: style })
var name = $('<span>').text(src.split('/').pop().replace(/_/g, ' '))
enlargeattachmentContainer.append(enlargedImage)
enlargeattachmentContainer.append(name)
setTimeout(function () {
enlargeattachmentContainer.show()
const iframe = document.querySelector('iframe').contentWindow
var iframe_document = iframe.document
iframe_image = iframe_document.getElementsByTagName('img')[0]
$(iframe_image).attr('style', 'width:100%; height:100%;')
}, 100)
}
function hideEnlargeattachment() {
var enlargeattachmentContainer = $('#enlargeattachmentContainer')
enlargeattachmentContainer.empty()
}
$(document).on('click', function (event) {
if (!$(event.target).closest('#enlargeattachmentContainer').length) {
hideEnlargeattachment()
}
})
</script>
{% endblock %}

View File

@@ -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 %}
<div class="oh-btn-group" onclick="event.stopPropagation();event.preventDefault()">
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="{% if request.task_id %}#addTimesheetModal{% else %}#genericModal{% endif %}"
hx-get="{% if request.task_id %}{% url 'update-time-sheet' instance.id %}?task_id={{ request.task_id }}{% else %}{% url 'update-time-sheet' instance.id %}{% endif %}"
hx-target="{% if request.task_id %}#addTimesheetModalBody{% else %}#genericModalBody{% endif %}"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
{% if request.task_id %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
hx-delete="{% url 'delete-time-sheet' instance.id %}?view=list&task_id={{ request.task_id }}&employee_id=request.user.employee_get.id"
hx-confirm="{% trans 'Do you want to delete this time sheet?' %}"
hx-target="#TaskTimesheetTarget"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
{% else %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-time-sheet' instance.id %}?view=list"
onclick="confirm(`{% trans 'Do you want to delete this time sheet?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
{% endif %}
</div>
{% else %}
<div class="oh-btn-group" onclick="event.stopPropagation();event.preventDefault()">
<a class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100 oh-btn--disabled" >
<ion-icon name="create-outline" role="img" class="md hydrated" style="color: blue" aria-label="create outline" ></ion-icon>
</a>
<a class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100 oh-btn--disabled">
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="trash outline" ></ion-icon>
</a>
</div>
{% endif %}

View File

@@ -0,0 +1,23 @@
{% load i18n %}
{% load basefilters %}
<div class="oh-btn-group w-100">
<a hx-get="{% url 'update-time-sheet' instance.id %}" hx-target="#genericModalBody" data-toggle="oh-modal-toggle"
data-target="#genericModal" class="oh-btn oh-btn--warning"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}">
<ion-icon name="create-outline" role="img" class="md hydrated" aria-label="create outline"></ion-icon>
{% trans "Edit" %}
</a>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<a href="{% url 'personal-time-sheet-view' instance.employee_id.id %}" class="oh-btn oh-btn--info me-1 ms-1"
style="width: 48%;">
<ion-icon name="bar-chart" role="img" class="md hydrated" aria-label="create outline"></ion-icon>
{% trans "View Timesheet Chart" %}
</a>
{% endif %}
<a href="{% url 'delete-time-sheet' instance.id %}" class="oh-btn oh-btn--danger"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)">
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="create outline"></ion-icon>
{% trans "Delete" %}
</a>
</div>

View File

@@ -0,0 +1,29 @@
{% load i18n %}
<label class="oh-label required-star" for="id_{{ form.employee_id.name }}"
title="{{ form.employee_id.help_text|safe }}">{% trans form.employee_id.label %}</label>
{{form.employee_id}}
<script>
$(document).ready(function () {
$("select").on("select2:select", function (e) {
$(this).closest("select")[0].dispatchEvent(new Event("change"));
});
});
var task_id = "{{ task_id }}"
if (task_id === "dynamic_create") {
var project_id = "{{ project_id }}"
var buttonId = "#modalButtontask_id"
var formId = "#modalButtontask_idForm"
var hxVals = {
project_id: project_id
};
// $(buttonId).attr('hx-vals', JSON.stringify(hxVals));
$(formId).attr('hx-vals', JSON.stringify(hxVals));
$(formId).attr('hx-target', "#dynamicModaltask_idBody");
$(buttonId).click();
}
</script>

View File

@@ -0,0 +1,59 @@
{% load i18n %}{% load basefilters%}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Time Sheet" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{form.project_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Task" %}</label>
{{form.task}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Date" %}</label>
{{form.date}}
</div>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Employee" %}</label>
{{form.employee_id}}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date From" %}</label>
{{form.start_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Till End Date" %}</label>
{{form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,3 @@
<div >
{% include "generic/horilla_form.html" %}
</div>

View File

@@ -0,0 +1,15 @@
{% load i18n %}
<label class="oh-label required-star" for="id_{{ form.task_id.title }}" title="{{ form.task_id.help_text|safe }}">
{% trans form.task_id.label %}
</label>
<div id="dynamic_field_task_id">
{{form.task_id}}
</div>
<script>
$(document).ready(function () {
$("select").on("select2:select", function (e) {
$(this).closest("select")[0].dispatchEvent(new Event("change"));
});
});
</script>

View File

@@ -0,0 +1,37 @@
{% load i18n %}
<div id="task-timesheet-container">
{% include "generic/components.html" %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">{% trans "Time sheet" %}</h5>
</span>
{% comment %} for add timesheet {% endcomment %}
<div class="d-flex flex-row-reverse oh-wrapper oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a class="oh-btn oh-btn--secondary oh-btn--shadow" data-toggle="oh-modal-toggle"
data-target="#addTimesheetModal" hx-get="{% url 'create-timesheet-task' task_id %}"
hx-target="#addTimesheetModalBody">
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans " Add" %}
</a>
</div>
</div>
</div>
<button type="button" style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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="close">
<ion-icon name="close-outline"></ion-icon>
</button>
{% include "generic/horilla_list_table.html" %}
</div>
</div>

View File

@@ -0,0 +1,51 @@
{% extends "index.html" %}
{% load i18n %}{% load static %}
{% block content %}
<style>
.completed--dot {
background-color: yellowgreen;
}
.in-progress--dot {
background-color: orange;
}
.new--dot {
background-color: cyan;
}
.status-completed {
border-left: 3.4px solid yellowgreen !important;
border-radius: 4px 0 0 4px;
}
.status-in_Progress {
border-left: 3.4px solid orange !important;
border-radius: 4px 0 0 4px;
}
</style>
<meta name="csrf-token" content="{{ csrf_token }}">
<div hx-get="{% url " time-sheet-nav" %}" hx-trigger="load">
</div>
{% include "generic/components.html" %}
<div class="oh-checkpoint-badge mb-2" id="selectedInstances" data-ids="[]" data-clicked="" style="display: none"></div>
<div class="oh-wrapper" id="listContainer">
<div class="animated-background"></div>
</div>
<script src="{% static 'cbv/deleteFunc.js' %}"></script>
<script src="{% static 'time_sheet/time_sheet_action.js' %}"></script>
{% endblock %}

View File

@@ -0,0 +1,8 @@
{% load i18n %}
<div id="stage-container">
{% include "generic/horilla_nav.html" %}
</div>
<script>
$('#applyFilter').closest('form').find('[name=field]').val('employee_id');
$('#applyFilter').click();
</script>

View File

@@ -1,152 +1,143 @@
{% extends 'index.html' %}
{% block content %}
{% load static i18n %}
{% load i18n %}
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
<div class="oh-dashboard row">
<div class="oh-dashboard__left col-12 col-sm-12 col-md-12 col-lg-9">
<div class="oh-dashboard__cards row">
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--success">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "Total Projects" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'objective-list-view' %}" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{total_projects}}</span>
</a>
</div>
</div>
</div>
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--neutral">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "New Projects" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'objective-list-view' %}" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{new_projects}}</span>
</a>
</div>
</div>
</div>
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--warning">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "Projects in progress" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'objective-list-view' %}" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{projects_in_progress}}</span>
</a>
</div>
</div>
</div>
</div>
<!-- movable cards for charts -->
<div class="oh-dashboard__movable-cards row mt-4">
<!-- chart -->
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title">{% trans "Project Status" %}</span>
<span class="oh-card-dashboard__title float-end" id="projectStatusForward"><ion-icon name="caret-forward"></ion-icon></span>
</div>
<div class="oh-card-dashboard__body">
<canvas id="projectStatusCanvas"></canvas>
</div>
</div>
</div>
<!-- chart -->
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title">{% trans "Task Status" %}</span>
<span class="oh-card-dashboard__title float-end" id="taskStatusForward"><ion-icon name="caret-forward"></ion-icon></span>
</div>
<div class="oh-card-dashboard__body">
<canvas id="taskStatusCanvas"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Right end column -->
<div class="oh-dashboard__right col-12 col-sm-12 col-md-12 col-lg-3">
<!-- Dashboard events -->
<div class="oh-dashboard__events">
<ul class="oh-dashboard__events-nav">
<li class="oh-dashboard__events-nav-item oh-dashboard__events-nav-item--active" data-target="0">
</li>
<li class="oh-dashboard__events-nav-item" data-target="1"></li>
<li class="oh-dashboard__events-nav-item" data-target="2"></li>
</ul>
</div>
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent mt-3">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title"> {% trans "Projects due in this month" %}</span>
<span class="oh-card-dashboard__title float-end"><a href="{% url 'project-view' %}" style="text-decoration:none; color:orange;">{% trans "View all" %}</a></span>
</div>
<div class="oh-card-dashboard__body">
<ul class="oh-card-dashboard__user-list">
{% if unexpired_project %}
{% for project in unexpired_project %}
<li class="oh-card-dashboard__user-item">
<a
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
hx-get="{%url 'project-detailed-view' project.id %}"
hx-target="#ProjectFormTarget"
>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="https://ui-avatars.com/api/?name=Beth+Gibbons&background=random"
class="oh-profile__image" alt="Beth Gibbons" />
</div>
<span class="oh-profile__name oh-text--dark">{{project.title}}</span>
</div>
</a>
</li>
{% endfor %}
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 70px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:16px" class="oh-404__subtitle">{% trans "No projects due in this month." %}</h3>
</div>
</div>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
</main>
<!-- Java script -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="{% static 'dashboard/projectChart.js' %}"></script>
{% endblock content %}
{% extends 'index.html' %}
{% block content %}
{% load static i18n %}
{% load i18n %}
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
<div class="oh-wrapper">
<div class="oh-dashboard row">
<div class="oh-dashboard__left col-12 col-sm-12 col-md-12 col-lg-9">
<div class="oh-dashboard__cards row">
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--success">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "Total Projects" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'project-view' %}" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{total_projects}}</span>
</a>
</div>
</div>
</div>
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--neutral">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "New Projects" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'project-view' %}?{{request.GET.urlencode}}&status=new" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{new_projects}}</span>
</a>
</div>
</div>
</div>
<!-- column -->
<div class="col-12 col-sm-12 col-md-6 col-lg-4">
<div class="oh-card-dashboard oh-card-dashboard oh-card-dashboard--warning">
<div class="oh-card-dashboard__header">
<span class="oh-card-dashboard__title">{% trans "Projects in progress" %}</span>
</div>
<div class="oh-card-dashboard__body">
<a href="{% url 'project-view' %}?{{request.GET.urlencode}}&status=in_progress" style="text-decoration: none;" class="oh-card-dashboard__counts">
<span class="oh-card-dashboard__count">{{projects_in_progress}}</span>
</a>
</div>
</div>
</div>
</div>
<!-- movable cards for charts -->
<div class="oh-dashboard__movable-cards row mt-4">
<!-- chart -->
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title">{% trans "Project Status" %}</span>
<span class="oh-card-dashboard__title float-end" id="projectStatusForward"><ion-icon name="caret-forward"></ion-icon></span>
</div>
<div class="oh-card-dashboard__body">
<canvas id="projectStatusCanvas"></canvas>
</div>
</div>
</div>
<!-- chart -->
<div class="col-12 col-sm-12 col-md-12 col-lg-6 oh-card-dashboard--moveable">
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title">{% trans "Task Status" %}</span>
<span class="oh-card-dashboard__title float-end" id="taskStatusForward"><ion-icon name="caret-forward"></ion-icon></span>
</div>
<div class="oh-card-dashboard__body">
<canvas id="taskStatusCanvas"></canvas>
</div>
</div>
</div>
</div>
</div>
<!-- Right end column -->
<div class="oh-dashboard__right col-12 col-sm-12 col-md-12 col-lg-3">
<!-- Dashboard events -->
<div class="oh-dashboard__events">
<ul class="oh-dashboard__events-nav">
<li class="oh-dashboard__events-nav-item oh-dabdshboard__events-nav-item--active" data-target="0">
</li>
<li class="oh-dashboard__events-nav-item" data-target="1"></li>
<li class="oh-dashboard__events-nav-item" data-target="2"></li>
</ul>
</div>
<div class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent mt-3">
<div class="oh-card-dashboard__header oh-card-dashboard__header--divider">
<span class="oh-card-dashboard__title"> {% trans "Projects due in this month" %}</span>
</div>
<div class="oh-card-dashboard__body">
<div class="oh-sticky-table h-100" hx-get="{% url 'projects-due-in-this-month' %}" hx-trigger="load" style="border: none;">
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectModalShow"
role="dialog"
aria-labelledby="ProjectModalShow"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
>
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title">{% trans 'Projects' %}</h2>
<button type="button" class="oh-modal__close--custom" onclick="$(this).closest('.oh-modal--show').removeClass('oh-modal--show');">
<ion-icon name="close-outline" role="img" aria-label="close outline"></ion-icon>
</button>
</div>
<div class="oh-modal__dialog-body" id="ProjectModalShowTarget">
</div>
</div>
</main>
<!-- Java script -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="{% static 'dashboard/projectChart.js' %}"></script>
{% endblock content %}

View File

@@ -1,71 +1,71 @@
{% load i18n %} {% load yes_no %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="projectDetails">
<h5 style="margin-bottom: 20px;">{{project.title}}</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Manager" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.manager}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Members" %}</span>
<span class="oh-timeoff-modal__stat-count">{% for member in project.members.all %}{{member}}, {% endfor %}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.get_status_display}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "No of Tasks" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task_count}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Start Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.start_date}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "End date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.end_date}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Document" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.document}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.description}}</span>
</div>
</div>
<div class="oh-modal__button-container text-center" style="width: 50%; margin:0 auto">
{% comment %} <div class="oh-btn-group"> {% endcomment %}
<a href="{%url 'task-view' project.id %}"
class="oh-btn oh-btn--info" >
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans "View" %}
</a>
{% comment %} </div> {% endcomment %}
</div>
</div>
{% load i18n %} {% load horillafilters %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="projectDetails">
<h5 style="margin-bottom: 20px;">{{project.title}}</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Manager" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.manager}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Members" %}</span>
<span class="oh-timeoff-modal__stat-count">{% for member in project.members.all %}{{member}}, {% endfor %}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.get_status_display}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "No of Tasks" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task_count}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Start Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.start_date}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "End date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.end_date}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Document" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.document}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{project.description}}</span>
</div>
</div>
<div class="oh-modal__button-container text-center" style="width: 50%; margin:0 auto">
{% comment %} <div class="oh-btn-group"> {% endcomment %}
<a href="{%url 'task-view' project.id %}"
class="oh-btn oh-btn--info" >
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans "View" %}
</a>
{% comment %} </div> {% endcomment %}
</div>
</div>

View File

@@ -1,44 +1,44 @@
{% load i18n %} {% load basefilters %}
{% comment %} <form
hx-get='{% url "project-filter" %}?view={{request.GET.view }}'
hx-target="#view-container"
hx-swap="innerHTML"
id="projectFilterForm"> {% endcomment %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');"
>{% trans "Project" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{f.form.manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start from" %}</label>
{{f.form.start_from}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton" type="submit" >
{% trans "Filter" %}
</button>
</div>
{% comment %} </form> {% endcomment %}
{% load i18n %} {% load basefilters %}
{% comment %} <form
hx-get='{% url "project-filter" %}?view={{request.GET.view }}'
hx-target="#view-container"
hx-swap="innerHTML"
id="projectFilterForm"> {% endcomment %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');"
>{% trans "Project" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{f.form.manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start from" %}</label>
{{f.form.start_from}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton" type="submit" >
{% trans "Filter" %}
</button>
</div>
{% comment %} </form> {% endcomment %}

View File

@@ -1,30 +1,30 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskStageTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-project' %}"
hx-target="#ProjectFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskStageTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-project' %}"
hx-target="#ProjectFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,30 +1,30 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#ProjectFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-project' project_id %}"
hx-target="#ProjectFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#ProjectFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-project' project_id %}"
hx-target="#ProjectFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,235 +1,234 @@
{% load i18n %}
<!-- start of import form -->
<div
class="oh-modal"
id="projectImport"
role="dialog"
aria-labelledby="projectImport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="projectImportLavel">
{% trans "Import Project" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-modal__dialog-body p-0 pt-2" id="projectImportModalBody">
<form
id="projectImportForm"
enctype="multipart/form-data"
>
<div id="loading" style="display: none;">
<div id="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<p id="progress-text">{% trans "Uploading..." %}</p>
</div>
<div id="error-container" style='color:red;'></div>
<div class="modal-body">
{% csrf_token %}
{% comment %} {% endcomment %}
<div class="oh-dropdown__import-form">
<label class="oh-dropdown__import-label" for="uploadFile">
<ion-icon name="cloud-upload" class="oh-dropdown__import-form-icon md hydrated" role="img" aria-label="cloud upload"></ion-icon>
<span class="oh-dropdown__import-form-title">{% trans "Upload a File" %}</span>
<span class="oh-dropdown__import-form-text">{% trans "Drag and drop files here" %}</span>
</label>
<input type="file" name="file" id="projectImportFile" />
</div>
</div>
<div class="modal-footer d-flex flex-row-reverse">
<input
type="submit"
class="oh-btn oh-btn--secondary"
value='{% trans "Upload" %}'
/>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- end of import form -->
<!-- start of navbar contents -->
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Projects" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% if projects %}
{% comment %} for search{% endcomment %}
<form
hx-get='{% url "project-filter" %}?view={{request.GET.view }}'
hx-target="#view-container"
hx-swap="innerHTML"
id="projectFilterForm"
class="d-flex"
onsubmit="event.preventDefault()"
>
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-project"
name="search"
placeholder="{% trans 'Search' %}"
onkeyup="$('.filterButton')[0].click()"
/>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
{% comment %} for list view {% endcomment %}
<li class="oh-view-type project-view-type" data-view="list">
<a
href="{% url 'project-view' %}?view=list"
{% comment %} hx-target="#view-container" {% endcomment %}
class="oh-btn oh-btn--view"
><ion-icon name="list-outline"></ion-icon
></a>
</li>
{% comment %} for card view {% endcomment %}
<li class="oh-view-type project-view-type" data-view="card">
<a
href="{% url 'project-view' %}?view=card"
{% comment %} hx-target="#view-container" {% endcomment %}
class="oh-btn oh-btn--view"
><ion-icon name="grid-outline"></ion-icon
></a>
</li>
</ul>
</div>
{% comment %} for filtering {% endcomment %}
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn ml-2"
@click="open = !open"
onclick="event.preventDefault()"
>
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'project/new/filter_project.html' %}
</div>
</div>
</form>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#"
class="oh-dropdown__link "
id="importProject"
data-toggle="oh-modal-toggle"
data-target="#projectImport"
>{% trans "Import" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#"
class="oh-dropdown__link "
id="exportProject"
>{% trans "Export" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="duplicateProject"
>{% trans "Duplicate" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveProject"
>{% trans "Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveProject"
>{% trans "Un-Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteProject"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
{% endif %}
{% comment %} for create project {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
hx-get="{% url 'create-project' %}"
hx-target="#ProjectFormTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
</div>
</section>
{% load i18n %}
<!-- start of import form -->
<div
class="oh-modal"
id="projectImport"
role="dialog"
aria-labelledby="projectImport"
aria-hidden="true"
>
<div class="oh-modal__dialog">
<div class="oh-modal__dialog-header">
<h2 class="oh-modal__dialog-title" id="projectImportLavel">
{% trans "Import Project" %}
</h2>
<button class="oh-modal__close" aria-label="Close">
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-modal__dialog-body p-0 pt-2" id="projectImportModalBody">
<form
id="projectImportForm"
enctype="multipart/form-data"
>
<div id="loading" style="display: none;">
<div id="progress">
<div class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<p id="progress-text">{% trans "Uploading..." %}</p>
</div>
<div id="error-container" style='color:red;'></div>
<div class="modal-body">
{% csrf_token %}
{% comment %} {% endcomment %}
<div class="oh-dropdown__import-form">
<label class="oh-dropdown__import-label" for="uploadFile">
<ion-icon name="cloud-upload" class="oh-dropdown__import-form-icon md hydrated" role="img" aria-label="cloud upload"></ion-icon>
<span class="oh-dropdown__import-form-title">{% trans "Upload a File" %}</span>
<span class="oh-dropdown__import-form-text">{% trans "Drag and drop files here" %}</span>
</label>
<input type="file" name="file" id="projectImportFile" />
</div>
</div>
<div class="modal-footer d-flex flex-row-reverse">
<input
type="submit"
class="oh-btn oh-btn--secondary"
value='{% trans "Upload" %}'
/>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- end of import form -->
<!-- start of navbar contents -->
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Projects" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% if projects %}
{% comment %} for search{% endcomment %}
<form
hx-get='{% url "project-filter" %}?view={{request.GET.view }}'
hx-target="#view-container"
hx-swap="innerHTML"
id="projectFilterForm"
class="d-flex"
onsubmit="event.preventDefault()"
>
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-project"
name="search"
placeholder="{% trans 'Search' %}"
onkeyup="$('.filterButton')[0].click()"
/>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
{% comment %} for list view {% endcomment %}
<li class="oh-view-type project-view-type" data-view="list">
<a
href="{% url 'project-view' %}?view=list"
{% comment %} hx-target="#view-container" {% endcomment %}
class="oh-btn oh-btn--view"
><ion-icon name="list-outline"></ion-icon
></a>
</li>
{% comment %} for card view {% endcomment %}
<li class="oh-view-type project-view-type" data-view="card">
<a
href="{% url 'project-view' %}?view=card"
{% comment %} hx-target="#view-container" {% endcomment %}
class="oh-btn oh-btn--view"
><ion-icon name="grid-outline"></ion-icon
></a>
</li>
</ul>
</div>
{% comment %} for filtering {% endcomment %}
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn ml-2"
@click="open = !open"
onclick="event.preventDefault()"
>
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'project/new/filter_project.html' %}
</div>
</div>
</form>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#"
class="oh-dropdown__link "
id="importProject"
data-toggle="oh-modal-toggle"
data-target="#projectImport"
>{% trans "Import" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#"
class="oh-dropdown__link "
id="exportProject"
>{% trans "Export" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="duplicateProject"
>{% trans "Duplicate" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveProject"
>{% trans "Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveProject"
>{% trans "Un-Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteProject"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
{% endif %}
{% comment %} for create project {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
hx-get="{% url 'create-project' %}"
hx-target="#ProjectFormTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
</div>
</section>

View File

@@ -1,34 +1,33 @@
{% extends 'index.html' %}
{% load static %}
{% block content %}
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'project/new/navbar.html' %}
</main>
<div id="view-container" class="oh-wrapper">
{% if view_type == 'list' %}
{% include 'project/new/project_list_view.html' %}
{% else %}
{% include 'project/new/project_kanban_view.html' %}
{% endif %}
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
{% endblock content %}
{% extends 'index.html' %}
{% load static %}
{% block content %}
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'project/new/navbar.html' %}
</main>
<div id="view-container" class="oh-wrapper">
{% if view_type == 'list' %}
{% include 'project/new/project_list_view.html' %}
{% else %}
{% include 'project/new/project_kanban_view.html' %}
{% endif %}
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
{% endblock content %}

View File

@@ -1,201 +1,201 @@
{% load static %}
{% load i18n %}
{% load basefilters %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=new&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:cyan"></span>
{% trans "New" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=in_progress&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:rgb(103, 171, 238)"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=completed&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "Completed" %}
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=on_hold&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:orange"></span>
{% trans "On Hold" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=cancelled&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Red"></span>
{% trans "Cancelled" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=expired&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:gray"></span>
{% trans "Expired" %}
</span>
</div>
{% comment %} kanban view {% endcomment %}
{% if projects %}
<div class="oh-layout--grid-3">
{% for project in projects %}
<div class="oh-kanban-card {% if project.status == 'new'%} row-status--cyan
{% elif project.status == 'in_progress' %} row-status--blue
{% elif project.status == 'completed' %} row-status--yellow
{% elif project.status == 'on_hold' %} row-status--orange
{% elif project.status == 'cancelled' %} row-status--red
{% else %} row-status--gray
{% endif %}"
style="color: inherit;text-decoration: none;">
{% comment %} url link {% endcomment %}
<a href="{% url 'task-view' project.id %}?view=card" style="color: inherit;text-decoration: none; display: flex;width:600px;">
{% comment %} placing image {% endcomment %}
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
{% if project.image %}
<img
src="{{emp.employee_profile.url}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
{% else %}
<img
src="https://ui-avatars.com/api/?name={{project.title}}&background=random"
class="oh-kanban-card__profile-image"
alt="Username"
/>
{% endif %}
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{project.title}}</span>
<span class="oh-kanban-card__subtitle">{% trans "Project manager" %} : {{project.manager}}</span><br>
<span class="oh-kanban-card__subtitle"> {% trans "Status" %}: {{project.get_status_display}}</span><br>
<span class="oh-kanban-card__subtitle">{% trans "End date" %} : {{project.end_date}}</span>
</div>
</a>
<div class="oh-tabs__input-badge-container">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1" title="{{ project.task_set.all|length}} Tasks">
{{ project.task_set.all|length}}
</span>
</div>
<div class="oh-kanban-card__dots">
{% comment %} dropdown {% endcomment %}
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
{% comment %} dropdown menu {% endcomment %}
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.project.change_project %} {% endcomment %}
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-project" project.id %}'
hx-target='#ProjectFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.employee.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
{% if project.is_active %}
<a href="{% url 'project-archive' project.id %}" onclick="return confirm('{% trans 'Do you want to archive this project?' %}')" class="oh-dropdown__link">
{% trans "Archive" %}
</a>
{% else %}
<a href="{% url 'project-archive' project.id %}" onclick="return confirm('{% trans 'Do you want to un archive this project?' %}')" class="oh-dropdown__link">
{% trans "Un-Archive" %}
</a>
{% endif %}
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.employee.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<form action="{% url 'delete-project' project.id %}?view=card" method="post" onsubmit="return confirm('{% trans 'Do you want to delete this employee?' %}')">
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</button>
</form>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% comment %} pagination {% endcomment %}
<div class="oh-pagination">
<span
class="oh-pagination__page"
>
{% trans "Page" %} {{ projects.number }} {% trans "of" %} {{ projects.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{projects.number}}"
hx-get="{% url 'project-filter' %}?view=card?page="
hx-target="#view-container"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{projects.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if projects.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page=1" class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.previous_page_number }}" class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if projects.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.next_page_number }}" class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.paginator.num_pages }}" class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% comment %} </div> {% endcomment %}
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available projects; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% comment %} js scripts {% endcomment %}
<script src="{% static '/project/project_view.js' %}"></script>
<script src="{% static '/project/import.js' %}"></script>
{% load static %}
{% load i18n %}{% load horillafilters %}
{% load basefilters %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=new&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:cyan"></span>
{% trans "New" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=in_progress&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:rgb(103, 171, 238)"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=completed&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "Completed" %}
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=on_hold&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:orange"></span>
{% trans "On Hold" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=cancelled&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Red"></span>
{% trans "Cancelled" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=expired&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:gray"></span>
{% trans "Expired" %}
</span>
</div>
{% comment %} kanban view {% endcomment %}
{% if projects %}
<div class="oh-layout--grid-3">
{% for project in projects %}
<div class="oh-kanban-card {% if project.status == 'new'%} row-status--cyan
{% elif project.status == 'in_progress' %} row-status--blue
{% elif project.status == 'completed' %} row-status--yellow
{% elif project.status == 'on_hold' %} row-status--orange
{% elif project.status == 'cancelled' %} row-status--red
{% else %} row-status--gray
{% endif %}"
style="color: inherit;text-decoration: none;">
{% comment %} url link {% endcomment %}
<a href="{% url 'task-view' project.id %}?view=card" style="color: inherit;text-decoration: none; display: flex;width:600px;">
{% comment %} placing image {% endcomment %}
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
{% if project.image %}
<img
src="{{emp.employee_profile.url}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
{% else %}
<img
src="https://ui-avatars.com/api/?name={{project.title}}&background=random"
class="oh-kanban-card__profile-image"
alt="Username"
/>
{% endif %}
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{project.title}}</span>
<span class="oh-kanban-card__subtitle">{% trans "Project manager" %} : {{project.manager}}</span><br>
<span class="oh-kanban-card__subtitle"> {% trans "Status" %}: {{project.get_status_display}}</span><br>
<span class="oh-kanban-card__subtitle">{% trans "End date" %} : {{project.end_date}}</span>
</div>
</a>
<div class="oh-tabs__input-badge-container">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1" title="{{ project.task_set.all|length}} Tasks">
{{ project.task_set.all|length}}
</span>
</div>
<div class="oh-kanban-card__dots">
{% comment %} dropdown {% endcomment %}
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
{% comment %} dropdown menu {% endcomment %}
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.project.change_project %} {% endcomment %}
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-project" project.id %}'
hx-target='#ProjectFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.employee.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
{% if project.is_active %}
<a href="{% url 'project-archive' project.id %}" onclick="return confirm('{% trans 'Do you want to archive this project?' %}')" class="oh-dropdown__link">
{% trans "Archive" %}
</a>
{% else %}
<a href="{% url 'project-archive' project.id %}" onclick="return confirm('{% trans 'Do you want to un archive this project?' %}')" class="oh-dropdown__link">
{% trans "Un-Archive" %}
</a>
{% endif %}
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.employee.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<form action="{% url 'delete-project' project.id %}?view=card" method="post" onsubmit="return confirm('{% trans 'Do you want to delete this employee?' %}')">
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</button>
</form>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% comment %} pagination {% endcomment %}
<div class="oh-pagination">
<span
class="oh-pagination__page"
>
{% trans "Page" %} {{ projects.number }} {% trans "of" %} {{ projects.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{projects.number}}"
hx-get="{% url 'project-filter' %}?view=card?page="
hx-target="#view-container"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{projects.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if projects.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page=1" class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.previous_page_number }}" class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if projects.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.next_page_number }}" class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#view-container' hx-get="{% url 'project-filter' %}?view=card&?{{pd}}&page={{ projects.paginator.num_pages }}" class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% comment %} </div> {% endcomment %}
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available projects; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% comment %} js scripts {% endcomment %}
<script src="{% static '/project/project_view.js' %}"></script>
<script src="{% static '/project/import.js' %}"></script>

View File

@@ -1,236 +1,236 @@
{% load static %}
{% load i18n %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
<div id="view-container">
{% if projects %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=new&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:cyan"></span>
{% trans "New" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=in_progress&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:rgb(103, 171, 238)"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=completed&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "Completed" %}
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=on_hold&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:orange"></span>
{% trans "On Hold" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=cancelled&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Red"></span>
{% trans "Cancelled" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=expired&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:gray"></span>
{% trans "Expired" %}
</span>
</div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input
type="checkbox"
class="oh-input oh-input__checkbox mt-1 mr-2 all-projects"
title='{% trans "Select All" %}'
/>
</div>
<div
hx-target="#section"
hx-get="{% url 'search-candidate' %}?{{pd}}&orderby=name&view=list"
>
{% trans "Project" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Project Manager" %}</div>
<div class="oh-sticky-table__th">{% trans "Project Members" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Start Date" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "File" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th"></div>
</div>
</div>
{% for project in projects %}
<div class="oh-sticky-table__tr ui-sortable-handle" data-url="{% url 'task-view' project.id %}?view=list">
<div class="oh-sticky-table__sd {% if project.status == 'new'%} row-status--cyan
{% elif project.status == 'in_progress' %} row-status--blue
{% elif project.status == 'completed' %} row-status--yellow
{% elif project.status == 'on_hold' %} row-status--orange
{% elif project.status == 'cancelled' %} row-status--red
{% else %} row-status--gray
{% endif %}" >
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{project.id}}"
class="oh-input oh-input__checkbox mt-1 mr-2 all-project-row"
/>
{{project.title}}
</div>
</div>
<div class="oh-sticky-table__td">{{project.manager}}</div>
<div class="oh-sticky-table__td">
{% for employee in project.members.all %} {{employee}}<br />
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{project.get_status_display}}</div>
<div class="oh-sticky-table__td">{{project.start_date}}</div>
<div class="oh-sticky-table__td">{% if project.end_date %}{{project.end_date}}{% endif %}</div>
<div class="oh-sticky-table__td">{% if project.document %}<a href="{{ project.document.url }}" target="_blank" onclick="event.stopPropagation();"><img style="width: 25px;margin: 5px auto ;" src="{% static 'images/ui/project/document.png' %}" class="" alt="document"/>{% endif %}</a></div>
<div class="oh-sticky-table__td">{{project.description}}</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
hx-get="{% url 'update-project' project.id %}"
hx-target="#ProjectFormTarget"
onclick="event.stopPropagation();"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-project' project.id %}?view=list"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available projects; please create a new one." %}</h3>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% comment %} <div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ allowances.number }} {% trans "of" %} {{
allowances.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{allowances.number}}"
hx-get="{% url 'filter-allowance' %}?{{pd}}"
hx-target="#payroll-allowance-container"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{allowances.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if allowances.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if allowances.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div> {% endcomment %}
</div>
{% comment %} js scripts {% endcomment %}
<script src="{% static '/project/project_view.js' %}"></script>
<script>
const tableRows = document.querySelectorAll('.oh-sticky-table__tr');
tableRows.forEach(row => {
row.addEventListener('click', event => {
if (event.target.closest('.oh-sticky-table__thead')) {
return;
}
const url = row.getAttribute('data-url');
window.location.href = url;
});
});
</script>
<script src="{% static '/project/project_action.js' %}"></script>
<script src="{% static '/project/import.js' %}"></script>
{% load static %}
{% load i18n %} {% load horillafilters %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
<div id="view-container">
{% if projects %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=new&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:cyan"></span>
{% trans "New" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=in_progress&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:rgb(103, 171, 238)"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=completed&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "Completed" %}
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=on_hold&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:orange"></span>
{% trans "On Hold" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=cancelled&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Red"></span>
{% trans "Cancelled" %}
</span>
</span><span class="m-3 draft" hx-get="{% url 'project-filter' %}?{{pd}}&status=expired&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:gray"></span>
{% trans "Expired" %}
</span>
</div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input
type="checkbox"
class="oh-input oh-input__checkbox mt-1 mr-2 all-projects"
title='{% trans "Select All" %}'
/>
</div>
<div
hx-target="#section"
hx-get="{% url 'search-candidate' %}?{{pd}}&orderby=name&view=list"
>
{% trans "Project" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Project Manager" %}</div>
<div class="oh-sticky-table__th">{% trans "Project Members" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Start Date" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "File" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th"></div>
</div>
</div>
{% for project in projects %}
<div class="oh-sticky-table__tr ui-sortable-handle" data-url="{% url 'task-view' project.id %}?view=list">
<div class="oh-sticky-table__sd {% if project.status == 'new'%} row-status--cyan
{% elif project.status == 'in_progress' %} row-status--blue
{% elif project.status == 'completed' %} row-status--yellow
{% elif project.status == 'on_hold' %} row-status--orange
{% elif project.status == 'cancelled' %} row-status--red
{% else %} row-status--gray
{% endif %}" >
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{project.id}}"
class="oh-input oh-input__checkbox mt-1 mr-2 all-project-row"
/>
{{project.title}}
</div>
</div>
<div class="oh-sticky-table__td">{{project.manager}}</div>
<div class="oh-sticky-table__td">
{% for employee in project.members.all %} {{employee}}<br />
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{project.get_status_display}}</div>
<div class="oh-sticky-table__td">{{project.start_date}}</div>
<div class="oh-sticky-table__td">{% if project.end_date %}{{project.end_date}}{% endif %}</div>
<div class="oh-sticky-table__td">{% if project.document %}<a href="{{ project.document.url }}" target="_blank" onclick="event.stopPropagation();"><img style="width: 25px;margin: 5px auto ;" src="{% static 'images/ui/project/document.png' %}" class="" alt="document"/>{% endif %}</a></div>
<div class="oh-sticky-table__td">{{project.description}}</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#ProjectModal"
hx-get="{% url 'update-project' project.id %}"
hx-target="#ProjectFormTarget"
onclick="event.stopPropagation();"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-project' project.id %}?view=list"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
</div>
</div>
</div>
{% endfor %}
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/project.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available projects; please create a new one." %}</h3>
</div>
</div>
{% endif %}
</div>
</div>
</div>
{% comment %} <div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ allowances.number }} {% trans "of" %} {{
allowances.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{allowances.number}}"
hx-get="{% url 'filter-allowance' %}?{{pd}}"
hx-target="#payroll-allowance-container"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{allowances.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if allowances.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if allowances.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#payroll-allowance-container"
hx-get="{% url 'filter-allowance' %}?{{pd}}&page={{ allowances.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div> {% endcomment %}
</div>
{% comment %} js scripts {% endcomment %}
<script src="{% static '/project/project_view.js' %}"></script>
<script>
const tableRows = document.querySelectorAll('.oh-sticky-table__tr');
tableRows.forEach(row => {
row.addEventListener('click', event => {
if (event.target.closest('.oh-sticky-table__thead')) {
return;
}
const url = row.getAttribute('data-url');
window.location.href = url;
});
});
</script>
<script src="{% static '/project/project_action.js' %}"></script>
<script src="{% static '/project/import.js' %}"></script>

View File

@@ -1,30 +1,30 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project Stage" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-project-stage' project_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project Stage" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-project-stage' project_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,30 +1,30 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project Stage" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-project-stage' stage_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}<span>{{form.errorList}}</span>
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Project Stage" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-project-stage' stage_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}<span>{{form.errorList}}</span>
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,43 +1,44 @@
{% load i18n %} {% load basefilters %}
<form
hx-get='{% url "task-filter" project_id %}?view={{request.GET.view}}'
hx-target="#view-container"
hx-swap="innerHTML"
id="taskFilterForm">
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Task" %}</div>
<div class="oh-accordion-body">
<div class="row">
<input type="text" value="{{project_id}}" name="project" style="display:none;">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{f.form.task_manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{f.form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton">
{% trans "Filter" %}
</button>
</div>
</form>
{% load i18n %} {% load basefilters %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Task" %}</div>
<div class="oh-accordion-body">
<div class="row">
<input type="text" value="{{project_id}}" name="project" style="display:none;">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Managers" %}</label>
{{f.form.task_managers}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{f.form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Members" %}</label>
{{f.form.task_members}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End till" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton"
id="taskFilterButton">
{% trans "Filter" %}
</button>
</div>

View File

@@ -1,30 +1,30 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-task' stage_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-task' stage_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,55 +1,55 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-task-in-project' project_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
</form>
</div>
<script>
$(document).ready(function(){
var data = {{ stages|safe }};
$('#project_stage').html('<options>--------</options>')
// Iterate through the data array and create options
$.each(data, function(index, stage) {
$("#project_stage").append("<option value=" + stage.pk + ">" + stage.fields.title + "</option>");
});
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
$("#project_stage").append( "<option value='old_stage'>Old Stage</option>");
});
$("#project_stage").change(function(e){
stage_id = $(this).val()
if (stage_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: {{project_id}} },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
</script>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-task-in-project' project_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
</form>
</div>
<script>
$(document).ready(function(){
var data = {{ stages|safe }};
$('#project_stage').html('<options>--------</options>')
// Iterate through the data array and create options
$.each(data, function(index, stage) {
$("#project_stage").append("<option value=" + stage.pk + ">" + stage.fields.title + "</option>");
});
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
$("#project_stage").append( "<option value='old_stage'>Old Stage</option>");
});
$("#project_stage").change(function(e){
stage_id = $(this).val()
if (stage_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: {{project_id}} },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
</script>

View File

@@ -1,38 +1,38 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Timesheet" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-timesheet-task' task_id %}"
hx-target="#TimesheetUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Timesheet" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-timesheet-task' task_id %}"
hx-target="#TimesheetUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -0,0 +1,26 @@
<form class="oh-kanban__add-card-form" hx-post="{% url 'quick-create-task' stage_id %}" hx-target="#{{hx_target}}">
{% if form.errors %}
<ul class="errorlist nonfield">
{% for error in form.non_field_errors %}
<li>
{{ error }}
</li>
{% endfor %}
</ul>
{% endif %}
{{form.as_p}}
<div class="d-flex align-items-center justify-content-end">
<a role="button"
class="oh-btn oh-btn--light-dark-text oh-btn--x-small oh-kanban__add-card-cancel-btn me-2 mt-2">Cancel</a>
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--x-small mt-2">Add</button>
</div>
</form>
<script>
$(document).ready(function (e) {
$('.oh-kanban__add-card-cancel-btn').click(function (e) {
e.stopPropagation();
$(this).closest('.oh-kanban__add-card-container').addClass('d-none');
$(this).closest('.list-quick-add').toggleClass('d-none');
});
});
</script>

View File

@@ -1,55 +1,55 @@
{% load i18n %}
<div class="oh-modal__dialog-header ">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task' task_id %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<!-- <script>
$(document).ready(function(){
console.log('ready modal')
$('#close1').click(function(e){
e.preventDefault()
e.stopPropagation()
var closestModal = $(this).closest('.oh-modal--show');
console.log(closestModal);
closestModal.removeClass('oh-modal--show')
// e.preventDefault()
// var parentDiv = $(this).parents().closest('oh-modal--show')
// console.log(parentDiv)
// parentDiv.classList.remove('oh-modal--show')
})
});
</script> -->
{% load i18n %}
<div class="oh-modal__dialog-header ">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task' task_id %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<!-- <script>
$(document).ready(function(){
console.log('ready modal')
$('#close1').click(function(e){
e.preventDefault()
e.stopPropagation()
var closestModal = $(this).closest('.oh-modal--show');
console.log(closestModal);
closestModal.removeClass('oh-modal--show')
// e.preventDefault()
// var parentDiv = $(this).parents().closest('oh-modal--show')
// console.log(parentDiv)
// parentDiv.classList.remove('oh-modal--show')
})
});
</script> -->

View File

@@ -1,37 +1,37 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Timesheet" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'update-timesheet-task' timesheet_id %}"
hx-target="#TimesheetUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Timesheet" %}</h5>
<br />
</span>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'update-timesheet-task' timesheet_id %}"
hx-target="#TimesheetUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>

View File

@@ -1,144 +1,141 @@
{% extends 'index.html' %}
{% block content %}
{% load i18n %} {% load yes_no %}{% load static %}
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'task/new/task_navbar.html' %}
</main>
<div id="ohMessages"></div>
<div id="view-container" class="oh-wrapper">
{% if view_type == 'list' %}
{% include 'task/new/task_list_view.html' %}
{% else %}
{% include 'task/new/task_kanban_view.html' %}
{% endif %}
</div>
<div
class="oh-modal"
id="TaskModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskFormTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskUpdateTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="TaskTimesheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width:550px"
id="ProjectStageFormTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskTimesheetModal"
role="dialog"
aria-labelledby="TaskTimesheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width:1350px"
id="TaskTimesheetTarget"
></div>
</div>
<div
class="oh-modal {{request.GET.active}}"
id="TimesheetUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TimesheetUpdateTarget"
></div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
{% comment %} function cDelete(timeSheetId) {
console.log("Confirm Delete called"); // Add this line for debugging
if (chonfirm("{% trans 'Do you want to delete this time sheet?' %}")) {
// User confirmed, perform the delete operation here
deleteTimeSheet(timeSheetId);
}
} {% endcomment %}
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 deleteTimeSheet(element,timeSheetId) {
console.log($(element).parent().closest('ui-sortable-handle')); // Add this line for debugging
$.ajax({
url: "/project/delete-time-sheet-ajax/" + timeSheetId + "/", // Include the time_sheet.id in the URL
type: "post",
data:{
csrfmiddlewaretoken: getCookie("csrftoken"),
},
success: function(response) {
// Time sheet deleted successfully, you can handle this case as needed
// For example, you can redirect the user or update the UI.
$("#ohMessages").append(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`)
$(element).closest('.oh-sticky-table__tr.ui-sortable-handle').hide()
},
error: function() {
// Handle errors if the delete operation fails
alert("Failed to delete time sheet");
}
});
}
</script>
{% extends 'index.html' %}
{% block content %}
{% load i18n %} {% load horillafilters %}{% load static %}
<style>
.avatars {
display: flex;
padding: 8px 10px 8px 10px;
}
.avatars__item {
background-color: #596376;
border: 2px solid white;
border-radius: 100%;
color: #ffffff;
display: block;
font-family: sans-serif;
font-size: 12px;
font-weight: 100;
height: 26px;
width: 26px;
line-height: 17px;
text-align: center;
transition: margin 0.1s ease-in-out;
overflow: hidden;
margin-left: -10px;
}
.avatars__item:first-child {
z-index: 5;
}
.avatars__item:nth-child(2) {
z-index: 4;
}
.avatars__item:nth-child(3) {
z-index: 3;
}
.avatars__item:nth-child(4) {
z-index: 2;
}
.avatars__item:nth-child(5) {
z-index: 1;
}
.avatars__item:last-child {
z-index: 0;
}
.avatars__item img {
width: 100%;
}
.avatars:hover .avatars__item {
margin-right: 10px;
}
.select2-container .select2-selection.select2-selection--multiple {
padding: 5px !important;
max-height: 70px !important;
overflow: hidden;
overflow-y: scroll;
}
.search-highlight {
background-color: rgba(255, 68, 0, 0.076);
}
#enlargeImageContainer {
position: absolute;
left: -300px;
top: 100px;
height: 200px;
width: 200px;
}
</style>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'task/new/task_navbar.html' %}
</main>
{% include "generic/components.html" %}
<div class="oh-wrapper">
{% include "cbv/projects/project_details.html" %}
<div id="viewContainer" >
{% if view_type == 'list' %}
{% include 'task/new/task_list_view.html' %}
{% else %}
{% include 'task/new/task_kanban_view.html' %}
{% endif %}
</div>
</div>
<div class="oh-modal" id="TaskModal" role="dialog" aria-labelledby="TaskModal" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width: 550px" id="TaskFormTarget"></div>
</div>
<div class="oh-modal" id="TaskUpdateModal" role="dialog" aria-labelledby="TaskUpdateModal" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width: 550px" id="TaskUpdateTarget"></div>
</div>
<div class="oh-modal" id="ProjectStageModal" role="dialog" aria-labelledby="TaskTimesheetModal" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width:550px" id="ProjectStageFormTarget"></div>
</div>
<div class="oh-modal" id="TaskTimesheetModal" role="dialog" aria-labelledby="TaskTimesheetModal" aria-hidden="true">
<div class="oh-modal__dialog" style="max-width:1350px" id="TaskTimesheetTarget"></div>
</div>
<div class="oh-modal {{request.GET.active}}" id="TimesheetUpdateModal" role="dialog" aria-labelledby="TaskUpdateModal"
aria-hidden="true">
<div class="oh-modal__dialog" style="max-width: 550px" id="TimesheetUpdateTarget"></div>
</div>
<div class="oh-modal" id="addTimesheetModal" role="dialog" aria-labelledby="addTimesheetModal" aria-hidden="true"
style="z-index: 1022;">
<div class="oh-modal__dialog" id="addTimesheetModalBody">
<!-- Modal hx content goes here -->
</div>
</div>
<script>
$(document).ready(function(){
$('#taskFilterButton').click()
});
$("#viewContainer").on("htmx:afterSwap", function (e) {
// Check if the event originated from the quick add button or editable input field
var triggerElement = e.target ? e.target : null;
if (triggerElement && (triggerElement.classList.contains('oh-table__editable-input') || triggerElement.classList.contains('oh-kanban__add-card-container'))) {
// If the event is triggered by the 'Quick add' button or Editable input field , return and do nothing
return;
}
// Load new scripts
$.getScript("{% static 'build/js/web.frontend.min.js' %}", function () { });
$.getScript("{% static '/project/task_pipeline.js' %}", function () { });
});
</script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="{% static '/project/task_pipeline.js' %}"></script>
{% endblock content %}

View File

@@ -1,105 +1,105 @@
{% load i18n %} {% load yes_no %}
<div id="ohMessages"></div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
{% comment %}
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input
type="checkbox"
class="oh-input oh-input__checkbox mt-1 mr-2 all-candidate"
/>
</div>
<div>{% trans "Tasks" %}</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Task Assigner" %}</div>
<div class="oh-sticky-table__th">{% trans "Task Members" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Document" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% endcomment %}
<div class="oh-sticky-table__tr ui-sortable-handle task_row">
<div class="oh-sticky-table__sd">{{task.title}}</div>
<div class="oh-sticky-table__td">{{task.task_assigner}}</div>
<div class="oh-sticky-table__td">
{% for employee in task.task_members.all %} {{employee}}<br />
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{task.end_date}}</div>
{% comment %}
<div class="oh-sticky-table__td">{{task.stage}}</div>
{% endcomment %}
<div class="oh-sticky-table__td oh-table-config__td">
<select
id="stageChange{{task.id}}"
class="oh-select w-100 stage-change"
data-task="{{task.id}}"
>
{% for stage in stages %} {% if stage.id == task.stage.id %}
<option value="{{stage.id}}" selected>{{stage}}</option>
{% else %}
<option value="{{stage.id}}">{{stage}}</option>
{% endif %} {% endfor %}
</select>
</div>
<div class="oh-sticky-table__td">{{task.document}}</div>
<div class="oh-sticky-table__td">{{task.description}}</div>
</div>
</div>
</div>
<script>
$(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;
}
$(".stage-change").change(function () {
var stage = $(this).val();
var task = $(this).data("task");
$.ajax({
type: "post",
url: "/project/task-stage-change",
data: {
csrfmiddlewaretoken: getCookie("csrftoken"),
stage: stage,
task: task,
},
success: function (response) {
$("#ohMessages").append(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`);
},
});
$(this).parents(".task_row").remove();
$();
});
});
</script>
</div>
{% load i18n %} {% load horillafilters %}
<div id="ohMessages"></div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
{% comment %}
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input
type="checkbox"
class="oh-input oh-input__checkbox mt-1 mr-2 all-candidate"
/>
</div>
<div>{% trans "Tasks" %}</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Task Assigner" %}</div>
<div class="oh-sticky-table__th">{% trans "Task Members" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Document" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% endcomment %}
<div class="oh-sticky-table__tr ui-sortable-handle task_row">
<div class="oh-sticky-table__sd">{{task.title}}</div>
<div class="oh-sticky-table__td">{{task.task_assigner}}</div>
<div class="oh-sticky-table__td">
{% for employee in task.task_members.all %} {{employee}}<br />
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{task.end_date}}</div>
{% comment %}
<div class="oh-sticky-table__td">{{task.stage}}</div>
{% endcomment %}
<div class="oh-sticky-table__td oh-table-config__td">
<select
id="stageChange{{task.id}}"
class="oh-select w-100 stage-change"
data-task="{{task.id}}"
>
{% for stage in stages %} {% if stage.id == task.stage.id %}
<option value="{{stage.id}}" selected>{{stage}}</option>
{% else %}
<option value="{{stage.id}}">{{stage}}</option>
{% endif %} {% endfor %}
</select>
</div>
<div class="oh-sticky-table__td">{{task.document}}</div>
<div class="oh-sticky-table__td">{{task.description}}</div>
</div>
</div>
</div>
<script>
$(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;
}
$(".stage-change").change(function () {
var stage = $(this).val();
var task = $(this).data("task");
$.ajax({
type: "post",
url: "/project/task-stage-change",
data: {
csrfmiddlewaretoken: getCookie("csrftoken"),
stage: stage,
task: task,
},
success: function (response) {
$("#ohMessages").append(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`);
},
});
$(this).parents(".task_row").remove();
$();
});
});
</script>
</div>

View File

@@ -0,0 +1,209 @@
{% load static %}
{% load i18n %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container" >
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<div id = 'ohMessages'>
</div>
{% include "filter_tags.html" %}
{% if stages %}
{% comment %} vertical tabs {% endcomment %}
<div class="oh-wrapper oh-tabs">
<div class="ui-sortable" id ="project container">
<div class="oh-kanban ui-sortable" id='projectStages'>
{% comment %} stages view {% endcomment %}
{% for stage in stages %}
<div
class="oh-kanban__section stages"
data-container='stages'
id="kanban{{stage.id}}"
data-project-id = "{{project.id}}"
>
<div class="oh-kanban__section-head stage ms-4" style="cursor: pointer;"
data-stage-id="{{stage.id}}"
data-sequence = "{{stage.sequence}}"
data-project-id='{{project.id}}'>
<div class="d-flex">
<span class="oh-kanban__section-title" data-type="label">{{stage.title}}</span>
</div>
<div class="oh-tabs__input-badge-container">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1">
{{ stage.tasks.all|length}}
</span>
</div>
{% comment %} drop down menu {% endcomment %}
<div class="oh-kanban__head-actions oh-kanban__dropdown" >
<div class="oh-kanban__head-actions oh-kanban__dropdown">
<div >
<a class="" @click="open = !open" style="padding:20px"
hx-get='{% url "create-task" stage.id %}'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
onclick="handleCreateTaskClick(event)"
>
<ion-icon name="add-sharp" role="img" class="md hydrated mt-1" aria-label="add sharp"></ion-icon>
</a>
</div>
<button class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-project-stage" stage.id %}'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-project-stage' stage.id %}" onsubmit="return confirm('{% trans "Are you sure you want to delete this stage?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% comment %} task inside stage {% endcomment %}
<div
class="oh-kanban__section-body ui-sortable tasks-container"
style="left-padding:10px;"
data-stage-id='{{stage.id}}'
data-project-id="{{project.id}}"
>
{% for task in stage.tasks.all|dictsort:"sequence" %}
{% if task in tasks %}
<div
class="oh-kanban__card task ms-3"
data-task-id="{{task.id}}"
data-task="{{task.title}}"
id="task-{{task.id}}"
style="cursor: pointer;overflow: visible;left-padding:10px"
>
<div class="oh-kanban__card-head">
<a hx-get='{% url "task-details" task.id %}?view=card'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="https://ui-avatars.com/api/?name={{task}}&amp;background=random"
class="oh-profile__image"
alt="task"
/>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">
{{task}}
</span>
<span class="oh-kanban-card__subtitle">{{task.task_manager}} </span><br>
<span class="oh-kanban-card__subtitle">{{task.end_date}}</span><br>
<span class="oh-kanban-card__subtitle">{{task.get_status_display}}</span>
</div>
</div>
</a>
{% comment %} drop down inside card {% endcomment %}
<div class="oh-kanban__card-actions oh-kanban__dropdown">
<button
class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle"
>
<ion-icon
name="ellipsis-vertical-sharp"
role="img"
class="md hydrated"
aria-label="ellipsis vertical sharp"
></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-task" task.id %}?project_task=true'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-task' task.id %}?view=card" onsubmit="return confirm('{% trans "Are you sure you want to delete this task?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
{% comment %} <a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#ProjectStageModal"
hx-get="{% url 'create-project-stage' project_id %}"
hx-target="#ProjectStageFormTarget"
>
<ion-icon class="me-1" name="add-outline"></ion-icon>{% trans "Stage" %}
</a> {% endcomment %}
</div>
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% comment %} js files {% endcomment %}
<script src="{% static '/project/task_pipeline.js' %}"></script>

View File

@@ -1,134 +1,133 @@
{% load i18n %} {% load yes_no %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">{{task.title}}</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Title" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.title}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Project" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.project}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Stage" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.stage}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Task manager" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.task_manager}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Task members" %}</span>
<span class="oh-timeoff-modal__stat-count">{% for member in task.task_members.all %}{{member}}, {% endfor %}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.get_status_display}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "End Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.end_date}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.description}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Document" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.document}}
</span>
</div>
</div>
<div class="oh-modal__button-container text-center">
<div class="oh-btn-group">
<a hx-get="{% url 'update-task' task.id %}"
hx-target='#TaskUpdateTarget'
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 50%;">
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans "Edit" %}
</a>
<a hx-get="{% url 'task-timesheet' task.id %}"
hx-target='#TaskTimesheetTarget'
data-target="#TaskTimesheetModal"
data-toggle="oh-modal-toggle"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 56%; background-color:green">
<ion-icon
name="bar-chart"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans " Time sheet" %}
</a>
<a href="{% url 'delete-task' task.id %}?view={{request.GET.view }}" class="oh-btn oh-btn--danger"style="width: 50%;"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)"
>
{% comment %} <a hx-get="{% url 'delete-task' task.id %}" class="oh-btn oh-btn--danger"style="width: 50%;"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)"
hx-target='#TaskUpdateTarget'
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 50%;">
{% endcomment %}
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Delete" %}
</a>
</div>
</div>
</div>
{% comment %} <script>
$(document).ready(function(){
console.log('ready modal')
$('.oh-modal__close').click(function(e){
e.preventDefault()
var parentDiv = $(this).parents().closest('oh-modal--show')
console.log(parentDiv)
parentDiv.classList.remove('oh-modal--show')
})
});
</script> {% endcomment %}
{% load i18n %} {% load horillafilters %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">{{task.title}}</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Title" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.title}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Project" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.project}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Stage" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.stage}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Task manager" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.task_manager}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Task members" %}</span>
<span class="oh-timeoff-modal__stat-count">{% for member in task.task_members.all %}{{member}}, {% endfor %}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.get_status_display}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "End Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.end_date}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.description}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Document" %}</span>
<span class="oh-timeoff-modal__stat-count">{{task.document}}
</span>
</div>
</div>
<div class="oh-modal__button-container text-center">
<div class="oh-btn-group">
<a hx-get="{% url 'update-task' task.id %}"
hx-target='#TaskUpdateTarget'
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 50%;">
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans "Edit" %}
</a>
<a hx-get="{% url 'task-timesheet' task.id %}"
hx-target='#TaskTimesheetTarget'
data-target="#TaskTimesheetModal"
data-toggle="oh-modal-toggle"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 56%; background-color:green">
<ion-icon
name="bar-chart"
role="img"
class="md hydrated"
aria-label="create outline"
>
</ion-icon>{% trans " Time sheet" %}
</a>
<a href="{% url 'delete-task' task.id %}?view={{request.GET.view }}" class="oh-btn oh-btn--danger"style="width: 50%;"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)"
>
{% comment %} <a hx-get="{% url 'delete-task' task.id %}" class="oh-btn oh-btn--danger"style="width: 50%;"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)"
hx-target='#TaskUpdateTarget'
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
data-key=".oh-kanban__section"
class="oh-btn oh-btn--info" style="width: 50%;">
{% endcomment %}
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Delete" %}
</a>
</div>
</div>
</div>
{% comment %} <script>
$(document).ready(function(){
console.log('ready modal')
$('.oh-modal__close').click(function(e){
e.preventDefault()
var parentDiv = $(this).parents().closest('oh-modal--show')
console.log(parentDiv)
parentDiv.classList.remove('oh-modal--show')
})
});
</script> {% endcomment %}

View File

@@ -1,210 +1,168 @@
{% load static %}
{% load i18n %}
{% comment %} for showing messages {% endcomment %}
{% if messages %}
<div class="oh-alert-container" >
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% load i18n %}
{% load static %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% if stages %}
<div class="oh-tabs oh-card mt-3 mb-3">
<div class="oh-kanban ui-sortable" id="projectStages" style="border: none;">
{% for stage in stages %}
<div class="oh-kanban__section ml-2 stage" id="projectStage{{stage.id}}" style="opacity: 1;"
data-stage-id="{{stage.id}}" data-sequence="{{stage.sequence}}" data-project-id="{{project.id}}">
<div class="oh-kanban-group__head" style="cursor: pointer;" onclick="updateStageClass(this)">
<span class="oh-kanban-gorup__title" data-type="label" style="display: flex;">
{{stage.title}}
<div class="oh-tabs__input-badge-container ml-2">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1 task-count">
{{ stage.tasks.all|length}}
</span>
</div>
</span>
{% if request.user|is_project_manager:stage.project or request.user.is_superuser %}
<div class="oh-kanban__head-actions oh-kanban__dropdown" onclick="event.stopPropagation();">
<button class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated"
aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a hx-get='{% url "update-project-stage" stage.id %}' hx-target='#TaskFormTarget'
class="oh-dropdown__link" data-toggle="oh-modal-toggle" data-target="#TaskModal">
{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<a hx-confirm="{% trans 'Are you sure you want to delete this stage?' %}"
hx-post="{% url 'delete-project-stage' stage.id %}?view=card"
hx-target="#projectStage{{stage.id}}"
class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</a>
</li>
</ul>
</div>
</div>
<button class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__add-card-btn"
title="{% trans 'Add task' %}" hx-get="{% url 'quick-create-task' stage.id %}"
hx-target="#taskCreateForm{{stage.id}}">
<ion-icon name="add-sharp" role="img" class="md hydrated" aria-label="add sharp"></ion-icon>
</button>
</div>
{% endif %}
</div>
<div class="oh-kanban__section-body ui-sortable task-container">
<div class="oh-card oh-kanban__add-card-container mb-2 d-none" id="taskCreateForm{{stage.id}}">
</div>
{% for task in stage.tasks.all|dictsort:"sequence" %}
{% if task in tasks %}
<div class="oh-kanban__card task" data-task-id="{{task.id}}" style="opacity: 1;min-height:85px">
<div class="oh-kanban__card-head" hx-get="{% url 'task-detail-view' task.id %}?view=card"
hx-target='#genericModalBody' data-toggle="oh-modal-toggle" style="cursor: pointer;"
data-target="#genericModal">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img src="{{task.get_avatar}}"
class="oh-profile__image" alt="{{task}}">
</div>
<span class="oh-profile__name oh-text--dark" data-type="label"
style="font-weight:bold">{{task}}</span>
</div>
{% if request.user|is_project_manager:stage.project or request.user.is_superuser or request.user|is_task_manager:task %}
<div class="oh-kanban__card-actions oh-kanban__dropdown" onclick="event.stopPropagation();">
<button
class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated"
aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a hx-get='{% url "update-task" task.id %}?project_task=true'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle" data-target="#TaskModal">
{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<a hx-confirm="{% trans 'Do you really want to delete this task?' %}"
hx-post="{% url 'delete-task' task.id %}?view=card"
hx-target="#taskCreateForm{{stage.id}}"
class="oh-dropdown__link oh-dropdown__link--danger">
{% trans "Delete" %}
</a>
</li>
</ul>
</div>
</div>
</div>
{% endif %}
</div>
<div class="oh-kanban__card-body">
<button class="oh-kanban__card-body-collapse oh-kanban__card-collapse--down"
aria-label="Toggle Options"></button>
<div class="oh-kanban__card-content oh-kanban__card-content--hide">
<span class="oh-kanban-card__subtitle d-flex">{% trans "Managers: " %}
<span class="oh-kanban-card__title ml-1">
{% for manager in task.task_managers.all %}
{{manager}}
{% endfor %}
</span>
</span><br>
<div style="display: flex;" class="mb-2">
<label class="oh-kanban-card__subtitle">{% trans "Status" %} </label>
<select class="oh-table__editable-input w-100 ml-2"
hx-get="{% url 'update-project-task-status' task.id %}?view=card"
hx-trigger="change" hx-swap="afterend" tabindex="-1" aria-hidden="true"
name="status">
{% for option in task.TASK_STATUS %}
<option value="{{option.0}}" {% if option.0 == task.status %}selected{% endif %}>{{option.1}}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="oh-kanban__card-footer">
<span class="oh-kanban__card-footer-text oh-text--light">{% trans "End date:" %} :
{{task.end_date}}</span>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
{% if request.user|is_project_manager:stages.first.project or request.user.is_superuser %}
<div class="oh-kanban__add-container">
<button class="oh-btn oh-btn--x-small oh-kanban__add-section mr-2" data-toggle="oh-modal-toggle"
data-target="#objectCreateModal" hx-get="{% url 'create-project-stage' project_id %}"
hx-target="#objectCreateModalTarget">
<ion-icon class="me-1 md hydrated" name="add-outline" role="img" aria-label="add outline"></ion-icon>
{% trans "Add Stage" %}
</button>
</div>
{% endif %}
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div>
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}"
class="" alt="Page not found. 404." />
<h3 style="font-size:20px" class="oh-404__subtitle">
{% trans "No search result found!" %}</h3>
</div>
</div>
{% endif %}
<div id = 'ohMessages'>
</div>
{% include "filter_tags.html" %}
{% if stages %}
{% comment %} vertical tabs {% endcomment %}
<div class="oh-wrapper oh-tabs">
<div class="ui-sortable" id ="project container">
<div class="oh-kanban ui-sortable" id='project_stages'>
{% comment %} stages view {% endcomment %}
{% for stage in stages %}
<div
class="oh-kanban__section stages"
data-container='stages'
id="kanban{{stage.id}}"
data-project-id = "{{project.id}}"
>
<div class="oh-kanban__section-head stage ms-4" style="cursor: pointer;"
data-stage-id="{{stage.id}}"
data-sequence = "{{stage.sequence}}"
data-project-id='{{project.id}}'>
<div class="d-flex">
<span class="oh-kanban__section-title" data-type="label">{{stage.title}}</span>
</div>
<div class="oh-tabs__input-badge-container">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1">
{{ stage.tasks.all|length}}
</span>
</div>
{% comment %} drop down menu {% endcomment %}
<div class="oh-kanban__head-actions oh-kanban__dropdown" >
<div class="oh-kanban__head-actions oh-kanban__dropdown">
<div >
<a class="" @click="open = !open" style="padding:20px"
hx-get='{% url "create-task" stage.id %}'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
onclick="handleCreateTaskClick(event)"
>
<ion-icon name="add-sharp" role="img" class="md hydrated mt-1" aria-label="add sharp"></ion-icon>
</a>
</div>
<button class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-project-stage" stage.id %}'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-project-stage' stage.id %}" onsubmit="return confirm('{% trans "Are you sure you want to delete this stage?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% comment %} task inside stage {% endcomment %}
<div
class="oh-kanban__section-body ui-sortable tasks-container"
style="left-padding:10px;"
data-stage-id='{{stage.id}}'
data-project-id="{{project.id}}"
>
{% for task in stage.tasks.all|dictsort:"sequence" %}
{% if task in tasks %}
<div
class="oh-kanban__card task ms-3"
data-task-id="{{task.id}}"
data-task="{{task.title}}"
id="task-{{task.id}}"
style="cursor: pointer;overflow: visible;left-padding:10px"
>
<div class="oh-kanban__card-head">
<a hx-get='{% url "task-details" task.id %}?view=card'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="https://ui-avatars.com/api/?name={{task}}&amp;background=random"
class="oh-profile__image"
alt="task"
/>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">
{{task}}
</span>
<span class="oh-kanban-card__subtitle">{{task.task_manager}}</span><br>
<span class="oh-kanban-card__subtitle">{{task.end_date}}</span><br>
<span class="oh-kanban-card__subtitle">{{task.get_status_display}}</span>
</div>
</div>
</a>
{% comment %} drop down inside card {% endcomment %}
<div class="oh-kanban__card-actions oh-kanban__dropdown">
<button
class="oh-btn oh-btn--small oh-btn--transparent oh-kanban__btn oh-kanban__dropdown-toggle"
>
<ion-icon
name="ellipsis-vertical-sharp"
role="img"
class="md hydrated"
aria-label="ellipsis vertical sharp"
></ion-icon>
</button>
<div class="oh-dropdown oh-kanban__dropdown-menu d-none">
<div class="oh-dropdown__menu oh-dropdown__menu--right">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
hx-get='{% url "update-task" task.id %}'
hx-target='#TaskFormTarget' class="oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
data-key=".oh-kanban__section"
>{% trans "Edit" %}
</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-task' task.id %}?view=card" onsubmit="return confirm('{% trans "Are you sure you want to delete this task?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#ProjectStageModal"
hx-get="{% url 'create-project-stage' project_id %}"
hx-target="#ProjectStageFormTarget"
>
<ion-icon class="me-1" name="add-outline"></ion-icon>{% trans "Stage" %}
</a>
</div>
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% comment %} js files {% endcomment %}
<script src="{% static '/project/task_pipeline.js' %}"></script>

View File

@@ -1,427 +1,214 @@
{% load static %}
{% load i18n %} {% load yes_no %}
{% comment %} for showing messages {% endcomment %}
<div class="oh-alert-container" id='ajaxMessages'>
{% load static %}
{% load i18n %}
{% load horillafilters taskfilters %}
{% if messages %}
<div class="oh-alert-container" id='ohMessages'>
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
<div id="projectStages" class="oh-card" style="margin-top: 5px">
{% if request.user|is_project_manager:stages.first.project or request.user.is_superuser %}
<div class="oh-kanban__add-container mb-2">
<button class="oh-btn oh-btn--x-small oh-kanban__add-section mr-2" data-toggle="oh-modal-toggle"
data-target="#objectCreateModal" hx-get="{% url 'create-project-stage' project_id %}"
hx-target="#objectCreateModalTarget">
<ion-icon class="me-1 md hydrated" name="add-outline" role="img" aria-label="add outline"></ion-icon>
{% trans "Add Stage" %}
</button>
</div>
{% endif %}
{% if stages %}
<div class="oh-accordion-meta">
<div class="">
{% for stage in stages %}
<!-- Start of stages comes under the project will loading here -->
<div class="oh-accordion-meta__header oh-accordion-meta__header--custom stage_header" data-target="#taskStage{{stage.id}}"
style ="min-height: 50px;"
>
<div class="d-flex">
<span class="oh-accordion-meta__title" style="display:inherit;">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1">
{{ stage.tasks.all|length}}
</span>
{{stage.title}}
</span>
</div>
<div class="d-flex oh-tabs__input-badge-container">
</div>
<div class="oh-btn-group ml-3">
{% if request.user|is_project_manager:stage.project or request.user.is_superuser %}
<div class="mr-2" onclick="event.stopPropagation();">
<a class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle = "oh-modal-toggle" data-target="#genericModal"
hx-get="{% url 'create-stage-task' stage.id %}" hx-target="#genericModalBody"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Add Task" %}
</a>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn oh-stop-prop oh-accordion-meta__btn" @click="open = !open"
@click.outside="open = false">
{% trans "Actions" %}
<ion-icon class="ms-2 oh-accordion-meta__btn-icon" name="caret-down-outline"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a class="oh-dropdown__link oh-dropdown__link" data-toggle="oh-modal-toggle"
data-target="#TaskModal" hx-get="{% url 'update-project-stage' stage.id %}"
onclick="event.stopPropagation()" hx-target="#TaskFormTarget">
{% trans "Update" %}</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-project-stage' stage.id %}"
onsubmit="return confirm('{% trans " Are you sure you want to delete this stage?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
{% endif %}
<div id="taskCreateForm{{stage.id}}"
class="oh-dropdown__menu oh-dropdown__menu--highlight oh-dropdown__menu--top-100 oh-dropdown__close-outside-click oh-dropdown__menu--right pt-2 pb-2 list-quick-add d-none"
style="z-index: 15;">
</div>
</div>
</div>
<!-- End of stages comes under the project will loading here -->
<div class="oh-accordion-meta__body d-none" id="taskStage{{stage.id}}">
{% if stage.tasks.all %}
<!-- Start of tasks comes under the stage will loading here -->
<div class="oh-sticky-table oh-sticky-table--no-overflow mb-5">
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input type="checkbox" id="selectAllTasks {{stage.id}}"
class="oh-input oh-input__checkbox mt-1 mr-2 all-candidate" />
</div>
<div>
{% trans "Tasks" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Task Managers" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Document" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
{% if request.user|is_project_manager:stage.project or request.user.is_superuser %}
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
{% endif %}
</div>
</div>
{% for task in stage.tasks.all %}
{% if task in tasks %}
<div class="oh-sticky-table__tr ui-sortable-handle task_row" data-task="{{task.title}}"
data-toggle="oh-modal-toggle" data-target="#genericModal" hx-target="#genericModalBody"
hx-get="{% url 'task-detail-view' task.id %}?view=list" onclick="event.stopPropagation()">
<div class="oh-sticky-table__sd">
<div class="d-flex">
<div class="">
<input type="checkbox" id="selectRowTask {{task.id}}"
class="oh-input oh-input__checkbox mt-1 mr-2 all-candidate" />
</div>
{{task.title}}
</div>
</div>
<div class="oh-sticky-table__td">
{% for manager in task.task_managers.all %}
{{manager}},
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{task.end_date}}</div>
<div class="oh-sticky-table__td">{{task.get_status_display}}</div>
<div class="oh-sticky-table__td oh-table-config__td" onclick='event.stopPropagation();'>
<select class="oh-table__editable-input w-100 mt-2 mr-2"
hx-get="{% url 'update-project-task-status' task.id %}?view=list"
hx-trigger="change" hx-swap="afterend" tabindex="-1" aria-hidden="true"
name="status">
{% for option in task.TASK_STATUS %}
<option value="{{option.0}}" {% if option.0 == task.status %}selected{% endif %}>
{{option.1}}</option>
{% endfor %}
</select>
</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation()">
{% if task.document %}
<a style="text-decoration: none" class="oh-btn oh-btn--light"
href="/media/{{task.document}}" target="_blank" title="{{task.document}}"
rel="noopener noreferrer"
>
<span class="oh-file-icon oh-file-icon--pdf"></span> &nbsp View
</a>
{% else %}
{% trans "None" %}
{% endif %}
</div>
<div class="oh-sticky-table__td">{{task.description}}</div>
{% if request.user|is_project_manager:stage.project or request.user.is_superuser %}
<div class="oh-sticky-table__td">
<div class="oh-btn-group"
onclick="event.preventDefault();event.stopPropagation()"
>
<button class="oh-btn oh-btn--light-bkg w-100" title="{% trans 'Edit' %}"
data-toggle="oh-modal-toggle" data-target="#TaskModal"
hx-get="{% url 'update-task' task.id %}?project_task=true"
hx-target="#TaskFormTarget"><ion-icon
name="create-outline"></ion-icon></button>
<a class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
hx-confirm="{% trans 'Do you really want to delete this task?' %}"
hx-post="{% url 'delete-task' task.id %}?view=list"
hx-target="#taskCreateForm{{stage.id}}">
<ion-icon name="trash-outline" role="img" class="md hydrated"
aria-label="trash outline"></ion-icon>
</a>
</div>
</div>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div>
<!-- End of tasks comes under the stage will loading here -->
{% else %}
<div class="oh-empty">
<img src="{% static 'images/ui/grid.svg' %} " class="mb-4" width="140" height="140" alt="Empty" />
<p class="oh-empty__message">
{% trans "No tasks found in this stage." %}
</p>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}"
class="" alt="Page not found. 404." />
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "No search result found!" %}</h3>
</div>
</div>
{% endif %}
</div>
{% if messages %}
<div class="oh-alert-container" id='ohMessages'>
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% if stages %}
<div class="oh-card" id="">
<div class="oh-accordion-meta">
<div class="">
{% for stage in stages %}
<div
class="oh-accordion-meta__header oh-accordion-meta__header--custom stage_header"
data-target="#taskstage{{stage.id}}"
>
<div class="d-flex">
<span class="oh-accordion-meta__title" style="display:inherit;">
<span class="oh-badge oh-badge--secondary oh-badge--small oh-badge--round mr-1">
{{ stage.tasks.all|length}}
</span>
{{stage.title}}
</span>
</div>
<div class="d-flex oh-tabs__input-badge-container">
</div>
<div class="oh-btn-group ml-3">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
hx-get="{% url 'create-task' stage.id %}"
hx-target="#TaskFormTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Add Task" %}
</a>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-stop-prop oh-accordion-meta__btn"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
<ion-icon
class="ms-2 oh-accordion-meta__btn-icon"
name="caret-down-outline"
></ion-icon>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right"
x-show="open"
style="display: none"
>
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a
class="oh-dropdown__link oh-dropdown__link"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
hx-get="{% url 'update-project-stage' stage.id %}"
onclick="event.stopPropagation()"
hx-target="#TaskFormTarget"
>
{% trans "Update" %}</a
>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-project-stage' stage.id %}" onsubmit="return confirm('{% trans "Are you sure you want to delete this stage?" %}');" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
<div
class="oh-accordion-meta__body d-none"
id="taskstage{{stage.id}}"
>
<!-- htmx task will loading here -->
<div
class="oh-sticky-table oh-sticky-table--no-overflow mb-5">
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable" >
<div class="oh-sticky-table__thead" >
<div class="oh-sticky-table__tr" id="task_view{{stage.id}}" >
<div class="oh-sticky-table__th" >
<div class="d-flex" >
<div class="">
<input
type="checkbox"
class="oh-input oh-input__checkbox mt-1 mr-2 all-candidate"
/>
</div>
<div
>
{% trans "Tasks" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Task Manager" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Document" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% for task in stage.tasks.all %}
{% comment %} {% if task in tasks %} {% endcomment %}
<div class="oh-sticky-table__tr ui-sortable-handle task_row"
data-task = "{{task.title}}"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
hx-target="#TaskFormTarget"
hx-get="{% url 'task-details' task.id %}?view=list"
onclick="event.stopPropagation()"
>
<div class="oh-sticky-table__sd" >{{task.title}}</div>
<div class="oh-sticky-table__td">{{task.task_manager}}</div>
<div class="oh-sticky-table__td">{{task.end_date}}</div>
<div class="oh-sticky-table__td">{{task.get_status_display}}</div>
<div class="oh-sticky-table__td oh-table-config__td">
<select
id="stageChange{{task.id}}"
class="oh-select w-100 stage-change"
data-task='{{task.id}}'
onchange="stageChange(this)"
>
{% for stage in stages %}
{% if stage.id == task.stage.id %}
<option value="{{stage.id}}" selected>{{stage}}</option>
{% else %}
<option value="{{stage.id}}">{{stage}}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="oh-sticky-table__td">{{task.document}}</div>
<div class="oh-sticky-table__td">{{task.description}}</div>
<div class="oh-sticky-table__td">
<div class="oh-btn-group">
<button class="oh-btn oh-btn--light-bkg w-100" title="{% trans 'Edit' %}" data-toggle="oh-modal-toggle"
data-target="#TaskModal" hx-get="{% url 'update-task' task.id %}"
hx-target="#TaskFormTarget"><ion-icon name="create-outline"></ion-icon></button>
{% comment %} <a class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100" id="delete-link"
href="{% url 'delete-task' task.id %}?view=list" title="{% trans 'Delete' %}">
<ion-icon name="trash-outline"></ion-icon>
</a> {% endcomment %}
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-task' task.id %}?view=list"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this task?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
</div>
</div>
</div>
{% comment %} {% endif %} {% endcomment %}
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
<script>
$(document).ready(function () {
$(".oh-accordion-meta__header").click(function () {
var target = $(this).data("target");
$(target).toggleClass("d-none");
});
});
</script>
</div>
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
<script src="{% static '/project/task_pipeline.js' %}"></script>
<script>
function stageChange(selectedElement){
var stage = selectedElement.value;
var task = selectedElement.getAttribute('data-task');
var task_row = $(selectedElement).parents('.task_row')
task_row.remove();
$(`#taskstage${stage} .oh-sticky-table__thead`).after(task_row)
$.ajax({
type:'post',
url:'/project/task-stage-change',
data:{
csrfmiddlewaretoken: getCookie("csrftoken"),
stage:stage,
task:task,
},
success: function(response){
$("#ajaxMessages").append(`
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated oh-alert--${response.type}">
${response.message}
</div>
</div>`);
}
})
}
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;
}
</script>
{% comment %}
<div class="oh-card">
<div class="oh-accordion-meta">
<div class="oh-accordion-meta__item">
{% for project_stage in project_stages %} {{project_stage.stage}} <br/>
{% endfor %}
{% for task in tasks %}
<div class="oh-accordion-meta__header">
<span class="oh-accordion-meta__title"
> {{ task.task_title }}</span
>
<div class="oh-accordion-meta__actions">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-stop-prop oh-accordion-meta__btn"
@click="open = !open"
@click.outside="open = false"
>
Actions
<ion-icon
class="ms-2 oh-accordion-meta__btn-icon"
name="caret-down-outline"
></ion-icon>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right"
x-show="open"
>
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link">Arrange</a>
</li>
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link">Forward</a>
</li>
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link">Archive</a>
</li>
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
>Delete</a
>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="oh-accordion-meta__body d-none">
<div
class="oh-sticky-table oh-sticky-table--no-overflow mb-5"
>
<div class="oh-sticky-table__table">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">Task Assigner</div>
<div class="oh-sticky-table__th">Task Members</div>
<div class="oh-sticky-table__th">End Date</div>
<div class="oh-sticky-table__th">Status</div>
<div class="oh-sticky-table__th">Documents</div>
<div class="oh-sticky-table__th">Description</div>
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-stop-prop oh-btn--transparent oh-accordion-meta__btn"
@click="open = !open"
@click.outside="open = false"
>
<ion-icon
name="ellipsis-vertical"
></ion-icon>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right"
x-show="open"
>
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link"
>Arrange</a
>
</li>
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link"
>Forward</a
>
</li>
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link"
>Archive</a
>
</li>
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
>Delete</a
>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<div class="oh-sticky-table__tbody">
<div
class="oh-sticky-table__tr oh-multiple-table-sort__movable"
>
<div class="oh-sticky-table__sd">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="https://ui-avatars.com/api/?name=Magdalene&background=random"
class="oh-profile__image"
alt="Mary Magdalene"
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{task.task_assigner_id}}</span
>
</div>
</div>
<div class="oh-sticky-table__td">
{% for employee in task.task_members_id.all %} {{employee}}<br />
{% endfor %}
</div>
<div class="oh-sticky-table__td">{{task.end_date}}</div>
<div class="oh-sticky-table__td">{{task.status}}</div>
<div class="oh-sticky-table__td">{{task.document}}</div>
<div class="oh-sticky-table__td">{{task.description}}</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<script src="./build/js/web.frontend.min.js"></script>
<script
type="module"
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.esm.js"
></script>
<script
nomodule
src="https://unpkg.com/ionicons@5.5.2/dist/ionicons/ionicons.js"
></script>
<script
type="module"
type="text/javascript"
src="./build/vendor/ionicons/ionicons/dist/ionicons/ionicons.esm.js"
></script> {% endcomment %}
{% comment %} js files {% endcomment %}

View File

@@ -1,104 +1,72 @@
{% load i18n %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{{project}} {% trans ":Tasks" %} </h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% comment %} for search{% endcomment %}
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-task"
data-view = "{{request.GET.view}}"
name="search"
placeholder="{% trans 'Search' %}"
{% comment %} hx-get="{% url 'task-filter' project_id %}?view={{request.GET.view}}"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#view-container" {% endcomment %}
/>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
{% comment %} for list view {% endcomment %}
<li class="oh-view-type task-view-type" data-view="list">
<a href= "{% url 'task-view' project_id %}?view=list"
class="oh-btn oh-btn--view"
>
<ion-icon name="list-outline"></ion-icon>
</a>
</li>
{% comment %} for card view {% endcomment %}
<li class="oh-view-type task-view-type" data-view="card">
<a
href= "{% url 'task-view' project_id %}"
class="oh-btn oh-btn--view"
>
<ion-icon name="grid-outline"></ion-icon
></a>
</li>
</ul>
</div>
{% comment %} for filtering {% endcomment %}
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'task/new/filter_task.html' %}
</div>
</div>
{% comment %} for create task {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TaskModal"
hx-get="{% url 'create-project-stage' project_id %}"
hx-target="#TaskFormTarget"
title="Create stage"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
</div>
</section>
{% load i18n taskfilters %}
<form
hx-get='{% url "task-filter" project_id %}?view={{request.GET.view}}'
hx-target="#viewContainer"
hx-swap="innerHTML"
id="taskFilterForm">
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{{project}}</h1>
<a class="oh-main__titlebar-search-toggle" role="button" aria-label="Toggle Search"
@click="searchShow = !searchShow">
<ion-icon name="search-outline" class="oh-main__titlebar-serach-icon md hydrated" role="img"
aria-label="search outline"></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
<div class="oh-input-group oh-input__search-group" :class="searchShow ? 'oh-input__search-group--show' : ''">
<ion-icon name="search-outline" class="oh-input-group__icon oh-input-group__icon--left"></ion-icon>
<input type="text" class="oh-input oh-input__icon" aria-label="Search Input"
name="search" placeholder="{% trans 'Search' %}"
onkeyup="$('#taskFilterButton').click()"
hx-target="#viewContainer" autocomplete="off" />
</div>
<input name='view' id="viewInput" hidden>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
<li class="oh-view-type task-view-type" data-view="list">
<a
onclick="$('#viewInput').val('list');$('#taskFilterButton').click()"
class="oh-btn oh-btn--view">
<ion-icon name="list-outline"></ion-icon>
</a>
</li>
<li class="oh-view-type task-view-type" data-view="card">
<a
onclick="$('#viewInput').val('card');$('#taskFilterButton').click()"
class="oh-btn oh-btn--view">
<ion-icon name="grid-outline"></ion-icon>
</a>
</li>
</ul>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open"
onclick="event.preventDefault()"
>
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4" x-show="open"
@click.outside="open = false" style="display: none;">
{% include 'task/new/filter_task.html' %}
</div>
</div>
{% if request.user|is_project_manager:project or request.user.is_superuser %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a class="oh-btn oh-btn--secondary oh-btn--shadow" title="{% trans 'Create task' %}"
hx-get='{% url "create-task" project_id %}' hx-target='#TaskFormTarget'
data-toggle="oh-modal-toggle" data-target="#TaskModal">
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
{% endif %}
</div>
</section>
</form>

View File

@@ -1,140 +1,137 @@
{% load i18n %} {% load yes_no %} {% load static %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">Time Sheet</h5>
</span>
{% comment %} for add timesheet {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TimesheetUpdateModal"
hx-get="{% url 'create-timesheet-task' task_id %}"
hx-target="#TimesheetUpdateTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans " Add" %}
</a>
</div>
</div>
</div>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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="close"
>
<ion-icon name="close-outline"></ion-icon>
</button>
{% if time_sheets %}
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div>{% trans "Employee" %}</div>
</div>
</div>
{% comment %}
<div class="oh-sticky-table__th">{% trans "Employee" %}</div>
{% endcomment %}
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Task" %}</div>
<div class="oh-sticky-table__th">{% trans "Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Time Spent" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% for time_sheet in time_sheets %}
<div class="oh-sticky-table__tr ui-sortable-handle"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<div class="oh-sticky-table__sd">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{time_sheet.employee_id.employee_first_name}}
{{time_sheet.employee_id.employee_last_name|default:""}}
</span>
</div>
</div>
<div class="oh-sticky-table__td">{{time_sheet.project_id.title}}</div>
<div class="oh-sticky-table__td">{{time_sheet.task_id}}</div>
<div class="oh-sticky-table__td">{{time_sheet.date}}</div>
<div class="oh-sticky-table__td">{{time_sheet.time_spent}}</div>
<div class="oh-sticky-table__td">{{time_sheet.get_status_display}}</div>
<div class="oh-sticky-table__td">{{time_sheet.description|truncatechars:15}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group " >
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#TimesheetUpdateModal"
hx-get="{% url 'update-timesheet-task' time_sheet.id %}"
hx-target="#TimesheetUpdateTarget"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<button
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
id="time_sheet.id"
onclick=" deleteTimeSheet(this,{{time_sheet.id}});"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</button>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 80px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:15px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}
</div>
{% load i18n %} {% load horillafilters %} {% load static %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">Time Sheet</h5>
</span>
{% comment %} for add timesheet {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TimesheetUpdateModal"
hx-get="{% url 'create-timesheet-task' task_id %}"
hx-target="#TimesheetUpdateTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans " Add" %}
</a>
</div>
</div>
</div>
<button
type="button"
style="
border: none;
background: none;
font-size: 1.5rem;
opacity: 0.7;
position: absolute;
top: 25px;
right: 15px;
cursor: pointer;
-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="close"
>
<ion-icon name="close-outline"></ion-icon>
</button>
{% if time_sheets %}
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div>{% trans "Employee" %}</div>
</div>
</div>
{% comment %}
<div class="oh-sticky-table__th">{% trans "Employee" %}</div>
{% endcomment %}
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Task" %}</div>
<div class="oh-sticky-table__th">{% trans "Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Time Spent" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% for time_sheet in time_sheets %}
<div class="oh-sticky-table__tr ui-sortable-handle"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<div class="oh-sticky-table__sd">
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{time_sheet.employee_id.employee_first_name}}
{{time_sheet.employee_id.employee_last_name|default:""}}
</span>
</div>
</div>
<div class="oh-sticky-table__td">{{time_sheet.project_id.title}}</div>
<div class="oh-sticky-table__td">{{time_sheet.task_id}}</div>
<div class="oh-sticky-table__td">{{time_sheet.date}}</div>
<div class="oh-sticky-table__td">{{time_sheet.time_spent}}</div>
<div class="oh-sticky-table__td">{{time_sheet.get_status_display}}</div>
<div class="oh-sticky-table__td">{{time_sheet.description|truncatechars:15}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group " >
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#TimesheetUpdateModal"
hx-get="{% url 'update-timesheet-task' time_sheet.id %}"
hx-target="#TimesheetUpdateTarget"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<button
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
id="time_sheet.id"
onclick=" deleteTimeSheet(this,{{time_sheet.id}});"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</button>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 80px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:15px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}
</div>

File diff suppressed because one or more lines are too long

View File

@@ -1,115 +1,115 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-task-all' %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% comment %} modals for showing new project and new task creation {% endcomment %}
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="ProjectStageModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function(){
$("#project_stage").html("<option>-------</option>");
$(document).on("change","#id_project",function(){
project_id = $(this).val()
if (project_id === 'create_new_project'){
$.ajax({
type: "GET",
url: '/project/create-project-time-sheet',
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if ($.isNumeric(project_id)) {
$.ajax({
type:"GET",
url:"/project/get-stages",
data:{'project_id':project_id},
beforeSend: function () {
$(".errorlist").remove();
},
success:function(response){
$('#project_stage').html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#project_stage").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
}
});
} else {
$("#project_stage").html("<option>---------</option>");
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>")
}
});
$(document).on("change","#project_stage",function(e){
stage_id = $(this).val()
if (stage_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: project_id },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
});
</script>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'create-task-all' %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% comment %} modals for showing new project and new task creation {% endcomment %}
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="ProjectStageModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function(){
$("#project_stage").html("<option>-------</option>");
$(document).on("change","#id_project",function(){
project_id = $(this).val()
if (project_id === 'create_new_project'){
$.ajax({
type: "GET",
url: '/project/create-project-time-sheet',
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if ($.isNumeric(project_id)) {
$.ajax({
type:"GET",
url:"/project/get-stages",
data:{'project_id':project_id},
beforeSend: function () {
$(".errorlist").remove();
},
success:function(response){
$('#project_stage').html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#project_stage").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
}
});
} else {
$("#project_stage").html("<option>---------</option>");
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>")
}
});
$(document).on("change","#project_stage",function(e){
stage_id = $(this).val()
if (stage_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: project_id },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
});
</script>

View File

@@ -1,110 +1,110 @@
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task-all' task_id %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% comment %} modals for showing new project and new task creation {% endcomment %}
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="ProjectStageModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function(){
{% comment %} $("#project_stage").html("<option>-------</option>"); {% endcomment %}
$(document).on("change","#id_project",function(){
project_id = $(this).val()
if (project_id === 'create_new_project'){
$.ajax({
type: "GET",
url: '/project/create-project-time-sheet',
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if ($.isNumeric(project_id)) {
$.ajax({
type:"GET",
url:"/project/get-stages",
data:{'project_id':project_id},
beforeSend: function () {
$(".errorlist").remove();
},
success:function(response){
$('#project_stage').html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#project_stage").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
}
});
} else {
$("#project_stage").html("<option>---------</option>");
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>")
}
});
$(document).on("change","#project_stage",function(e){
task_id = $(this).val()
if (task_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: project_id },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
});
</script>
{% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TaskFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task-all' task_id %}"
hx-target="#TaskUpdateTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
{% comment %} modals for showing new project and new task creation {% endcomment %}
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="ProjectStageModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function(){
{% comment %} $("#project_stage").html("<option>-------</option>"); {% endcomment %}
$(document).on("change","#id_project",function(){
project_id = $(this).val()
if (project_id === 'create_new_project'){
$.ajax({
type: "GET",
url: '/project/create-project-time-sheet',
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if ($.isNumeric(project_id)) {
$.ajax({
type:"GET",
url:"/project/get-stages",
data:{'project_id':project_id},
beforeSend: function () {
$(".errorlist").remove();
},
success:function(response){
$('#project_stage').html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#project_stage").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>");
}
});
} else {
$("#project_stage").html("<option>---------</option>");
$("#project_stage").append( "<option value='create_new_stage'>Create a new Stage</option>")
}
});
$(document).on("change","#project_stage",function(e){
task_id = $(this).val()
if (task_id === "create_new_stage") {
$.ajax({
type: "GET",
url: '/project/create-stage-taskall',
data: { project_id: project_id },
success: function (response) {
$("#ProjectStageModal").addClass("oh-modal--show");
$("#ProjectStageFormTarget").html(response);
},
});
}
});
});
</script>

View File

@@ -1,157 +1,157 @@
{% load i18n %}
{% load static %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=to_do&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "To Do" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=in_progress&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=completed&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=expired&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Tomato"></span>
{% trans "Expired" %}
</span>
</div>
{% if tasks %}
<div class="oh-layout--grid-3">
{% for task in tasks %}
<div class="oh-kanban-card {% if task.status == 'to_do'%} to-do-task
{% elif task.status == 'in_progress' %} in-progress-task
{% elif task.status == 'completed' %} completed-task
{% else %} expired-task
{% endif %} "
style="color: inherit;text-decoration: none;">
<a hx-get="{% url 'task-details' task.id %}" hx-target="#TaskDetailsTarget" data-toggle='oh-modal-toggle' data-target = '#TaskModal' style="color: inherit;text-decoration: none; display: flex; width:550px">
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
<img
src="https://ui-avatars.com/api/?name={{task.title}}&background=random"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{task.title}}</span>
<span class="oh-kanban-card__subtitle">{% trans "Project Name" %}: {{task.project}}</span><br>
<span class="oh-kanban-card__subtitle">{% trans "Stage Name" %} : {{task.stage}}</span><br/>
<span class="oh-kanban-card__subtitle">{% trans "End Date" %} : {{task.end_date}}</span>
</div>
</a>
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" title="Options" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
{% if perms.recruitment.change_candidate %}
<li class="oh-dropdown__item">
<a hx-get="{% url 'update-task-all' task.id %}" hx-target='#TaskDetailsTarget' data-toggle = 'oh-modal-toggle' data-target="#TaskModal" hx-swap='innerHTML' class="oh-dropdown__link">{% trans "Edit" %}</a>
</li>
{% endif %}
<li class="oh-dropdown__item">
{% if task.is_active %}
<a href="{% url 'task-all-archive' task.id %}" onclick="return confirm('{% trans 'Do you want to archive this task?' %}')" class="oh-dropdown__link">
{% trans "Archive" %}
</a>
{% else %}
<a href="{% url 'task-all-archive' task.id %}" onclick="return confirm('{% trans 'Do you want to un archive this task?' %}')" class="oh-dropdown__link">
{% trans "Un-Archive" %}
</a>
{% endif %}
</li>
{% if perms.recruitment.delete_candidate %}
<li class="oh-dropdown__item">
<form action="{% url 'delete-task' task.id %}?task_all=true" onsubmit="return confirm('{% trans "Do you want to delete this candidate?" %}')" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span
class="oh-pagination__page"
data-toggle="modal"
data-target="#addEmployeeModal"
>
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span
>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{data.number}}"
hx-get="{% url 'search-candidate' %}?{{pd}}&view=card"
hx-target="#section"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page=1&view=card" class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.previous_page_number }}&view=card" class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.next_page_number }}&view=card" class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.paginator.num_pages }}&view=card" class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% load i18n %}
{% load static %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=to_do&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "To Do" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=in_progress&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=completed&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=expired&view=card" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Tomato"></span>
{% trans "Expired" %}
</span>
</div>
{% if tasks %}
<div class="oh-layout--grid-3">
{% for task in tasks %}
<div class="oh-kanban-card {% if task.status == 'to_do'%} to-do-task
{% elif task.status == 'in_progress' %} in-progress-task
{% elif task.status == 'completed' %} completed-task
{% else %} expired-task
{% endif %} "
style="color: inherit;text-decoration: none;">
<a hx-get="{% url 'task-details' task.id %}" hx-target="#TaskDetailsTarget" data-toggle='oh-modal-toggle' data-target = '#TaskModal' style="color: inherit;text-decoration: none; display: flex; width:550px">
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
<img
src="https://ui-avatars.com/api/?name={{task.title}}&background=random"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{task.title}}</span>
<span class="oh-kanban-card__subtitle">{% trans "Project Name" %}: {{task.project}}</span><br>
<span class="oh-kanban-card__subtitle">{% trans "Stage Name" %} : {{task.stage}}</span><br/>
<span class="oh-kanban-card__subtitle">{% trans "End Date" %} : {{task.end_date}}</span>
</div>
</a>
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" title="Options" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
{% if perms.recruitment.change_candidate %}
<li class="oh-dropdown__item">
<a hx-get="{% url 'update-task-all' task.id %}" hx-target='#TaskDetailsTarget' data-toggle = 'oh-modal-toggle' data-target="#TaskModal" hx-swap='innerHTML' class="oh-dropdown__link">{% trans "Edit" %}</a>
</li>
{% endif %}
<li class="oh-dropdown__item">
{% if task.is_active %}
<a href="{% url 'task-all-archive' task.id %}" onclick="return confirm('{% trans 'Do you want to archive this task?' %}')" class="oh-dropdown__link">
{% trans "Archive" %}
</a>
{% else %}
<a href="{% url 'task-all-archive' task.id %}" onclick="return confirm('{% trans 'Do you want to un archive this task?' %}')" class="oh-dropdown__link">
{% trans "Un-Archive" %}
</a>
{% endif %}
</li>
{% if perms.recruitment.delete_candidate %}
<li class="oh-dropdown__item">
<form action="{% url 'delete-task' task.id %}?task_all=true" onsubmit="return confirm('{% trans "Do you want to delete this candidate?" %}')" method="post">
{% csrf_token %}
<button class="oh-dropdown__link oh-dropdown__link--danger ">{% trans "Delete" %}</button>
</form>
</li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span
class="oh-pagination__page"
data-toggle="modal"
data-target="#addEmployeeModal"
>
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span
>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{data.number}}"
hx-get="{% url 'search-candidate' %}?{{pd}}&view=card"
hx-target="#section"
min="1"
/>
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page=1&view=card" class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.previous_page_number }}&view=card" class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.next_page_number }}&view=card" class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.paginator.num_pages }}&view=card" class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}

View File

@@ -1,48 +1,48 @@
{% load i18n %} {% load basefilters %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');"
>{% trans "Task" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{f.form.task_manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{f.form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{f.form.project}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button
class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton"
type="submit"
>
{% trans "Filter" %}
</button>
</div>
{% load i18n %} {% load basefilters %}
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header"
onclick="event.stopImmediatePropagation();$(this).parent().toggleClass('oh-accordion--show');"
>{% trans "Task" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Manager" %}</label>
{{f.form.task_manager}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Stage" %}</label>
{{f.form.stage}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{f.form.project}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "End Date" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button
class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton"
type="submit"
>
{% trans "Filter" %}
</button>
</div>

View File

@@ -1,180 +1,180 @@
{% load i18n %}
{% load static %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% if tasks %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=to_do&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "To Do" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=in_progress&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=completed&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=expired&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Tomato"></span>
{% trans "Expired" %}
</span>
</div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th" >
<div class="d-flex">
<div class="">
<input type="checkbox" class="oh-input oh-input__checkbox mt-1 mr-2 all-task-all" />
</div>
<div hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&orderby=name&view=list">
{% trans "Task" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Mangers" %}</div>
<div class="oh-sticky-table__th">{% trans "Members" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
{% comment %} <div class="oh-sticky-table__th"></div> {% endcomment %}
<div class="oh-sticky-table__th"></div>
</div>
</div>
{% for task in tasks %}
<div class="oh-sticky-table__tbody ui-sortable" hx-get="{% url 'task-details' task.id %}" hx-target="#TaskDetailsTarget" data-toggle='oh-modal-toggle' data-target = '#TaskModal'>
<div class="oh-sticky-table__tr ui-sortable-handle">
<div class="oh-sticky-table__sd {% if task.status == 'to_do'%} to-do-task
{% elif task.status == 'in_progress' %} in-progress-task
{% elif task.status == 'completed' %} completed-task
{% else %} expired-task
{% endif %}">
<div class="d-flex">
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{task.id}}"
class="oh-input candidate-checkbox oh-input__checkbox mt-2 mr-2 all-task-all-row"
/>
</div>
<div class="oh-profile oh-profile--md">
<span class="oh-profile__name oh-text--dark">{{task.title}}</span>
</div>
</div>
</div>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.project}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.stage}}</a>
<a href="#" style="color: inherit;text-decoration: none;"
class="oh-sticky-table__td">{{task.task_manager}} </a>
<a href="#" style="color: inherit;text-decoration: none;"
class="oh-sticky-table__td">
{% for member in task.task_members.all %}
{{member}},
{% endfor %}
</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.end_date}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.get_status_display}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.description}}</a>
{% comment %} <a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">
{% if perms.recruitment.view_history %}
<button hx-get="{% url 'candidate-history' cand.id %}" hx-target='#section'
class="oh-btn oh-btn--info">history</button>
{% endif %}
</a> {% endcomment %}
<div href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">
<div class="oh-btn-group">
{% comment %} {% if perms.recruitment.change_candidate %} {% endcomment %}
<a hx-get="{% url 'update-task-all' task.id %}" hx-target='#TaskDetailsTarget' data-toggle = 'oh-modal-toggle' data-target="#TaskModal" hx-swap='innerHTML'
class="oh-btn oh-btn--light-bkg w-100" title="Edit"><ion-icon name="create-outline"></ion-icon></a>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.recruitment.delete_candidate %} {% endcomment %}
<form action="{% url 'delete-task' task.id %}?task_all=true" method='post'
onsubmit="Are you sure want to delete this candidate?">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
title="Remove"><ion-icon name="trash-outline"></ion-icon></button>
</form>
{% comment %} {% endif %} {% endcomment %}
</div>
</div>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% comment %} pagination {% endcomment %}
<div class="oh-pagination">
<span class="oh-pagination__page" data-toggle="modal" data-target="#addEmployeeModal">
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input type="number" name="page" class="oh-pagination__input" value="{{data.number}}"
hx-get="{% url 'search-candidate' %}?{{pd}}&view=list" hx-target="#section" min="1" />
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page=1&view=list"
class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section'
hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.previous_page_number }}&view=list"
class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.next_page_number }}&view=list"
class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section'
hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.paginator.num_pages }}&view=list"
class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
<script src="{% static '/task_all/task_all_action.js' %}"></script>
{% load i18n %}
{% load static %}
{% if messages %}
<div class="oh-wrapper">
{% for message in messages %}
<div class="oh-alert-container">
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% if tasks %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=to_do&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:yellowgreen"></span>
{% trans "To Do" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=in_progress&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=completed&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
<span class="m-3 draft" hx-get="{% url 'task-all-filter' %}?{{pd}}&status=expired&view=list" hx-target="#view-container" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Tomato"></span>
{% trans "Expired" %}
</span>
</div>
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr">
<div class="oh-sticky-table__th" >
<div class="d-flex">
<div class="">
<input type="checkbox" class="oh-input oh-input__checkbox mt-1 mr-2 all-task-all" />
</div>
<div hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&orderby=name&view=list">
{% trans "Task" %}
</div>
</div>
</div>
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Stage" %}</div>
<div class="oh-sticky-table__th">{% trans "Mangers" %}</div>
<div class="oh-sticky-table__th">{% trans "Members" %}</div>
<div class="oh-sticky-table__th">{% trans "End Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
{% comment %} <div class="oh-sticky-table__th"></div> {% endcomment %}
<div class="oh-sticky-table__th"></div>
</div>
</div>
{% for task in tasks %}
<div class="oh-sticky-table__tbody ui-sortable" hx-get="{% url 'task-details' task.id %}" hx-target="#TaskDetailsTarget" data-toggle='oh-modal-toggle' data-target = '#TaskModal'>
<div class="oh-sticky-table__tr ui-sortable-handle">
<div class="oh-sticky-table__sd {% if task.status == 'to_do'%} to-do-task
{% elif task.status == 'in_progress' %} in-progress-task
{% elif task.status == 'completed' %} completed-task
{% else %} expired-task
{% endif %}">
<div class="d-flex">
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{task.id}}"
class="oh-input candidate-checkbox oh-input__checkbox mt-2 mr-2 all-task-all-row"
/>
</div>
<div class="oh-profile oh-profile--md">
<span class="oh-profile__name oh-text--dark">{{task.title}}</span>
</div>
</div>
</div>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.project}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.stage}}</a>
<a href="#" style="color: inherit;text-decoration: none;"
class="oh-sticky-table__td">{{task.task_manager}} </a>
<a href="#" style="color: inherit;text-decoration: none;"
class="oh-sticky-table__td">
{% for member in task.task_members.all %}
{{member}},
{% endfor %}
</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.end_date}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.get_status_display}}</a>
<a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">{{task.description}}</a>
{% comment %} <a href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">
{% if perms.recruitment.view_history %}
<button hx-get="{% url 'candidate-history' cand.id %}" hx-target='#section'
class="oh-btn oh-btn--info">history</button>
{% endif %}
</a> {% endcomment %}
<div href="#" style="color: inherit;text-decoration: none;" class="oh-sticky-table__td">
<div class="oh-btn-group">
{% comment %} {% if perms.recruitment.change_candidate %} {% endcomment %}
<a hx-get="{% url 'update-task-all' task.id %}" hx-target='#TaskDetailsTarget' data-toggle = 'oh-modal-toggle' data-target="#TaskModal" hx-swap='innerHTML'
class="oh-btn oh-btn--light-bkg w-100" title="Edit"><ion-icon name="create-outline"></ion-icon></a>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.recruitment.delete_candidate %} {% endcomment %}
<form action="{% url 'delete-task' task.id %}?task_all=true" method='post'
onsubmit="Are you sure want to delete this candidate?">
{% csrf_token %}
<button type='submit' class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
title="Remove"><ion-icon name="trash-outline"></ion-icon></button>
</form>
{% comment %} {% endif %} {% endcomment %}
</div>
</div>
</a>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% comment %} pagination {% endcomment %}
<div class="oh-pagination">
<span class="oh-pagination__page" data-toggle="modal" data-target="#addEmployeeModal">
{% trans "Page" %} {{ data.number }} {% trans "of" %} {{ data.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input type="number" name="page" class="oh-pagination__input" value="{{data.number}}"
hx-get="{% url 'search-candidate' %}?{{pd}}&view=list" hx-target="#section" min="1" />
<span class="oh-pagination__label">{% trans "of" %} {{data.paginator.num_pages}}</span>
</div>
<ul class="oh-pagination__items">
{% if data.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page=1&view=list"
class="oh-pagination__link">{% trans "First" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section'
hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.previous_page_number }}&view=list"
class="oh-pagination__link">{% trans "Previous" %}</a>
</li>
{% endif %}
{% if data.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section' hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.next_page_number }}&view=list"
class="oh-pagination__link">{% trans "Next" %}</a>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a hx-target='#section'
hx-get="{% url 'search-candidate' %}?{{pd}}&page={{ data.paginator.num_pages }}&view=list"
class="oh-pagination__link">{% trans "Last" %}</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/task.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available tasks; please create a new one." %}</h3>
</div>
</div>
{% endif %}
<script src="{% static '/task_all/task_all_action.js' %}"></script>

View File

@@ -1,168 +1,168 @@
{% load i18n %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Tasks" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% if tasks %}
<form
hx-get="{% url 'task-all-filter' %}?view={{view_type}}""
hx-target="#view-container"
hx-swap="innerHTML"
id="taskAllFilterForm"
class="d-flex"
onsubmit="event.preventDefault()"
>
{% comment %} for search{% endcomment %}
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-task-all"
name="search"
placeholder="{% trans 'Search' %}"
onkeyup="$('.filterButton')[0].click()"
/>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
{% comment %} for list view {% endcomment %}
<li class="oh-view-type task-all-view-type" data-view="list">
<a
data-view="list"
class="oh-btn oh-btn--view"
hx-get= "{% url 'task-all-filter' %}?view=list"
hx-target="#view-container"
>
<ion-icon name="list-outline"></ion-icon>
</a>
</li>
{% comment %} for card view {% endcomment %}
<li class="oh-view-type task-all-view-type" data-view="card">
<a
class="oh-btn oh-btn--view"
hx-get= "{% url 'task-all-filter' %}"
hx-target="#view-container"
>
<ion-icon name="grid-outline"></ion-icon
></a>
</li>
</ul>
</div>
{% comment %} for filtering {% endcomment %}
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn ml-2"
@click="open = !open"
onclick="event.preventDefault()"
>
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'task_all/task_all_filter.html' %}
</div>
</div>
</form>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveTaskAll"
>{% trans "Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveTaskAll"
>{% trans "Un-Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteTaskAll"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
{% endif %}
{% comment %} for create {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
hx-get="{% url 'create-task-all' %}"
hx-target="#TaskUpdateTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans " Task" %}
</a>
</div>
</div>
</div>
</div>
</section>
<script>
$(document).ready(function(){
});
</script>
{% load i18n %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Tasks" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
<div class="oh-main__titlebar oh-main__titlebar--right">
{% if tasks %}
<form
hx-get="{% url 'task-all-filter' %}?view={{view_type}}""
hx-target="#view-container"
hx-swap="innerHTML"
id="taskAllFilterForm"
class="d-flex"
onsubmit="event.preventDefault()"
>
{% comment %} for search{% endcomment %}
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-task-all"
name="search"
placeholder="{% trans 'Search' %}"
onkeyup="$('.filterButton')[0].click()"
/>
</div>
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
{% comment %} for list view {% endcomment %}
<li class="oh-view-type task-all-view-type" data-view="list">
<a
data-view="list"
class="oh-btn oh-btn--view"
hx-get= "{% url 'task-all-filter' %}?view=list"
hx-target="#view-container"
>
<ion-icon name="list-outline"></ion-icon>
</a>
</li>
{% comment %} for card view {% endcomment %}
<li class="oh-view-type task-all-view-type" data-view="card">
<a
class="oh-btn oh-btn--view"
hx-get= "{% url 'task-all-filter' %}"
hx-target="#view-container"
>
<ion-icon name="grid-outline"></ion-icon
></a>
</li>
</ul>
</div>
{% comment %} for filtering {% endcomment %}
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn ml-2"
@click="open = !open"
onclick="event.preventDefault()"
>
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
<div id="filterCount"></div>
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'task_all/task_all_filter.html' %}
</div>
</div>
</form>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveTaskAll"
>{% trans "Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveTaskAll"
>{% trans "Un-Archive" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteTaskAll"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
{% endif %}
{% comment %} for create {% endcomment %}
<div class="oh-main__titlebar-button-container">
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TaskUpdateModal"
hx-get="{% url 'create-task-all' %}"
hx-target="#TaskUpdateTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans " Task" %}
</a>
</div>
</div>
</div>
</div>
</section>
<script>
$(document).ready(function(){
});
</script>

View File

@@ -1,123 +1,122 @@
{% extends 'index.html' %}
{% load static %}
{% block content %}
<style>
.to-do-task{
border-left: solid 5px yellowgreen !important;
border-radius: 5px 0 0 5px;
}
.in-progress-task{
border-left: solid 5px Orange !important;
border-radius: 5px 0 0 5px;
}
.completed-task{
border-left: solid 5px DodgerBlue !important;
border-radius: 5px 0 0 5px;
}
.expired-task{
border-left: solid 5px Tomato !important;
border-radius: 5px 0 0 5px;
}
</style>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'task_all/task_all_navbar.html' %}
</main>
<div id="view-container" class="oh-wrapper">
{% if view_type == 'list' %}
{% include 'task_all/task_all_list.html' %}
{% else %}
{% include 'task_all/task_all_card.html' %}
{% endif %}
</div>
<div
class="oh-modal"
id="TaskModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskDetailsTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskUpdateTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskTimesheetModal"
role="dialog"
aria-labelledby="TaskTimesheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width:1350px"
id="TaskTimesheetTarget"
></div>
</div>
<div
class="oh-modal"
id="TimesheetUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TimesheetUpdateTarget"
></div>
</div>
<script>
$(document).ready(function(){
$("#filter-task-all").keyup(function (e) {
let search = $(this).val()
$(".task-all-view-type").attr("hx-vals", `{"search":"${search}"}`);
$("#filter-task-all").attr("hx-vals",`{'search':"${search}"}`)
});
$(".task-all-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);
console.log(view)
$("#taskAllFilterForm").attr("hx-vals", `{"view":"${view}"}`);
$('#filter-task-all').attr("hx-vals", `{"view":"${view}"}`);
}
});
});
</script>
{% endblock content %}
{% extends 'index.html' %}
{% load static %}
{% block content %}
<style>
.to-do-task{
border-left: solid 5px yellowgreen !important;
border-radius: 5px 0 0 5px;
}
.in-progress-task{
border-left: solid 5px Orange !important;
border-radius: 5px 0 0 5px;
}
.completed-task{
border-left: solid 5px DodgerBlue !important;
border-radius: 5px 0 0 5px;
}
.expired-task{
border-left: solid 5px Tomato !important;
border-radius: 5px 0 0 5px;
}
</style>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'task_all/task_all_navbar.html' %}
</main>
<div id="view-container" class="oh-wrapper">
{% if view_type == 'list' %}
{% include 'task_all/task_all_list.html' %}
{% else %}
{% include 'task_all/task_all_card.html' %}
{% endif %}
</div>
<div
class="oh-modal"
id="TaskModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskDetailsTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskUpdateTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskTimesheetModal"
role="dialog"
aria-labelledby="TaskTimesheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width:1350px"
id="TaskTimesheetTarget"
></div>
</div>
<div
class="oh-modal"
id="TimesheetUpdateModal"
role="dialog"
aria-labelledby="TaskUpdateModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TimesheetUpdateTarget"
></div>
</div>
<script>
$(document).ready(function(){
$("#filter-task-all").keyup(function (e) {
let search = $(this).val()
$(".task-all-view-type").attr("hx-vals", `{"search":"${search}"}`);
$("#filter-task-all").attr("hx-vals",`{'search':"${search}"}`)
});
$(".task-all-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);
console.log(view)
$("#taskAllFilterForm").attr("hx-vals", `{"view":"${view}"}`);
$('#filter-task-all').attr("hx-vals", `{"view":"${view}"}`);
}
});
});
</script>
{% endblock content %}

View File

@@ -1,42 +1,42 @@
{% load i18n %}
<div class="oh-modal__dialog-header ">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
id="close1"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task' task_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<script>
$(document).ready(function(){
$('#close1').click(function(e){
e.preventDefault()
e.stopPropagation()
var closestModal = $(this).closest('.oh-modal--show');
closestModal.removeClass('oh-modal--show')
// e.preventDefault()
// var parentDiv = $(this).parents().closest('oh-modal--show')
// console.log(parentDiv)
// parentDiv.classList.remove('oh-modal--show')
})
});
</script>
{% load i18n %}
<div class="oh-modal__dialog-header ">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Task" %}</h5>
<br />
</span>
<button
type="button"
id="close1"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{%url 'update-task' task_id %}"
hx-target="#TaskFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{ form.as_p }}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<script>
$(document).ready(function(){
$('#close1').click(function(e){
e.preventDefault()
e.stopPropagation()
var closestModal = $(this).closest('.oh-modal--show');
closestModal.removeClass('oh-modal--show')
// e.preventDefault()
// var parentDiv = $(this).parents().closest('oh-modal--show')
// console.log(parentDiv)
// parentDiv.classList.remove('oh-modal--show')
})
});
</script>

View File

@@ -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 %}
<div
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
>
<div
class="oh-card-dashboard__header oh-card-dashboard__header--divider d-flex justify-content-between align-items-center"
>
<div class="">
<h5 class="oh-card-dashboard__title">
Personal Timesheet of {{emp_name|capfirst}}
</h5>
</div>
<div>
<span class="oh-card-dashboard__title me-5"
><a id="previous">Previous</a></span
>
<span class="oh-card-dashboard__title me-5"><a id="next">Next</a></span>
<select
class="oh-select oh-select--sm me-5"
name=""
id="personalTimesheetSelect"
>
<option value="week" selected>Week</option>
<option value="month">Month</option>
</select>
</div>
</div>
<div class="oh-card-dashboard__body" style="width: 99%">
<canvas id="personalChart"></canvas>
</div>
</div>
{% else %} {% include '404.html' %} {% endif %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
$(document).ready(function () {
Date.prototype.getWeek = function () { return $.datepicker.iso8601Week(this); }
var myDate = new Date();
var myWeek=myDate.getWeek();
var year = myDate. getFullYear()
var month = myDate.getMonth()
function personalChart(dataSet, labels) {
const data = {
labels: labels,
datasets: dataSet,
};
// Create chart using the Chart.js library
window["personalChart"] = {};
const ctx = document.getElementById("personalChart").getContext("2d");
personalChart = new Chart(ctx, {
type: "bar",
data: data,
options: {
animation: {
duration: 1000, // Duration of the animation in milliseconds
easing: 'easeInOutQuart', // Easing function for the animation
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
}
}
},
});
}
function personalChart_one(dataSet, labels) {
personalChart.destroy()
const data = {
labels: labels,
datasets: dataSet,
};
// Create chart using the Chart.js library
window["personalChart"] = {};
const ctx = document.getElementById("personalChart").getContext("2d");
personalChart = new Chart(ctx, {
type: "bar",
data: data,
options: {
animation: {
duration: 1000, // Duration of the animation in milliseconds
easing: 'easeInOutQuart', // Easing function for the animation
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
},
});
}
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/" ,
data :{
emp_id : {{emp_id}},
year : year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val(),
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
$("#previous").click(function(){
if ($("#personalTimesheetSelect").val() == "week"){
myWeek-=1
if (myWeek<1){
year = year - 1;
myWeek = 52+myWeek;
}
}
else if ($("#personalTimesheetSelect").val() == "month"){
month-=1
if (month<0){
year=year-1;
month = 12+month
}
}
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year : year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val()
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
$("#next").click(function(){
this_week = myDate.getWeek()
this_month = myDate.getMonth()
if ($("#personalTimesheetSelect").val() == "week"){
if (myWeek < this_week){
myWeek+=1
} else {
myWeek = this_week
}
}
else if ($("#personalTimesheetSelect").val() == "month"){
if (month < this_month){
month+=1
} else {
month = this_month
}
}
year = myDate. getFullYear()
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year:year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val()
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
$("#personalTimesheetSelect").change(function(){
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year:year,
week:myWeek,
week:myWeek,
month:month,
selected:$("#personalTimesheetSelect").val(),
},
success: function (response) {
// Code to handle the response.labels
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
});
</script>
{% 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 %}
<div
class="oh-card-dashboard oh-card-dashboard--no-scale oh-card-dashboard--transparent"
>
<div
class="oh-card-dashboard__header oh-card-dashboard__header--divider d-flex justify-content-between align-items-center"
>
<div class="">
<h5 class="oh-card-dashboard__title">
Personal Timesheet of {{emp_name|capfirst}}
</h5>
</div>
<div>
<span class="oh-card-dashboard__title me-5"
><a id="previous">Previous</a></span
>
<span class="oh-card-dashboard__title me-5"><a id="next">Next</a></span>
<select
class="oh-select oh-select--sm me-5"
name=""
id="personalTimesheetSelect"
>
<option value="week" selected>Week</option>
<option value="month">Month</option>
</select>
</div>
</div>
<div class="oh-card-dashboard__body" style="width: 99%">
<canvas id="personalChart"></canvas>
</div>
</div>
{% else %} {% include '404.html' %} {% endif %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
$(document).ready(function () {
Date.prototype.getWeek = function () { return $.datepicker.iso8601Week(this); }
var myDate = new Date();
var myWeek=myDate.getWeek();
var year = myDate. getFullYear()
var month = myDate.getMonth()
function personalChart(dataSet, labels) {
const data = {
labels: labels,
datasets: dataSet,
};
// Create chart using the Chart.js library
window["personalChart"] = {};
const ctx = document.getElementById("personalChart").getContext("2d");
personalChart = new Chart(ctx, {
type: "bar",
data: data,
options: {
animation: {
duration: 1000, // Duration of the animation in milliseconds
easing: 'easeInOutQuart', // Easing function for the animation
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true,
}
}
},
});
}
function personalChart_one(dataSet, labels) {
personalChart.destroy()
const data = {
labels: labels,
datasets: dataSet,
};
// Create chart using the Chart.js library
window["personalChart"] = {};
const ctx = document.getElementById("personalChart").getContext("2d");
personalChart = new Chart(ctx, {
type: "bar",
data: data,
options: {
animation: {
duration: 1000, // Duration of the animation in milliseconds
easing: 'easeInOutQuart', // Easing function for the animation
},
scales: {
x: {
stacked: true,
},
y: {
stacked: true
}
}
},
});
}
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/" ,
data :{
emp_id : {{emp_id}},
year : year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val(),
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
$("#previous").click(function(){
if ($("#personalTimesheetSelect").val() == "week"){
myWeek-=1
if (myWeek<1){
year = year - 1;
myWeek = 52+myWeek;
}
}
else if ($("#personalTimesheetSelect").val() == "month"){
month-=1
if (month<0){
year=year-1;
month = 12+month
}
}
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year : year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val()
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
$("#next").click(function(){
this_week = myDate.getWeek()
this_month = myDate.getMonth()
if ($("#personalTimesheetSelect").val() == "week"){
if (myWeek < this_week){
myWeek+=1
} else {
myWeek = this_week
}
}
else if ($("#personalTimesheetSelect").val() == "month"){
if (month < this_month){
month+=1
} else {
month = this_month
}
}
year = myDate. getFullYear()
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year:year,
week:myWeek,
month:month,
selected : $("#personalTimesheetSelect").val()
},
success: function (response) {
// Code to handle the response
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
$("#personalTimesheetSelect").change(function(){
$.ajax({
type: "GET",
url: "/project/personal-time-sheet/",
data :{
emp_id : {{emp_id}},
year:year,
week:myWeek,
week:myWeek,
month:month,
selected:$("#personalTimesheetSelect").val(),
},
success: function (response) {
// Code to handle the response.labels
dataSet = response.dataSet;
labels = response.labels;
personalChart_one(dataSet, labels);
},
error: function(response){
console.log("error")
},
});
})
});
</script>
{% endblock %}

View File

@@ -1,66 +1,66 @@
{% load i18n %} {% load basefilters %}
<form hx-get='{% url "filter-time-sheet" %}' hx-target="#TimeSheetList" id="timesheetForm">
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Time Sheet" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{f.form.project_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Task" %}</label>
{{f.form.task}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Date" %}</label>
{{f.form.date}}
</div>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Employee" %}</label>
{{f.form.employee_id}}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date From" %}</label>
{{f.form.start_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Till End Date" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton">
{% trans "Filter" %}
</button>
</div>
</form>
{% load i18n %} {% load basefilters %}
<form hx-get='{% url "filter-time-sheet" %}' hx-target="#TimeSheetList" id="timesheetForm">
<div class="oh-dropdown__filter-body">
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Time Sheet" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Project" %}</label>
{{f.form.project_id}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Status" %}</label>
{{f.form.status}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Task" %}</label>
{{f.form.task}}
</div>
<div class="oh-input-group">
<label class="oh-label">{% trans "Date" %}</label>
{{f.form.date}}
</div>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Employee" %}</label>
{{f.form.employee_id}}
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="oh-accordion">
<div class="oh-accordion-header">{% trans "Advanced" %}</div>
<div class="oh-accordion-body">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Start Date From" %}</label>
{{f.form.start_from}}
</div>
</div>
<div class="col-sm-12 col-md-12 col-lg-6">
<div class="oh-input-group">
<label class="oh-label">{% trans "Till End Date" %}</label>
{{f.form.end_till}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="oh-dropdown__filter-footer">
<button class="oh-btn oh-btn--secondary oh-btn--small w-100 filterButton">
{% trans "Filter" %}
</button>
</div>
</form>

View File

@@ -1,126 +1,126 @@
{% block content %} {% load static %} {% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Time Sheet" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TimeSheetFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-time-sheet' %}"
hx-target="#TimeSheetFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{form.as_p}}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function () {
var project_id;
$("#id_task_id").html("<option>---------</option>");
$("#id_project").change(function (e) {
project_id = $(this).val(); // Set the project_id value here
var createProjectUrl = "{% url 'create-project-time-sheet' %}";
if (project_id === "create_new_project") {
$.ajax({
type: "GET",
url: createProjectUrl,
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if (project_id != "create_new_project") {
$.ajax({
type: "GET",
url: "{% url 'time-sheet-initial' %}",
data: { project_id: project_id },
beforeSend: function () {
$(".errorlist").remove();
},
success: function (response) {
$("#id_task_id").html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#id_task_id").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#id_task_id").append( "<option value='create_new_task'>Create a new task</option>");
},
});
} else {
$("#id_task_id").html("<option>---------</option>");
}
});
$("#id_task_id").change(function (e) {
var task_id = $(this).val();
var createTaskUrl = "{% url 'create-task-time-sheet' %}";
if (task_id === "create_new_task") {
$.ajax({
type: "GET",
url: createTaskUrl,
data: { project_id: project_id },
success: function (response) {
$("#TaskModal").addClass("oh-modal--show");
$("#TaskFormTarget").html(response);
},
});
}
});
});
</script>
{% endblock content %}
{% block content %} {% load static %} {% load i18n %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5>{% trans "Time Sheet" %}</h5>
<br />
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#TimeSheetFormTarget"
onclick="location.reload()"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<form
hx-post="{% url 'create-time-sheet' %}"
hx-target="#TimeSheetFormTarget"
hx-encoding="multipart/form-data"
>
{% csrf_token %} {{form.as_p}}
<div class="oh-modal__dialog-footer">
<button type="submit" class="oh-btn oh-btn--secondary oh-btn--shadow">
{% trans "Save" %}
</button>
</div>
</form>
</div>
<div
class="oh-modal"
id="ProjectModal"
role="dialog"
aria-labelledby="ProjectModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectFormTarget"
></div>
</div>
<div
class="oh-modal"
id="TaskModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TaskFormTarget"
></div>
</div>
<div
class="oh-modal"
id="ProjectStageModal"
role="dialog"
aria-labelledby="TaskModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="ProjectStageFormTarget"
></div>
</div>
<script>
$(document).ready(function () {
var project_id;
$("#id_task_id").html("<option>---------</option>");
$("#id_project").change(function (e) {
project_id = $(this).val(); // Set the project_id value here
var createProjectUrl = "{% url 'create-project-time-sheet' %}";
if (project_id === "create_new_project") {
$.ajax({
type: "GET",
url: createProjectUrl,
success: function (response) {
$("#ProjectModal").addClass("oh-modal--show");
$("#ProjectFormTarget").html(response);
},
});0
}
if (project_id != "create_new_project") {
$.ajax({
type: "GET",
url: "{% url 'time-sheet-initial' %}",
data: { project_id: project_id },
beforeSend: function () {
$(".errorlist").remove();
},
success: function (response) {
$("#id_task_id").html("<option>---------</option>");
for (let i = 0; i < response.data.length; i++) {
const element = response.data[i];
$("#id_task_id").append("<option value="+ element.id + ">"+element.title+"</option>");
}
$("#id_task_id").append( "<option value='create_new_task'>Create a new task</option>");
},
});
} else {
$("#id_task_id").html("<option>---------</option>");
}
});
$("#id_task_id").change(function (e) {
var task_id = $(this).val();
var createTaskUrl = "{% url 'create-task-time-sheet' %}";
if (task_id === "create_new_task") {
$.ajax({
type: "GET",
url: createTaskUrl,
data: { project_id: project_id },
success: function (response) {
$("#TaskModal").addClass("oh-modal--show");
$("#TaskFormTarget").html(response);
},
});
}
});
});
</script>
{% endblock content %}

View File

@@ -27,8 +27,8 @@
-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"
>
<ion-icon name="close-outline"></ion-icon>
</button>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,149 +1,149 @@
{% load i18n %}
{% load static %}
{% load basefilters %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% load i18n %}
{% load static %}
{% load basefilters %}
{% if messages %}
<div class="oh-alert-container">
{% for message in messages %}
<div class="oh-alert oh-alert--animated {{message.tags}}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=in_progress&view=card" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=completed&view=card" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
</div>
{% if time_sheets %}
<div class="oh-layout--grid-3">
{% for time_sheet in time_sheets %}
<div class="oh-kanban-card
{% if time_sheet.status == 'in_Progress' %} in-progress-time-sheet
{% else %} completed-time-sheet
{% endif %}"
style="color: inherit;text-decoration: none;">
<a data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
style="color: inherit;text-decoration: none; display: flex;">
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{time_sheet.employee_id}}</span>
<span class="oh-kanban-card__subtitle">{{time_sheet.date}}</span><br>
<span class="oh-kanban-card__subtitle"><b>{{time_sheet.project_id}}</b></span><br>
<span class="oh-kanban-card__subtitle"><b>{{time_sheet.task_id}}</b> | </span>
<span class="oh-kanban-card__subtitle">{% trans "Time Spent" %} : <b>{{time_sheet.time_spent}}</b></span>
</div>
</a>
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a hx-get="{% url 'update-time-sheet' time_sheet.id %}?view=card" hx-target="#TimeSheetFormTarget" class="oh-dropdown__link" data-toggle="oh-modal-toggle" data-target="#TimeSheetModal">{% trans "Edit" %}</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-time-sheet' time_sheet.id %}?view=card" method="post" onsubmit="return confirm('{% trans "Do you want to delete this employee?" %}')">
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{time_sheets.number}}"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&view=card"
hx-target="#TimeSheetList"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{time_sheet.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if time_sheets.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page=1&view=card"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.previous_page_number }}&view=card"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if time_sheets.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.next_page_number }}&view=card"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.paginator.num_pages }}&view=card"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}
{% include "filter_tags.html" %}
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=in_progress&view=card" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=completed&view=card" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
</div>
{% if time_sheets %}
<div class="oh-layout--grid-3">
{% for time_sheet in time_sheets %}
<div class="oh-kanban-card
{% if time_sheet.status == 'in_Progress' %} in-progress-time-sheet
{% else %} completed-time-sheet
{% endif %}"
style="color: inherit;text-decoration: none;">
<a data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
style="color: inherit;text-decoration: none; display: flex;">
<div class="oh-kanban-card__avatar">
<div class="oh-kanban-card__profile-container">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
</div>
<div class="oh-kanban-card__details">
<span class="oh-kanban-card__title">{{time_sheet.employee_id}}</span>
<span class="oh-kanban-card__subtitle">{{time_sheet.date}}</span><br>
<span class="oh-kanban-card__subtitle"><b>{{time_sheet.project_id}}</b></span><br>
<span class="oh-kanban-card__subtitle"><b>{{time_sheet.task_id}}</b> | </span>
<span class="oh-kanban-card__subtitle">{% trans "Time Spent" %} : <b>{{time_sheet.time_spent}}</b></span>
</div>
</a>
<div class="oh-kanban-card__dots">
<div class="oh-dropdown" x-data="{show: false}">
<button class="oh-btn oh-btn--transparent text-muted p-3" @click="show = !show">
<ion-icon name="ellipsis-vertical-sharp" role="img" class="md hydrated" aria-label="ellipsis vertical sharp"></ion-icon>
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--dark-border oh-dropdown__menu--right" x-show="show" @click.outside="show = false" style="display: none;">
<ul class="oh-dropdown__items">
<li class="oh-dropdown__item">
<a hx-get="{% url 'update-time-sheet' time_sheet.id %}?view=card" hx-target="#TimeSheetFormTarget" class="oh-dropdown__link" data-toggle="oh-modal-toggle" data-target="#TimeSheetModal">{% trans "Edit" %}</a>
</li>
<li class="oh-dropdown__item">
<form action="{% url 'delete-time-sheet' time_sheet.id %}?view=card" method="post" onsubmit="return confirm('{% trans "Do you want to delete this employee?" %}')">
{% csrf_token %}
<button type="submit" class="oh-dropdown__link oh-dropdown__link--danger">{% trans "Delete" %}</button>
</form>
</li>
</ul>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{time_sheets.number}}"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&view=card"
hx-target="#TimeSheetList"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{time_sheet.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if time_sheets.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page=1&view=card"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.previous_page_number }}&view=card"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if time_sheets.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.next_page_number }}&view=card"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.paginator.num_pages }}&view=card"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}

View File

@@ -1,188 +1,188 @@
{% load i18n %} {% load yes_no %} {% load static %}
{% include "filter_tags.html" %}
{% if time_sheets %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=in_progress&view=list" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=completed&view=list" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
</div>
{% comment %} table of contents {% endcomment %}
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr ">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input type="checkbox" class="oh-input oh-input__checkbox mt-1 mr-2 all-time-sheet" />
</div>
<div>{% trans "Employee" %}</div>
</div>
</div>
{% comment %}
<div class="oh-sticky-table__th">{% trans "Employee" %}</div>
{% endcomment %}
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Task" %}</div>
<div class="oh-sticky-table__th">{% trans "Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Time Spent" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% for time_sheet in time_sheets %}
<div class="oh-sticky-table__tr ui-sortable-handle"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<div class="oh-sticky-table__sd {% if time_sheet.status == 'in_Progress' %} in-progress-time-sheet
{% else %} completed-time-sheet
{% endif %} ">
<div class="d-flex">
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{time_sheet.id}}"
class="oh-input candidate-checkbox oh-input__checkbox mt-2 mr-2 all-time-sheet-row"
/>
</div>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{time_sheet.employee_id.employee_first_name}}
{{time_sheet.employee_id.employee_last_name|default:""}}
</span>
</div>
</div>
</div>
<div class="oh-sticky-table__td">{{time_sheet.project_id.title}}</div>
<div class="oh-sticky-table__td">{{time_sheet.task_id}}</div>
<div class="oh-sticky-table__td">{{time_sheet.date}}</div>
<div class="oh-sticky-table__td">{{time_sheet.time_spent}}</div>
<div class="oh-sticky-table__td">{{time_sheet.get_status_display}}</div>
<div class="oh-sticky-table__td">{{time_sheet.description|truncatechars:15}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group " >
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'update-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-time-sheet' time_sheet.id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this time sheet?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{time_sheets.number}}"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}"
hx-target="#TimeSheetList"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{time_sheet.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if time_sheets.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if time_sheets.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}
<script src="{% static '/time_sheet/time_sheet_action.js' %}"></script>
{% load i18n %} {% load horillafilters %} {% load static %}
{% include "filter_tags.html" %}
{% if time_sheets %}
<div class="oh-tabs__content oh-tabs__content--active" style="padding-top: 10px;">
{% comment %} easy filters {% endcomment %}
<div class="d-flex flex-row-reverse">
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=in_progress&view=list" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:Orange"></span>
{% trans "In progress" %}
</span>
<span class="m-3 draft" hx-get="{% url 'filter-time-sheet' %}?{{pd}}&status=completed&view=list" hx-target="#TimeSheetList" style="cursor: pointer">
<span class="oh-dot oh-dot--small me-1" style="background-color:DodgerBlue"></span>
{% trans "Completed" %}
</span>
</div>
{% comment %} table of contents {% endcomment %}
<div class="oh-sticky-table">
<div class="oh-sticky-table__table oh-table--sortable">
<div class="oh-sticky-table__thead">
<div class="oh-sticky-table__tr ">
<div class="oh-sticky-table__th">
<div class="d-flex">
<div class="">
<input type="checkbox" class="oh-input oh-input__checkbox mt-1 mr-2 all-time-sheet" />
</div>
<div>{% trans "Employee" %}</div>
</div>
</div>
{% comment %}
<div class="oh-sticky-table__th">{% trans "Employee" %}</div>
{% endcomment %}
<div class="oh-sticky-table__th">{% trans "Project" %}</div>
<div class="oh-sticky-table__th">{% trans "Task" %}</div>
<div class="oh-sticky-table__th">{% trans "Date" %}</div>
<div class="oh-sticky-table__th">{% trans "Time Spent" %}</div>
<div class="oh-sticky-table__th">{% trans "Status" %}</div>
<div class="oh-sticky-table__th">{% trans "Description" %}</div>
<div class="oh-sticky-table__th">{% trans "Actions" %}</div>
</div>
</div>
{% for time_sheet in time_sheets %}
<div class="oh-sticky-table__tr ui-sortable-handle"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'view-single-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<div class="oh-sticky-table__sd {% if time_sheet.status == 'in_Progress' %} in-progress-time-sheet
{% else %} completed-time-sheet
{% endif %} ">
<div class="d-flex">
<div onclick="event.stopPropagation();">
<input
type="checkbox"
id="{{time_sheet.id}}"
class="oh-input candidate-checkbox oh-input__checkbox mt-2 mr-2 all-time-sheet-row"
/>
</div>
<div class="oh-profile oh-profile--md">
<div class="oh-profile__avatar mr-1">
<img
src="{{time_sheet.employee_id.get_avatar}}"
class="oh-kanban-card__profile-image"
alt="Username"
/>
</div>
<span class="oh-profile__name oh-text--dark"
>{{time_sheet.employee_id.employee_first_name}}
{{time_sheet.employee_id.employee_last_name|default:""}}
</span>
</div>
</div>
</div>
<div class="oh-sticky-table__td">{{time_sheet.project_id.title}}</div>
<div class="oh-sticky-table__td">{{time_sheet.task_id}}</div>
<div class="oh-sticky-table__td">{{time_sheet.date}}</div>
<div class="oh-sticky-table__td">{{time_sheet.time_spent}}</div>
<div class="oh-sticky-table__td">{{time_sheet.get_status_display}}</div>
<div class="oh-sticky-table__td">{{time_sheet.description|truncatechars:15}}</div>
<div class="oh-sticky-table__td" onclick="event.stopPropagation();">
<div class="oh-btn-group " >
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'update-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
>
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
style="color: blue"
aria-label="create outline"
></ion-icon>
</a>
<a
class="oh-btn oh-btn--danger-outline oh-btn--light-bkg w-100"
href="{% url 'delete-time-sheet' time_sheet.id %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this time sheet?' %}`)"
>
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="trash outline"
></ion-icon>
</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="oh-pagination">
<span class="oh-pagination__page">
{% trans "Page" %} {{ time_sheets.number }} {% trans "of" %} {{ time_sheets.paginator.num_pages }}.
</span>
<nav class="oh-pagination__nav">
<div class="oh-pagination__input-container me-3">
<span class="oh-pagination__label me-1">{% trans "Page" %}</span>
<input
type="number"
name="page"
class="oh-pagination__input"
value="{{time_sheets.number}}"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}"
hx-target="#TimeSheetList"
min="1"
/>
<span class="oh-pagination__label"
>{% trans "of" %} {{time_sheet.paginator.num_pages}}</span
>
</div>
<ul class="oh-pagination__items">
{% if time_sheets.has_previous %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page=1"
class="oh-pagination__link"
>{% trans "First" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.previous_page_number }}"
class="oh-pagination__link"
>{% trans "Previous" %}</a
>
</li>
{% endif %} {% if time_sheets.has_next %}
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.next_page_number }}"
class="oh-pagination__link"
>{% trans "Next" %}</a
>
</li>
<li class="oh-pagination__item oh-pagination__item--wide">
<a
hx-target="#TimeSheetList"
hx-get="{% url 'filter-time-sheet' %}?{{pd}}&page={{ time_sheets.paginator.num_pages }}"
class="oh-pagination__link"
>{% trans "Last" %}</a
>
</li>
{% endif %}
</ul>
</nav>
</div>
{% else %}
<div style="height: 380px; display:flex;align-items: center;justify-content: center;" class="">
<div style="" class="">
<img style="display: block;width: 200px;margin: 10px auto ;" src="{% static 'images/ui/project/timesheet.png' %}" class="" alt="Page not found. 404."/>
<h3 style="font-size:20px" class="oh-404__subtitle">{% trans "There are currently no available timesheets; please create a new one." %}</h3>
</div>
</div>
{% endif %}
<script src="{% static '/time_sheet/time_sheet_action.js' %}"></script>

View File

@@ -1,144 +1,144 @@
{% load i18n %}
{% load basefilters %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Time Sheet" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="oh-main__titlebar oh-main__titlebar--right">
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-time-sheet"
name="search"
placeholder="{% trans 'Search' %}"
hx-get="{% url 'filter-time-sheet' %}"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#TimeSheetList"
hx-swap="innerHTML"
/>
</div>
{% endif %}
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
<li class="oh-view-type time-sheet-view-type" data-view="list">
<a
hx-get="{% url 'filter-time-sheet' %}?view=list"
hx-target="#TimeSheetList"
class="oh-btn oh-btn--view"
><ion-icon name="list-outline"></ion-icon
></a>
</li>
<li class="oh-view-type time-sheet-view-type" data-view="card">
<a
hx-get="{% url 'filter-time-sheet' %}?view=card"
hx-target="#TimeSheetList"
class="oh-btn oh-btn--view"
><ion-icon name="grid-outline"></ion-icon
></a>
</li>
<li class="oh-view-type time-sheet-view-type">
<a
href="{% url 'personal-time-sheet-view' request.user.employee_get.id %}"
class="oh-btn oh-btn--view"
><ion-icon name="bar-chart"></ion-icon
></a>
</li>
</ul>
</div>
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'time_sheet/filters.html' %}
</div>
</div>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
{% comment %} <li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveEmployees"
>{% trans "Archive" %}</a
>
</li> {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
{% comment %} <li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveEmployees"
>{% trans "Un-Archive" %}</a
>
</li> {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteTimeSheet"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'create-time-sheet' %}"
hx-target="#TimeSheetFormTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
</div>
</section>
{% load i18n %}
{% load basefilters %}
<section class="oh-wrapper oh-main__topbar" x-data="{searchShow: false}">
<div class="oh-main__titlebar oh-main__titlebar--left">
<h1 class="oh-main__titlebar-title fw-bold">{% trans "Time Sheet" %}</h1>
<a
class="oh-main__titlebar-search-toggle"
role="button"
aria-label="Toggle Search"
@click="searchShow = !searchShow"
>
<ion-icon
name="search-outline"
class="oh-main__titlebar-serach-icon md hydrated"
role="img"
aria-label="search outline"
></ion-icon>
</a>
</div>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<div class="oh-main__titlebar oh-main__titlebar--right">
<div
class="oh-input-group oh-input__search-group"
:class="searchShow ? 'oh-input__search-group--show' : ''"
>
<ion-icon
name="search-outline"
class="oh-input-group__icon oh-input-group__icon--left"
></ion-icon>
<input
type="text"
class="oh-input oh-input__icon"
aria-label="Search Input"
id="filter-time-sheet"
name="search"
placeholder="{% trans 'Search' %}"
hx-get="{% url 'filter-time-sheet' %}"
hx-trigger="keyup changed delay:500ms, search"
hx-target="#TimeSheetList"
hx-swap="innerHTML"
/>
</div>
{% endif %}
<div class="oh-dropdown" x-data="{open: false}">
<ul class="oh-view-types ml-2">
<li class="oh-view-type time-sheet-view-type" data-view="list">
<a
hx-get="{% url 'filter-time-sheet' %}?view=list"
hx-target="#TimeSheetList"
class="oh-btn oh-btn--view"
><ion-icon name="list-outline"></ion-icon
></a>
</li>
<li class="oh-view-type time-sheet-view-type" data-view="card">
<a
hx-get="{% url 'filter-time-sheet' %}?view=card"
hx-target="#TimeSheetList"
class="oh-btn oh-btn--view"
><ion-icon name="grid-outline"></ion-icon
></a>
</li>
<li class="oh-view-type time-sheet-view-type">
<a
href="{% url 'personal-time-sheet-view' request.user.employee_get.id %}"
class="oh-btn oh-btn--view"
><ion-icon name="bar-chart"></ion-icon
></a>
</li>
</ul>
</div>
<div class="oh-main__titlebar-button-container">
<div class="oh-dropdown" x-data="{open: false}">
<button class="oh-btn ml-2" @click="open = !open">
<ion-icon name="filter" class="mr-1"></ion-icon>{% trans "Filter" %}
</button>
<div
class="oh-dropdown__menu oh-dropdown__menu--right oh-dropdown__filter p-4"
x-show="open"
@click.outside="open = false"
style="display: none;"
>
{% include 'time_sheet/filters.html' %}
</div>
</div>
{% comment %} for actions {% endcomment %}
<div class="oh-btn-group ml-2">
<div class="oh-dropdown" x-data="{open: false}">
<button
class="oh-btn oh-btn--dropdown oh-btn oh-btn--shadow"
@click="open = !open"
@click.outside="open = false"
>
{% trans "Actions" %}
</button>
<div class="oh-dropdown__menu oh-dropdown__menu--right" x-show="open" style="display: none;">
<ul class="oh-dropdown__items">
{% comment %} {% if perms.delete_employee %} {% endcomment %}
{% comment %} <li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="archiveEmployees"
>{% trans "Archive" %}</a
>
</li> {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
{% comment %} <li class="oh-dropdown__item">
<a href="#" class="oh-dropdown__link " id="unArchiveEmployees"
>{% trans "Un-Archive" %}</a
>
</li> {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% if perms.delete_employee %} {% endcomment %}
<li class="oh-dropdown__item">
<a
href="#"
class="oh-dropdown__link oh-dropdown__link--danger"
id="deleteTimeSheet"
>{% trans "Delete" %}</a
>
</li>
{% comment %} {% endif %} {% endcomment %}
</ul>
</div>
</div>
</div>
<div class="oh-btn-group ml-2">
<div>
<a
class="oh-btn oh-btn--secondary oh-btn--shadow"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
hx-get="{% url 'create-time-sheet' %}"
hx-target="#TimeSheetFormTarget"
>
<ion-icon class="me-2" name="add-outline"></ion-icon>{% trans "Create" %}
</a>
</div>
</div>
</div>
</div>
</section>

View File

@@ -1,94 +1,92 @@
{% load i18n %} {% load yes_no %} {% load basefilters %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">Timesheet Details</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#OneContractTarget"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Employee" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.employee_id}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Project" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.project_id}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Task" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.task_id}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.date}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Time Spent" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.time_spent}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.status}}</span>
</div>
</div>
<div class="mt-3 mb-5" style="width:100%;">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.description}}</span>
</div>
</div>
<div class="oh-modal__button-container text-center">
<div class="oh-btn-group">
<a hx-get="{% url 'update-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
class="oh-btn oh-btn--warning"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}">
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Edit" %}
</a>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<a href="{% url 'personal-time-sheet-view' time_sheet.employee_id.id %}"
class="oh-btn oh-btn--info me-1 ms-1"
style="width: 48%;">
<ion-icon
name="bar-chart"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "View Timesheet Chart" %}
</a>
{% endif %}
<a href="{% url 'delete-time-sheet' time_sheet.id %}"
class="oh-btn oh-btn--danger"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)">
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Delete" %}
</a>
</div>
</div>
</div>
{% load i18n %} {% load horillafilters %} {% load basefilters %}
<div class="oh-modal__dialog-header">
<span class="oh-modal__dialog-title" id="addEmployeeObjectiveModalLabel">
<h5 style="margin-bottom: 20px;">Timesheet Details</h5>
</span>
<button
type="button"
class="oh-modal__close"
data-dismiss="oh-modal"
aria-label="Close"
data-toggle="oh-modal-toggle"
hx-target="#OneContractTarget"
>
<ion-icon name="close-outline"></ion-icon>
</button>
<div class="oh-timeoff-modal__stats-container mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Employee" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.employee_id}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Project" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.project_id}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Task" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.task_id}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Date" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.date}}</span>
</div>
</div>
<div class="oh-timeoff-modal__stats-container mt-3 mb-3">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Time Spent" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.time_spent}}</span>
</div>
<div class="oh-timeoff-modal__stat" style="margin-left: 20px;">
<span class="oh-timeoff-modal__stat-title">{% trans "Status" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.status}}</span>
</div>
</div>
<div class="mt-3 mb-5" style="width:100%;">
<div class="oh-timeoff-modal__stat">
<span class="oh-timeoff-modal__stat-title">{% trans "Description" %}</span>
<span class="oh-timeoff-modal__stat-count">{{time_sheet.description}}</span>
</div>
</div>
<div class="oh-modal__button-container text-center">
<div class="oh-btn-group">
<a hx-get="{% url 'update-time-sheet' time_sheet.id %}"
hx-target="#TimeSheetFormTarget"
data-toggle="oh-modal-toggle"
data-target="#TimeSheetModal"
class="oh-btn oh-btn--warning"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}">
<ion-icon
name="create-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Edit" %}
</a>
{% if perms.project.view_timesheet or request.user|is_reportingmanager %}
<a href="{% url 'personal-time-sheet-view' time_sheet.employee_id.id %}"
class="oh-btn oh-btn--info me-1 ms-1"
style="width: 48%;">
<ion-icon
name="bar-chart"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "View Timesheet Chart" %}
</a>
{% endif %}
<a href="{% url 'delete-time-sheet' time_sheet.id %}"
class="oh-btn oh-btn--danger"
style="{% if perms.project.view_timesheet or request.user|is_reportingmanager %} width: 25%;{% else %}width: 50%;{% endif %}"
onclick="event.preventDefault();event.stopPropagation(); confirm(`{% trans 'Do you want to delete this project?' %}`)">
<ion-icon
name="trash-outline"
role="img"
class="md hydrated"
aria-label="create outline"
></ion-icon>{% trans "Delete" %}
</a>
</div>
</div>
</div>

View File

@@ -1,65 +1,65 @@
{% extends 'index.html' %}
{% block content %}
{% load i18n %}
{% load basefilters %}
<style>
.in-progress-time-sheet{
border-left: solid 5px Orange !important;
border-radius: 5px 0 0 5px;
}
.completed-time-sheet{
border-left: solid 5px DodgerBlue !important;
border-radius: 5px 0 0 5px;
}
</style>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'time_sheet/time_sheet_navbar.html' %}
<div id="TimeSheetList" class="oh-wrapper">
{% if view_type == "card" %}
{% include 'time_sheet/time_sheet_card_view.html' %}
{% else %}
{% include 'time_sheet/time_sheet_list_view.html' %}
{% endif %}
</div>
</main>
<div
class="oh-modal"
id="TimeSheetModal"
role="dialog"
aria-labelledby="TimeSheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TimeSheetFormTarget"
></div>
</div>
<script>
$(document).ready(function () {
$("#filter-time-sheet").keyup(function (e) {
$(".time-sheet-view-type").attr("hx-vals", `{"search":"${$(this).val()}"}`);
});
$(".time-sheet-view-type").click(function (e) {
let view = $(this).data("view");
var currentURL = window.location.href;
if (view != undefined){
if (/\?view=[^&]+/.test(currentURL)) {
newURL = currentURL.replace(/\?view=[^&]+/, "?view="+view);
}
else {
var separator = currentURL.includes('?') ? '&' : '?';
newURL = currentURL + separator + "view="+view;
}
history.pushState({}, "", newURL);
$("#filter-time-sheet").attr("hx-vals", `{"view":"${view}"}`);
$('#timesheetForm').attr("hx-vals", `{"view":"${view}"}`);
}
});
});
</script>
{% endblock content %}
{% extends 'index.html' %}
{% block content %}
{% load i18n %}
{% load basefilters %}
<style>
.in-progress-time-sheet{
border-left: solid 5px Orange !important;
border-radius: 5px 0 0 5px;
}
.completed-time-sheet{
border-left: solid 5px DodgerBlue !important;
border-radius: 5px 0 0 5px;
}
</style>
<main :class="sidebarOpen ? 'oh-main__sidebar-visible' : ''">
{% include 'time_sheet/time_sheet_navbar.html' %}
<div id="TimeSheetList" class="oh-wrapper">
{% if view_type == "card" %}
{% include 'time_sheet/time_sheet_card_view.html' %}
{% else %}
{% include 'time_sheet/time_sheet_list_view.html' %}
{% endif %}
</div>
</main>
<div
class="oh-modal"
id="TimeSheetModal"
role="dialog"
aria-labelledby="TimeSheetModal"
aria-hidden="true"
>
<div
class="oh-modal__dialog"
style="max-width: 550px"
id="TimeSheetFormTarget"
></div>
</div>
<script>
$(document).ready(function () {
$("#filter-time-sheet").keyup(function (e) {
$(".time-sheet-view-type").attr("hx-vals", `{"search":"${$(this).val()}"}`);
});
$(".time-sheet-view-type").click(function (e) {
let view = $(this).data("view");
var currentURL = window.location.href;
if (view != undefined){
if (/\?view=[^&]+/.test(currentURL)) {
newURL = currentURL.replace(/\?view=[^&]+/, "?view="+view);
}
else {
var separator = currentURL.includes('?') ? '&' : '?';
newURL = currentURL + separator + "view="+view;
}
history.pushState({}, "", newURL);
$("#filter-time-sheet").attr("hx-vals", `{"view":"${view}"}`);
$('#timesheetForm').attr("hx-vals", `{"view":"${view}"}`);
}
});
});
</script>
{% endblock content %}

View File

@@ -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

View File

@@ -1,3 +1,3 @@
from django.test import TestCase
# Create your tests here.
from django.test import TestCase
# Create your tests here.

View File

@@ -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(
'project-detailed-view/<int:project_id>/',
views.project_detailed_view,
name='project-detailed-view'
"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/<int:project_id>/",
# views.project_detailed_view,
# name="project-detailed-view",
# ),
path(
"project-detailed-view/<int:pk>/",
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/<int:project_id>/",
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/<int:project_id>/",
# views.project_update,
# name="update-project"
# ),
path(
"update-project/<int:pk>/",
projects.ProjectFormView.as_view(),
name="update-project",
),
path(
"change-project-status/<int:project_id>/",
views.change_project_status,
name="change-project-status",
),
path(
"delete-project/<int:project_id>/", views.project_delete, name="delete-project"
),
path("delete-project/<int:project_id>/", 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/<int:project_id>/',views.project_archive,name='project-archive'),
# Task
path('task-view/<int:project_id>/',views.task_view,name='task-view',kwargs={"model":Project}),
path('create-task/<int:stage_id>/',views.create_task,name='create-task'),
path('create-task-in-project/<int:project_id>/',views.create_task_in_project,name='create-task-in-project'),
path('update-task/<int:task_id>/',views.update_task,name='update-task'),
path('delete-task/<int:task_id>/',views.delete_task,name='delete-task'),
path('task-details/<int:task_id>/',views.task_details,name='task-details'),
path('task-filter/<int:project_id>/',views.task_filter,name='task-filter'),
path('task-stage-change',views.task_stage_change,name='task-stage-change'),
path('task-timesheet/<int:task_id>/',views.task_timesheet,name='task-timesheet'),
path("create-timesheet-task/<int:task_id>/",views.create_timesheet_task,name="create-timesheet-task"),
path("update-timesheet-task/<int:timesheet_id>/",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/<int:task_id>/',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/<int:task_id>/',views.task_all_archive,name='task-all-archive'),
# Project stage
path('create-project-stage/<int:project_id>/',views.create_project_stage,name='create-project-stage'),
path('update-project-stage/<int:stage_id>/',views.update_project_stage,name='update-project-stage'),
path('delete-project-stage/<int:stage_id>/',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("project-bulk-export", views.project_bulk_export, name="project-bulk-export"),
path(
"update-time-sheet/<int:time_sheet_id>/",
views.time_sheet_update,
"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/<int:project_id>/",
views.project_archive,
name="project-archive",
),
# Task
path(
"task-view/<int:project_id>/",
views.task_view,
name="task-view",
kwargs={"model": Project},
),
path(
"create-task/<int:project_id>/",
tasks.TaskCreateForm.as_view(),
name="create-task",
),
path(
"quick-create-task/<int:stage_id>/",
views.quick_create_task,
name="quick-create-task",
),
# path("create-task/<int:stage_id>/", views.create_task, name="create-task"),
path(
"create-task-in-project/<int:project_id>/",
views.create_task_in_project,
name="create-task-in-project",
),
path(
"create-stage-task/<int:stage_id>/",
tasks.TaskCreateForm.as_view(),
name="create-stage-task",
),
path("update-task/<int:pk>/", tasks.TaskCreateForm.as_view(), name="update-task"),
# path("update-task/<int:task_id>/", views.update_task, name="update-task"),
path("delete-task/<int:task_id>/", views.delete_task, name="delete-task"),
path("task-details/<int:task_id>/", views.task_details, name="task-details"),
path("task-filter/<int:project_id>/", views.task_filter, name="task-filter"),
path("task-stage-change", views.task_stage_change, name="task-stage-change"),
# path("task-timesheet/<int:task_id>/", views.task_timesheet, name="task-timesheet"),
path(
"task-timesheet/<int:task_id>/",
timesheet.TaskTimeSheet.as_view(),
name="task-timesheet",
),
# path(
# "create-timesheet-task/<int:task_id>/",
# views.create_timesheet_task,
# name="create-timesheet-task",
# ),
path(
"update-timesheet-task/<int:timesheet_id>/",
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/", 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/<int:pk>/",
tasks.TaskCreateForm.as_view(),
name="update-task-all",
),
path(
"task-detail-view/<int:pk>/",
tasks.TaskDetailView.as_view(),
name="task-detail-view",
),
path(
"update-project-task-status/<int:task_id>/",
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/<int:task_id>/", 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/<int:task_id>/",
views.task_all_archive,
name="task-all-archive",
),
# Project stage
path(
"create-project-stage/<int:project_id>/",
project_stage.ProjectStageCreateForm.as_view(),
name="create-project-stage",
),
# path(
# "create-project-stage/<int:project_id>/",
# views.create_project_stage,
# name="create-project-stage",
# ),
path(
"update-project-stage/<int:pk>/",
project_stage.ProjectStageCreateForm.as_view(),
name="update-project-stage",
),
# path(
# "update-project-stage/<int:stage_id>/",
# views.update_project_stage,
# name="update-project-stage",
# ),
path(
"delete-project-stage/<int:stage_id>/",
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(
"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/<int:pk>/",
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/<int:task_id>/",
timesheet.TimeSheetFormView.as_view(),
name="create-timesheet-task",
),
path(
"update-time-sheet/<int:pk>/",
timesheet.TimeSheetFormView.as_view(),
name="update-time-sheet",
),
path(
"delete-time-sheet-ajax/<int:time_sheet_id>/",
views.time_sheet_delete_ajax,
name="delete-time-sheet-ajax",
),
# path(
# "update-time-sheet/<int:time_sheet_id>/",
# 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 @@ urlpatterns = [
views.time_sheet_task_creation,
name="create-task-time-sheet",
),
path(
"update-time-sheet/<int:time_sheet_id>/",
views.time_sheet_update,
name="update-time-sheet",
),
path(
"delete-time-sheet/<int:time_sheet_id>/",
views.time_sheet_delete,
@@ -110,8 +271,14 @@ urlpatterns = [
name="personal-time-sheet-view",
),
path("personal-time-sheet/", views.personal_time_sheet, name="personal-time-sheet"),
path("view-single-time-sheet/<int:time_sheet_id>", 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/<int:time_sheet_id>",
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",
),
]

File diff suppressed because it is too large Load Diff